40 Commits
Author SHA1 Message Date
EnumDev 8cafe93b33 Update README.md 2026-02-15 20:12:48 +02:00
EnumDev adcf06c149 Reduce gif quality for faster playback 2026-02-15 19:55:49 +02:00
EnumDev 5ef39f89cc Update preview gif 2026-02-15 19:52:35 +02:00
EnumDev 738d2c2086 Disale local IP module if no ip is available 2026-02-15 19:42:18 +02:00
EnumDevandGitHub 637bd2c3e3 Merge pull request #1 from EnumeratedDev/experimental
Merge experimental changes
2026-02-15 17:39:04 +00:00
EnumDev 08035010c9 Add used and total GPU vram variables 2025-12-29 19:30:28 +02:00
EnumDev 983b347d88 Add post installation steps to README.md 2025-12-24 17:59:07 +02:00
EnumDev db96cd7e5e Detect GPU VRAM 2025-12-24 17:52:00 +02:00
EnumDev 96a2dee0e1 Update go modules 2025-12-24 17:40:56 +02:00
EnumDev 85d96fceec Improve GPU name fetching 2025-12-24 17:28:42 +02:00
EnumDev d83440491b Merge branch 'master' into experimental 2025-12-23 16:33:58 +02:00
EnumDev 16b27c4002 Update README.md 2025-12-23 16:32:50 +02:00
EnumDev 701585bd5e Remove duplicate whitespaces in motherboard names 2025-12-23 11:42:15 +02:00
EnumDev b60c9df495 Fallback to product name if subsystem can't be detected 2025-12-23 11:27:12 +02:00
EnumDev 428ee7ceee Improve 'cpus' module 2025-12-23 11:21:40 +02:00
EnumDev fbc9977bf4 Improve 'gpus' module 2025-12-23 11:09:07 +02:00
EnumDev 2f71da0673 Rework package counting 2025-12-22 21:18:06 +02:00
EnumDev d4e324d79f Do not attempt to show memory if not found 2025-12-22 20:02:02 +02:00
EnumDev 7f7c7d646d Do not attempt to show init system if not found 2025-12-22 20:01:14 +02:00
EnumDev cecce857e1 Fetch module information outside os.Expand() function 2025-12-22 17:38:35 +02:00
EnumDev fabc1adbf7 Take default color length into account when calculating module text
length
2025-12-22 14:28:51 +02:00
EnumDev 3b7cb5e4e2 Fix wrong module name in default config 2025-12-22 14:14:03 +02:00
EnumDev 1b0ad32b6b Trim last newline character from ascii art 2025-12-22 13:53:14 +02:00
EnumDev b52145614f Insert default color at the start of each module's text 2025-12-22 13:40:17 +02:00
EnumDev 705a40047b Remove 'distro name' flag and config option 2025-12-22 13:12:28 +02:00
EnumDev 341138116b Add default color map 2025-12-22 12:42:36 +02:00
EnumDev 051301aacd Rewrite 'GetDistroAsciiArt' function 2025-12-22 12:26:42 +02:00
EnumDev 3131d94429 Add 'config' flag 2025-12-22 12:07:03 +02:00
EnumDev c7bfaf7a31 Add 'custom' module 2025-12-22 11:54:30 +02:00
EnumDev 9ce8e5ef0b Replace old color code formatting with new one in cachyos ascii art 2025-12-21 22:25:58 +02:00
EnumDev 92ea038c77 Fix color codes not carrying over from last line of ascii art 2025-12-21 22:19:55 +02:00
EnumDev 9680c024b9 Rename config.yaml to config.yml 2025-12-21 18:40:48 +02:00
EnumDev 13cd6f1d45 Improve partitions module 2025-12-21 17:27:39 +02:00
EnumDev e400e338a0 Rename and improve cpus module 2025-12-21 17:16:05 +02:00
EnumDev d1b095ba73 Improve motherboard module 2025-12-21 16:36:24 +02:00
EnumDev 663e69638b Improve DE/WM module 2025-12-21 16:35:55 +02:00
EnumDev 4e296b5104 Update Makefile and add version flag 2025-12-21 16:34:57 +02:00
EnumDev eef69eed9a Remove dependency on bash, switch to new module system and simplify code 2025-12-21 15:42:57 +02:00
EnumDev 489e6e2143 Get BPM package count using directory contents 2025-10-25 15:28:21 +03:00
EnumDev ba43a98926 Add LXQt detection 2025-10-12 14:32:21 +03:00
32 changed files with 1423 additions and 644 deletions
+24 -21
View File
@@ -1,32 +1,35 @@
SHELL := /bin/bash # Installation paths
PREFIX ?= /usr/local
ifeq ($(PREFIX),) BINDIR ?= $(PREFIX)/bin
PREFIX := /usr/local
endif
ifeq ($(BINDIR),)
BINDIR := $(PREFIX)/bin
endif
ifeq ($(SYSCONFDIR),)
SYSCONFDIR := $(PREFIX)/etc SYSCONFDIR := $(PREFIX)/etc
endif
ifeq ($(GO),) # Compilers and tools
GO := $(shell type -a -P go | head -n 1) GO ?= go
endif
# Build-time variables
VERSION ?= $(shell git describe --tags --dirty)
build: build:
mkdir -p build mkdir -p build
cd src; $(GO) build -ldflags "-w -X 'main.systemConfigDir=$(SYSCONFDIR)'" -o ../build/stormfetch stormfetch cd src; $(GO) build -ldflags "-w -X 'main.StormfetchVersion=$(VERSION)' -X 'main.SystemConfigDir=$(SYSCONFDIR)'" -o ../build/stormfetch stormfetch
install: build/stormfetch config/ install: build/stormfetch config/
mkdir -p $(DESTDIR)$(BINDIR) # Create directoriy
mkdir -p $(DESTDIR)$(SYSCONFDIR)/stormfetch/ install -dm755 $(DESTDIR)$(BINDIR)
cp build/stormfetch $(DESTDIR)$(BINDIR)/stormfetch # Install binary
cp -r config/. $(DESTDIR)$(SYSCONFDIR)/stormfetch/ install -m755 build/stormfetch $(DESTDIR)$(BINDIR)/stormfetch
run: build/stormfetch install-config:
build/stormfetch # Create directory
install -dm755 $(DESTDIR)$(SYSCONFDIR)
# Install files
cp -r config $(DESTDIR)$(SYSCONFDIR)/stormfetch/
uninstall:
-rm -f $(DESTDIR)$(BINDIR)/stormfetch
-rm -rf $(DESTDIR)$(SYSCONFDIR)/stormfetch
clean: clean:
rm -r build/ rm -r build/
.PHONY: build .PHONY: build install install-config uninstall clean
+7 -4
View File
@@ -1,10 +1,10 @@
# Stormfetch # Stormfetch
## A simple linux fetch program written in go and bash ## A simple linux fetch program written in go
### Project Information ### Project Information
Stormfetch is a program that can read your system's information and display it in the terminal along with the ASCII art of the Linux distribution you are running. Stormfetch is a program that can read your system's information and display it in the terminal along with the ASCII art of the Linux distribution you are running.
Stormfetch is still in beta, so distro compatibility is limited. If you would like to contribute ASCII art or add other compatibility features feel free to create a pull request or notify me through GitLab Issues. At the moment ascii art for different distributions is limited. If you would like to contribute ascii art feel free to make a pull request.
### How it looks ### How it looks
![Stormfetch gif](media/stormfetch.gif) ![Stormfetch gif](media/stormfetch.gif)
@@ -19,7 +19,10 @@ Stormfetch is still in beta, so distro compatibility is limited. If you would li
``` ```
make SYSCONFDIR=/etc make SYSCONFDIR=/etc
``` ```
- Run the following command to install stormfetch into your system. You may also append a DESTDIR variable at the end of this line if you wish to install in a different location - Run the following command to install stormfetch into your system. You may also append a DESTDIR variable at the end of this line if you wish to install in a different root directory
``` ```
make install PREFIX=/usr SYSCONFDIR=/etc make install
make install-config SYSCONFDIR=/etc
``` ```
### Post installation
- (Optional) Download `curl` from your package manager to fetch the amdgpu.ids database for AMD GPUs. It may be safely uninstalled after running stormfetch once with curl installed
+4 -4
View File
@@ -1,5 +1,5 @@
#/4;27;4;11 #/4;27;4;11
${C1} -` %1 -`
.o+` .o+`
`ooo/ `ooo/
`+oooo: `+oooo:
@@ -8,9 +8,9 @@ ${C1} -`
`/:-:++oooo+: `/:-:++oooo+:
`/++++/+++++++: `/++++/+++++++:
`/++++++++++++++: `/++++++++++++++:
`/+++o${C2}oooooooo${C1}oooo/` `/+++o%2oooooooo%1oooo/`
${C2} ${C1}./${C2}ooosssso++osssssso${C1}+` %2 %1./%2ooosssso++osssssso%1+`
${C2} .oossssso-````/ossssss+` %2 .oossssso-````/ossssss+`
-osssssso. :ssssssso. -osssssso. :ssssssso.
:osssssss/ osssso+++. :osssssss/ osssso+++.
/ossssssss/ +ssssooo/- /ossssssss/ +ssssooo/-
+1 -1
View File
@@ -1,5 +1,5 @@
#/45;27;45;7 #/45;27;45;7
${C1} 'o' %1 'o'
'ooo' 'ooo'
'ooxoo' 'ooxoo'
'ooxxxoo' 'ooxxxoo'
+2 -2
View File
@@ -1,11 +1,11 @@
#/7;8;8;7 #/7;8;8;7
${C1} \\\\\\\\\\\\ %1 \\\\\\\\\\\\
\\\ \\\ \\\ \\\
\\\ \\\ \\\ \\\
\\\ \\\\\\\\\\\\\\\\\ \\\ \\\\\\\\\\\\\\\\\
\\\ \\\ \\\ \\\
\\\ \\\ \\\ \\\
\\\ ${C2}______ \\\ \\\ %2______ \\\
\\\ /// \\\ ///
\\\ /// \\\ ///
\\\ /// \\\ ///
+22 -22
View File
@@ -1,23 +1,23 @@
#/43;45;45;15 #/43;45;45;15
${C3}.${C1}-------------------------: %3.%1-------------------------:
.${C2}+=${C1}========================. .%2+=%1========================.
:${C2}++${C1}===${C2}++===${C1}===============- :${C2}++${C1}- :%2++%1===%2++===%1===============- :%2++%1-
:${C2}*++${C1}====${C2}+++++==${C1}===========- .==: :%2*++%1====%2+++++==%1===========- .==:
-${C2}*+++${C1}=====${C2}+***++=${C1}=========: -%2*+++%1=====%2+***++=%1=========:
=${C2}*++++=${C1}=======------------: =%2*++++=%1=======------------:
=${C2}*+++++=${C1}====- ${C3}...${C1} =%2*+++++=%1====- %3...%1
.${C2}+*+++++${C1}=-===: .${C2}=+++=${C1}: .%2+*+++++%1=-===: .%2=+++=%1:
:${C2}++++${C1}=====-==: -***${C2}**${C1}+ :%2++++%1=====-==: -***%2**%1+
:${C2}++=${C1}=======-=. .=+**+${C3}.${C1} :%2++=%1=======-=. .=+**+%3.%1
.${C2}+${C1}==========-. ${C3}.${C1} .%2+%1==========-. %3.%1
:${C2}+++++++${C1}====- ${C3}.${C1}--==-${C3}.${C1} :%2+++++++%1====- %3.%1--==-%3.%1
:${C2}++${C1}==========. ${C3}:${C2}+++++++${C1}${C3}: :%2++%1==========. %3:%2+++++++%1%3:
${C1}.-===========. =*****+*+ %1.-===========. =*****+*+
${C1}.-===========: .+*****+: %1.-===========: .+*****+:
${C1}-=======${C2}++++${C1}:::::::::::::::::::::::::-: ${C3}.${C1}---: %1-=======%2++++%1:::::::::::::::::::::::::-: %3.%1---:
:======${C2}++++${C1}====${C2}+++******************=. :======%2++++%1====%2+++******************=.
${C1}:=====${C2}+++${C1}==========${C2}++++++++++++++*- %1:=====%2+++%1==========%2++++++++++++++*-
${C1}.====${C2}++${C1}==============${C2}++++++++++*- %1.====%2++%1==============%2++++++++++*-
${C1}.===${C2}+${C1}==================${C2}+++++++: %1.===%2+%1==================%2+++++++:
${C1}.-=======================${C2}+++: %1.-=======================%2+++:
${C3}.......................... %3..........................
+7 -7
View File
@@ -1,16 +1,16 @@
#/1;7;9;15 #/1;7;9;15
${C2} _,met&&&&&gg. %2 _,met&&&&&gg.
,g&&&&&&&&&&&&&&&P. ,g&&&&&&&&&&&&&&&P.
,g&&P" """Y&&.". ,g&&P" """Y&&.".
,&&P' `&&&. ,&&P' `&&&.
',&&P ,ggs. `&&b: ',&&P ,ggs. `&&b:
`d&&' ,&P"' ${C1}.${C2} &&& `d&&' ,&P"' %1.%2 &&&
&&P d&' ${C1},${C2} &&P &&P d&' %1,%2 &&P
&&: &&. ${C1}-${C2} ,d&&' &&: &&. %1-%2 ,d&&'
&&; Y&b._ _,d&P' &&; Y&b._ _,d&P'
Y&&. ${C1}`.${C2}`"Y&&&&P"' Y&&. %1`.%2`"Y&&&&P"'
${C2} `&&b ${C1}"-.__ %2 `&&b %1"-.__
${C2} `Y&& %2 `Y&&
`Y&&. `Y&&.
`&&b. `&&b.
`Y&&b. `Y&&b.
+13 -13
View File
@@ -1,20 +1,20 @@
#/12;7;4;15 #/12;7;4;15
${C1} .',;::::;,'. %1 .',;::::;,'.
.';:cccccccccccc:;,. .';:cccccccccccc:;,.
.;cccccccccccccccccccccc;. .;cccccccccccccccccccccc;.
.:cccccccccccccccccccccccccc:. .:cccccccccccccccccccccccccc:.
.;ccccccccccccc;${C2}.:dddl:.${C1};ccccccc;. .;ccccccccccccc;%2.:dddl:.%1;ccccccc;.
.:ccccccccccccc;${C2}OWMKOOXMWd${C1};ccccccc:. .:ccccccccccccc;%2OWMKOOXMWd%1;ccccccc:.
.:ccccccccccccc;${C2}KMMc${C1};cc;${C2}xMMc${C1};ccccccc:. .:ccccccccccccc;%2KMMc%1;cc;%2xMMc%1;ccccccc:.
,cccccccccccccc;${C2}MMM.${C1};cc;${C2};WW:${C1};cccccccc, ,cccccccccccccc;%2MMM.%1;cc;%2;WW:%1;cccccccc,
:cccccccccccccc;${C2}MMM.${C1};cccccccccccccccc: :cccccccccccccc;%2MMM.%1;cccccccccccccccc:
:ccccccc;${C2}oxOOOo${C1};${C2}MMM0OOk.${C1};cccccccccccc: :ccccccc;%2oxOOOo%1;%2MMM0OOk.%1;cccccccccccc:
cccccc;${C2}0MMKxdd:${C1};${C2}MMMkddc.${C1};cccccccccccc; cccccc;%20MMKxdd:%1;%2MMMkddc.%1;cccccccccccc;
ccccc;${C2}XM0'${C1};cccc;${C2}MMM.${C1};cccccccccccccccc' ccccc;%2XM0'%1;cccc;%2MMM.%1;cccccccccccccccc'
ccccc;${C2}MMo${C1};ccccc;${C2}MMW.${C1};ccccccccccccccc; ccccc;%2MMo%1;ccccc;%2MMW.%1;ccccccccccccccc;
ccccc;${C2}0MNc.${C1}ccc${C2}.xMMd${C1};ccccccccccccccc; ccccc;%20MNc.%1ccc%2.xMMd%1;ccccccccccccccc;
cccccc;${C2}dNMWXXXWM0:${C1};cccccccccccccc:, cccccc;%2dNMWXXXWM0:%1;cccccccccccccc:,
cccccccc;${C2}.:odl:.${C1};cccccccccccccc:,. cccccccc;%2.:odl:.%1;cccccccccccccc:,.
:cccccccccccccccccccccccccccc:'. :cccccccccccccccccccccccccccc:'.
.:cccccccccccccccccccccc:;,.. .:cccccccccccccccccccccc:;,..
'::cccccccccccccc::;,. '::cccccccccccccc::;,.
+17 -17
View File
@@ -1,19 +1,19 @@
#/5;7;13;69 #/5;7;13;69
${C1} -/oyddmdhs+:. %1 -/oyddmdhs+:.
-o${C2}dNMMMMMMMMNNmhy+${C1}-` -o%2dNMMMMMMMMNNmhy+%1-`
-y${C2}NMMMMMMMMMMMNNNmmdhy${C1}+- -y%2NMMMMMMMMMMMNNNmmdhy%1+-
`o${C2}mMMMMMMMMMMMMNmdmmmmddhhy${C1}/` `o%2mMMMMMMMMMMMMNmdmmmmddhhy%1/`
om${C2}MMMMMMMMMMMN${C1}hhyyyo${C2}hmdddhhhd${C1}o` om%2MMMMMMMMMMMN%1hhyyyo%2hmdddhhhd%1o`
.y${C2}dMMMMMMMMMMd${C1}hs++so/s${C2}mdddhhhhdm${C1}+` .y%2dMMMMMMMMMMd%1hs++so/s%2mdddhhhhdm%1+`
oy${C2}hdmNMMMMMMMN${C1}dyooy${C2}dmddddhhhhyhN${C1}d. oy%2hdmNMMMMMMMN%1dyooy%2dmddddhhhhyhN%1d.
:o${C2}yhhdNNMMMMMMMNNNmmdddhhhhhyym${C1}Mh :o%2yhhdNNMMMMMMMNNNmmdddhhhhhyym%1Mh
.:${C2}+sydNMMMMMNNNmmmdddhhhhhhmM${C1}my .:%2+sydNMMMMMNNNmmmdddhhhhhhmM%1my
/m${C2}MMMMMMNNNmmmdddhhhhhmMNh${C1}s: /m%2MMMMMMNNNmmmdddhhhhhmMNh%1s:
`o${C2}NMMMMMMMNNNmmmddddhhdmMNhs${C1}+` `o%2NMMMMMMMNNNmmmddddhhdmMNhs%1+`
`s${C2}NMMMMMMMMNNNmmmdddddmNMmhs${C1}/. `s%2NMMMMMMMMNNNmmmdddddmNMmhs%1/.
/N${C2}MMMMMMMMNNNNmmmdddmNMNdso${C1}:` /N%2MMMMMMMMNNNNmmmdddmNMNdso%1:`
+M${C2}MMMMMMNNNNNmmmmdmNMNdso${C1}/- +M%2MMMMMMNNNNNmmmmdmNMNdso%1/-
yM${C2}MNNNNNNNmmmmmNNMmhs+/${C1}-` yM%2MNNNNNNNmmmmmNNMmhs+/%1-`
/h${C2}MMNNNNNNNNMNdhs++/${C1}-` /h%2MMNNNNNNNNMNdhs++/%1-`
`/${C2}ohdmmddhys+++/:${C1}.` `/%2ohdmmddhys+++/:%1.`
`-//////:--. `-//////:--.
+19 -19
View File
@@ -1,20 +1,20 @@
#/34;15;46;252 #/34;15;46;252
${C2} ...-:::::-... %2 ...-:::::-...
${C2} .-MMMMMMMMMMMMMMM-. %2 .-MMMMMMMMMMMMMMM-.
.-MMMM${C1}`..-:::::::-..`${C2}MMMM-. .-MMMM%1`..-:::::::-..`%2MMMM-.
.:MMMM${C1}.:MMMMMMMMMMMMMMM:.${C2}MMMM:. .:MMMM%1.:MMMMMMMMMMMMMMM:.%2MMMM:.
-MMM${C1}-M---MMMMMMMMMMMMMMMMMMM.${C2}MMM- -MMM%1-M---MMMMMMMMMMMMMMMMMMM.%2MMM-
`:MMM${C1}:MM` :MMMM:....::-...-MMMM:${C2}MMM:` `:MMM%1:MM` :MMMM:....::-...-MMMM:%2MMM:`
:MMM${C1}:MMM` :MM:` `` `` `:MMM:${C2}MMM: :MMM%1:MMM` :MM:` `` `` `:MMM:%2MMM:
.MMM${C1}.MMMM` :MM. -MM. .MM- `MMMM.${C2}MMM. .MMM%1.MMMM` :MM. -MM. .MM- `MMMM.%2MMM.
:MMM${C1}:MMMM` :MM. -MM- .MM: `MMMM-${C2}MMM: :MMM%1:MMMM` :MM. -MM- .MM: `MMMM-%2MMM:
:MMM${C1}:MMMM` :MM. -MM- .MM: `MMMM:${C2}MMM: :MMM%1:MMMM` :MM. -MM- .MM: `MMMM:%2MMM:
:MMM${C1}:MMMM` :MM. -MM- .MM: `MMMM-${C2}MMM: :MMM%1:MMMM` :MM. -MM- .MM: `MMMM-%2MMM:
.MMM${C1}.MMMM` :MM:--:MM:--:MM: `MMMM.${C2}MMM. .MMM%1.MMMM` :MM:--:MM:--:MM: `MMMM.%2MMM.
:MMM${C1}:MMM- `-MMMMMMMMMMMM-` -MMM-${C2}MMM: :MMM%1:MMM- `-MMMMMMMMMMMM-` -MMM-%2MMM:
:MMM${C1}:MMM:` `:MMM:${C2}MMM: :MMM%1:MMM:` `:MMM:%2MMM:
.MMM${C1}.MMMM:--------------:MMMM.${C2}MMM. .MMM%1.MMMM:--------------:MMMM.%2MMM.
'-MMMM${C1}.-MMMMMMMMMMMMMMM-.${C2}MMMM-' '-MMMM%1.-MMMMMMMMMMMMMMM-.%2MMMM-'
'.-MMMM${C1}``--:::::--``${C2}MMMM-.' '.-MMMM%1``--:::::--``%2MMMM-.'
${C2} '-MMMMMMMMMMMMM-' %2 '-MMMMMMMMMMMMM-'
${C2} ``-:::::-`` %2 ``-:::::-``
+12 -12
View File
@@ -1,18 +1,18 @@
#/2;7;10;15 #/2;7;10;15
${C2} .;ldkO0000Okdl;. %2 .;ldkO0000Okdl;.
.;d00xl:^''''''^:ok00d;. .;d00xl:^''''''^:ok00d;.
.d00l' 'o00d. .d00l' 'o00d.
.d0Kd'${C1} Okxol:;,. ${C2}:O0d. .d0Kd'%1 Okxol:;,. %2:O0d.
.OK${C1}KKK0kOKKKKKKKKKKOxo:, ${C2}lKO. .OK%1KKK0kOKKKKKKKKKKOxo:, %2lKO.
,0K${C1}KKKKKKKKKKKKKKK0P^${C2},,,${C1}^dx:${C2} ;00, ,0K%1KKKKKKKKKKKKKKK0P^%2,,,%1^dx:%2 ;00,
.OK${C1}KKKKKKKKKKKKKKKk'${C2}.oOPPb.${C1}'0k.${C2} cKO. .OK%1KKKKKKKKKKKKKKKk'%2.oOPPb.%1'0k.%2 cKO.
:KK${C1}KKKKKKKKKKKKKKK: ${C2}kKx..dd ${C1}lKd${C2} 'OK: :KK%1KKKKKKKKKKKKKKK: %2kKx..dd %1lKd%2 'OK:
dKK${C1}KKKKKKKKKOx0KKKd ${C2}^0KKKO' ${C1}kKKc${C2} dKd dKK%1KKKKKKKKKOx0KKKd %2^0KKKO' %1kKKc%2 dKd
dKK${C1}KKKKKKKKKK;.;oOKx,..${C2}^${C1}..;kKKK0.${C2} dKd dKK%1KKKKKKKKKK;.;oOKx,..%2^%1..;kKKK0.%2 dKd
:KK${C1}KKKKKKKKKK0o;...^cdxxOK0O/^^' ${C2}.0K: :KK%1KKKKKKKKKK0o;...^cdxxOK0O/^^' %2.0K:
kKK${C1}KKKKKKKKKKKKK0x;,,......,;od ${C2}lKk kKK%1KKKKKKKKKKKKK0x;,,......,;od %2lKk
'0K${C1}KKKKKKKKKKKKKKKKKKKK00KKOo^ ${C2}c00' '0K%1KKKKKKKKKKKKKKKKKKKK00KKOo^ %2c00'
'kK${C1}KKOxddxkOO00000Okxoc;'' ${C2}.dKk' 'kK%1KKOxddxkOO00000Okxoc;'' %2.dKk'
l0Ko. .c00l' l0Ko. .c00l'
'l0Kk:. .;xK0l' 'l0Kk:. .;xK0l'
'lkK0xl:;,,,,;:ldO0kl' 'lkK0xl:;,,,,;:ldO0kl'
+1 -1
View File
@@ -1,5 +1,5 @@
#/2;7;10;15 #/2;7;10;15
${C1} ...... %1 ......
.,cdxxxoc,. .:kKMMMNWMMMNk:. .,cdxxxoc,. .:kKMMMNWMMMNk:.
cKMMN0OOOKWMMXo. ; ;0MWk:. .:OMMk. cKMMN0OOOKWMMXo. ; ;0MWk:. .:OMMk.
;WMK;. .lKMMNM, :NMK, .OMW; ;WMK;. .lKMMNM, :NMK, .OMW;
+16 -16
View File
@@ -1,21 +1,21 @@
#/33;7;11;15 #/33;7;11;15
${C1} ///////////// %1 /////////////
///////////////////// /////////////////////
///////${C2}*767${C1}//////////////// ///////%2*767%1////////////////
//////${C2}7676767676*${C1}////////////// //////%27676767676*%1//////////////
/////${C2}76767${C1}//${C2}7676767${C1}////////////// /////%276767%1//%27676767%1//////////////
/////${C2}767676${C1}///${C2}*76767${C1}/////////////// /////%2767676%1///%2*76767%1///////////////
///////${C2}767676${C1}///${C2}76767${C1}.///${C2}7676*${C1}/////// ///////%2767676%1///%276767%1.///%27676*%1///////
/////////${C2}767676${C1}//${C2}76767${C1}///${C2}767676${C1}//////// /////////%2767676%1//%276767%1///%2767676%1////////
//////////${C2}76767676767${C1}////${C2}76767${C1}///////// //////////%276767676767%1////%276767%1/////////
///////////${C2}76767676${C1}//////${C2}7676${C1}////////// ///////////%276767676%1//////%27676%1//////////
////////////,${C2}7676${C1},///////${C2}767${C1}/////////// ////////////,%27676%1,///////%2767%1///////////
/////////////*${C2}7676${C1}///////${C2}76${C1}//////////// /////////////*%27676%1///////%276%1////////////
///////////////${C2}7676${C1}//////////////////// ///////////////%27676%1////////////////////
///////////////${C2}7676${C1}///${C2}767${C1}//////////// ///////////////%27676%1///%2767%1////////////
//////////////////////${C2}'${C1}//////////// //////////////////////%2'%1////////////
//////${C2}.7676767676767676767,${C1}////// //////%2.7676767676767676767,%1//////
/////${C2}767676767676767676767${C1}///// /////%2767676767676767676767%1/////
/////////////////////////// ///////////////////////////
///////////////////// /////////////////////
///////////// /////////////
+14 -14
View File
@@ -1,15 +1,15 @@
#/27;39;5;12 #/27;39;5;12
${C1}@@@@ ${C1}@@@${C2}* %1@@@@ %1@@@%2*
${C1}@@${C2}~~~~${C1}@@ ${C1}@@${C2}~~~ %1@@%2~~~~%1@@ %1@@%2~~~
${C1}@@${C2}~~ ${C2}~~${C1}@ ${C1}@@${C2}~~ %1@@%2~~ %2~~%1@ %1@@%2~~
${C1}@@${C2}~~ ${C2}~${C1}@ ${C1}@@@${C2}~~ %1@@%2~~ %2~%1@ %1@@@%2~~
${C2}*${C1}@@${C2}~~ ${C1}@@@@ ${C2}~${C1}@@@@${C2}~~~ ${C1}@@@${C2}* %2*%1@@%2~~ %1@@@@ %2~%1@@@@%2~~~ %1@@@%2*
${C2}~~ ${C1}@@${C2}~~~~${C1}@@ ${C2}~~~~ ${C1}@@${C2}~~~ %2~~ %1@@%2~~~~%1@@ %2~~~~ %1@@%2~~~
${C1}@@${C2}~~ ${C2}~~${C1}@ ${C1}@@${C2}~~ %1@@%2~~ %2~~%1@ %1@@%2~~
${C1}@@${C2}~~ ${C2}~${C1}@ ${C1}@@@${C2}~~ %1@@%2~~ %2~%1@ %1@@@%2~~
${C2}*${C1}@@${C2}~~ ${C1}@@@@ ${C2}~${C1}@@@@${C2}~~~ ${C1}@@@${C2}* %2*%1@@%2~~ %1@@@@ %2~%1@@@@%2~~~ %1@@@%2*
${C2}~~ ${C1}@@${C2}~~~~${C1}@@ ${C2}~~~~ ${C1}@@${C2}~~~ %2~~ %1@@%2~~~~%1@@ %2~~~~ %1@@%2~~~
${C1}@@${C2}~~ ${C2}~~${C1}@ ${C1}@@${C2}~~ %1@@%2~~ %2~~%1@ %1@@%2~~
${C1}@@${C2}~~ ${C2}~${C1}@ ${C1}@@@${C2}~~ %1@@%2~~ %2~%1@ %1@@@%2~~
${C2}*${C1}@@${C2}~~ ${C2}~${C1}@@@@${C2}~~~ %2*%1@@%2~~ %2~%1@@@@%2~~~
${C2}~~ ${C2}~~~~ %2~~ %2~~~~
+16 -16
View File
@@ -1,21 +1,21 @@
#/1;7;3;15 #/1;7;3;15
${C1} .-/+oossssoo+\-. %1 .-/+oossssoo+\-.
':+ssssssssssssssssss+:' ':+ssssssssssssssssss+:'
-+ssssssssssssssssssyyssss+- -+ssssssssssssssssssyyssss+-
.ossssssssssssssssss${C2}dMMMNy${C1}sssso. .ossssssssssssssssss%2dMMMNy%1sssso.
/sssssssssss${C2}hdmmNNmmyNMMMMh${C1}ssssss\ /sssssssssss%2hdmmNNmmyNMMMMh%1ssssss\
+sssssssss${C2}hm${C1}yd${C2}MMMMMMMNddddy${C1}ssssssss+ +sssssssss%2hm%1yd%2MMMMMMMNddddy%1ssssssss+
/ssssssss${C2}hNMMM${C1}yh${C2}hyyyyhmNMMMNh${C1}ssssssss\ /ssssssss%2hNMMM%1yh%2hyyyyhmNMMMNh%1ssssssss\
.ssssssss${C2}dMMMNh${C1}ssssssssss${C2}hNMMMd${C1}ssssssss. .ssssssss%2dMMMNh%1ssssssssss%2hNMMMd%1ssssssss.
+ssss${C2}hhhyNMMNy${C1}ssssssssssss${C2}yNMMMy${C1}sssssss+ +ssss%2hhhyNMMNy%1ssssssssssss%2yNMMMy%1sssssss+
oss${C2}yNMMMNyMMh${C1}ssssssssssssss${C2}hmmmh${C1}ssssssso oss%2yNMMMNyMMh%1ssssssssssssss%2hmmmh%1ssssssso
oss${C2}yNMMMNyMMh${C1}sssssssssssssshmmmh${C1}ssssssso oss%2yNMMMNyMMh%1sssssssssssssshmmmh%1ssssssso
+ssss${C2}hhhyNMMNy${C1}ssssssssssss${C2}yNMMMy${C1}sssssss+ +ssss%2hhhyNMMNy%1ssssssssssss%2yNMMMy%1sssssss+
.ssssssss${C2}dMMMNh${C1}ssssssssss${C2}hNMMMd${C1}ssssssss. .ssssssss%2dMMMNh%1ssssssssss%2hNMMMd%1ssssssss.
\ssssssss${C2}hNMMM${C1}yh${C2}hyyyyhdNMMMNh${C1}ssssssss/ \ssssssss%2hNMMM%1yh%2hyyyyhdNMMMNh%1ssssssss/
+sssssssss${C2}dm${C1}yd${C2}MMMMMMMMddddy${C1}ssssssss+ +sssssssss%2dm%1yd%2MMMMMMMMddddy%1ssssssss+
\sssssssssss${C2}hdmNNNNmyNMMMMh${C1}ssssss/ \sssssssssss%2hdmNNNNmyNMMMMh%1ssssss/
.ossssssssssssssssss${C2}dMMMNy${C1}sssso. .ossssssssssssssssss%2dMMMNy%1sssso.
-+sssssssssssssssss${C2}yyy${C1}ssss+- -+sssssssssssssssss%2yyy%1ssss+-
':+ssssssssssssssssss+:' ':+ssssssssssssssssss+:'
.-\+oossssoo+/-. .-\+oossssoo+/-.
+18 -18
View File
@@ -1,19 +1,19 @@
#/2;36;72;15 #/2;36;72;15
${C2}:::::----::::: %2:::::----:::::
${C2}::----------------::= %2::----------------::=
${C2}:-------------------:: %2:-------------------::
${C1} : ${C2}:::. ..:-------: %1 : %2:::. ..:-------:
${C1} =*=. ${C2}.:-----: %1 =*=. %2.:-----:
${C1} =****- ${C2}:-----: %1 =****- %2:-----:
${C1}=*****- ${C2}:::::: :-----: %1=*****- %2:::::: :-----:
${C1}=****+ ${C2}:--------: :----: %1=****+ %2:--------: :----:
${C1}+****- ${C2}.----------. :----: %1+****- %2.----------. :----:
${C1}+****- ${C2}.----------. :----- %1+****- %2.----------. :-----
${C1}=****+ ${C2}:--------: :----: %1=****+ %2:--------: :----:
${C1}=*****- ${C2}:::::: :-----: %1=*****- %2:::::: :-----:
${C1} =*****- ${C2}:----: %1 =*****- %2:----:
${C1} =*****+: ${C2}:-: %1 =*****+: %2:-:
${C1} =*******=:: ::=-. ${C2}. %1 =*******=:: ::=-. %2.
${C1} ==*******************- %1 ==*******************-
${C1} ===****************=- %1 ===****************=-
${C1} ===+=****++=== %1 ===+=****++===
-7
View File
@@ -1,7 +0,0 @@
distro_ascii: auto
fetch_script: auto
show_fs_type: true
hidden_partitions: []
# Hiding squashfs prevents snaps from showing up
hidden_filesystems: ["squashfs"]
hidden_gpus: []
+17
View File
@@ -0,0 +1,17 @@
distro_ascii: auto
disable_amdgpu_ids_warning: false
modules:
- name: distribution
- name: hostname
- name: kernel
- name: packages
- name: shell
- name: init_system
- name: motherboard
- name: cpus
- name: gpus
- name: memory
- name: partitions
- name: local_ip
- name: de_wm
- name: monitors
-46
View File
@@ -1,46 +0,0 @@
echo -e "${C3}Distribution: ${C4}${DISTRO_LONG_NAME} ($(uname -m))"
echo -e "${C3}Hostname: ${C4}$(cat /etc/hostname)"
echo -e "${C3}Kernel: ${C4}$(uname -s) $(uname -r)"
echo -e "${C3}Packages: ${C4}${PACKAGES}"
echo -e "${C3}Shell: ${C4}${USER_SHELL}"
echo -e "${C3}Init: ${C4}${INIT_SYSTEM}"
echo -e "${C3}Libc: ${C4}${LIBC}"
[ -n "$MOTHERBOARD" ] && echo -e "${C3}Motherboard: ${C4}${MOTHERBOARD}"
[ -n "$CPU_MODEL" ] && echo -e "${C3}CPU: ${C4}${CPU_MODEL} (${CPU_THREADS} threads)"
for i in $(seq "${CONNECTED_GPUS}"); do
gpu="GPU$i"
echo -e "${C3}GPU: ${C4}${!gpu}"
done
[ -n "$MEM_TOTAL" ] && [ -n "$MEM_USED" ] && echo -e "${C3}Memory: ${C4}${MEM_USED} MiB / ${MEM_TOTAL} MiB"
for i in $(seq "${MOUNTED_PARTITIONS}"); do
mountpoint="PARTITION${i}_MOUNTPOINT"
label="PARTITION${i}_LABEL"
type="PARTITION${i}_TYPE"
total="PARTITION${i}_TOTAL_SIZE"
used="PARTITION${i}_USED_SIZE"
if [ -z "${!type}" ]; then
if [ -z "${!label}" ]; then
echo -e "${C3}Partition ${!mountpoint}: ${C4}${!used}/${!total}"
else
echo -e "${C3}Partition ${!label}: ${C4}${!used}/${!total}"
fi
else
if [ -z "${!label}" ]; then
echo -e "${C3}Partition ${!mountpoint} (${!type}): ${C4}${!used}/${!total}"
else
echo -e "${C3}Partition ${!label} (${!type}): ${C4}${!used}/${!total}"
fi
fi
done
[ -n "$LOCAL_IPV4" ] && echo -e "${C3}Local IPv4 Address: ${C4}${LOCAL_IPV4}"
if [ -n "$DISPLAY_PROTOCOL" ]; then
echo -e "${C3}Display Protocol: ${C4}${DISPLAY_PROTOCOL}"
for i in $(seq "${CONNECTED_MONITORS}"); do
monitor="MONITOR$i"
echo -e "${C3}Screen $i: ${C4}${!monitor}"
done
fi
[ -n "$DE_WM" ] && echo -e "${C3}DE/WM: ${C4}${DE_WM}"
# Exiting with error code 0 in case the condition above returns 1
exit 0
Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 12 MiB

+15 -10
View File
@@ -2,21 +2,26 @@ package main
import ( import (
"fmt" "fmt"
"strconv"
"strings" "strings"
) )
func setupColorMap(asciiArt string) map[string]string { func setupColorMap(asciiArtHeader string) []string {
colorMap := make(map[string]string) colorMap := make([]string, 10)
// Read colors from ascii art // Set default color map values
if strings.HasPrefix(asciiArt, "#/") { for i := range 10 {
firstLine := strings.Split(asciiArt, "\n")[0] colorMap[i] = "\033[0m"
ansiColors := strings.Split(strings.TrimPrefix(firstLine, "#/"), ";")
for i, ansiColor := range ansiColors {
colorMap["C"+strconv.Itoa(i+1)] = fmt.Sprintf("\033[38;5;%sm", ansiColor)
} }
// Return if header is empty
if asciiArtHeader == "" {
return colorMap
}
// Read colors from ascii art header
ansiColors := strings.Split(strings.TrimPrefix(asciiArtHeader, "#/"), ";")
for i := 0; i < 9 && i < len(ansiColors); i++ {
colorMap[i+1] = fmt.Sprintf("\033[38;5;%sm", ansiColors[i])
} }
return colorMap return colorMap
+48
View File
@@ -0,0 +1,48 @@
package main
import (
"log"
"os"
"path"
"gopkg.in/yaml.v3"
)
type StormfetchConfig struct {
Ascii string `yaml:"distro_ascii"`
DisableAmdgpuIdsWarning bool `yaml:"disable_amdgpu_ids_warning"`
Modules []stormfetchModuleConfig `yaml:"modules"`
AnsiiColors []int `yaml:"ansii_colors"`
ForceConfigAnsii bool `yaml:"force_config_ansii"`
}
var config = StormfetchConfig{
Ascii: "auto",
Modules: make([]stormfetchModuleConfig, 0),
}
func readConfig() {
if ConfigPath == "" {
// Get home directory
userConfigDir, _ := os.UserConfigDir()
// Find valid config directory
if _, err := os.Stat(path.Join(userConfigDir, "stormfetch/config.yml")); err == nil {
ConfigPath = path.Join(userConfigDir, "stormfetch/config.yml")
} else if _, err := os.Stat(path.Join(SystemConfigDir, "stormfetch/config.yml")); err == nil {
ConfigPath = path.Join(SystemConfigDir, "stormfetch/config.yml")
} else {
log.Fatalf("Config file not found: %s", err.Error())
}
}
// Parse config
bytes, err := os.ReadFile(ConfigPath)
if err != nil {
log.Fatal(err)
}
err = yaml.Unmarshal(bytes, &config)
if err != nil {
log.Fatal(err)
}
}
+5 -5
View File
@@ -1,19 +1,19 @@
module stormfetch module stormfetch
go 1.22 go 1.24.0
require ( require (
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728
github.com/jackmordaunt/ghw v1.0.4 github.com/jackmordaunt/ghw v1.0.5
github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/go-ps v1.0.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/jackmordaunt/pcidb v1.0.1 // indirect github.com/jackmordaunt/pcidb v1.0.1 // indirect
github.com/jackmordaunt/wmi v1.2.4 // indirect github.com/jackmordaunt/wmi v1.2.4 // indirect
github.com/kr/pretty v0.1.0 // indirect github.com/kr/pretty v0.1.0 // indirect
golang.org/x/sys v0.3.0 // indirect golang.org/x/sys v0.39.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
) )
+9
View File
@@ -1,9 +1,15 @@
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728 h1:RkGhqHxEVAvPM0/R+8g7XRwQnHatO0KAuVcwHo8q9W8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728/go.mod h1:SyRD8YfuKk+ZXlDqYiqe1qMSqjNgtHzBTG810KUagMc=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/jackmordaunt/ghw v1.0.4 h1:as+COFuPuXaNQC3WqzoHS/E2JYWZU7gN8ompNTUxNxs= github.com/jackmordaunt/ghw v1.0.4 h1:as+COFuPuXaNQC3WqzoHS/E2JYWZU7gN8ompNTUxNxs=
github.com/jackmordaunt/ghw v1.0.4/go.mod h1:4dReYvJ36CoAzIxlEx8du25Qi/YqKYvEGE9QJoRXiK8= github.com/jackmordaunt/ghw v1.0.4/go.mod h1:4dReYvJ36CoAzIxlEx8du25Qi/YqKYvEGE9QJoRXiK8=
github.com/jackmordaunt/ghw v1.0.5 h1:3rTXwu0D9RkungV7/WlhC8HVuDlPaq0aKY8u0I51jLY=
github.com/jackmordaunt/ghw v1.0.5/go.mod h1:VpFlLXnJErgoRttR3WOxun4v5EE8/xfB4cK26G5q2U0=
github.com/jackmordaunt/pcidb v1.0.1 h1:uLLZa6kD5P39r2cwMyJJkxmuHfH9Wq19gYQEbYcB0Z4= github.com/jackmordaunt/pcidb v1.0.1 h1:uLLZa6kD5P39r2cwMyJJkxmuHfH9Wq19gYQEbYcB0Z4=
github.com/jackmordaunt/pcidb v1.0.1/go.mod h1:OMmhrZOZVu2hYXhBDZXddypxwKR/dp4DbIgzCkQDxdQ= github.com/jackmordaunt/pcidb v1.0.1/go.mod h1:OMmhrZOZVu2hYXhBDZXddypxwKR/dp4DbIgzCkQDxdQ=
github.com/jackmordaunt/wmi v1.2.4 h1:/XyuMiKby0qXNQp1j0uU0JqTWLM0QGOVok1Hf5Yagtg= github.com/jackmordaunt/wmi v1.2.4 h1:/XyuMiKby0qXNQp1j0uU0JqTWLM0QGOVok1Hf5Yagtg=
@@ -16,8 +22,11 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+158 -33
View File
@@ -2,49 +2,164 @@ package main
import ( import (
"fmt" "fmt"
"os"
"slices"
"strconv"
"strings"
"github.com/go-gl/glfw/v3.3/glfw" "github.com/go-gl/glfw/v3.3/glfw"
"github.com/jackmordaunt/ghw" "github.com/jackmordaunt/ghw"
"os"
"os/exec"
"slices"
"strings"
) )
func GetCPUModel() string { type CPU struct {
cpu, err := ghw.CPU() Model string
if err != nil { Cores int
return "" Threads int
}
if len(cpu.Processors) == 0 {
return ""
}
return cpu.Processors[0].Model
} }
func GetCPUThreads() int { type GPU struct {
cpu, err := ghw.CPU() PCIAddress string
if err != nil { Vendor string
return 0 Name string
} Product string
return int(cpu.TotalThreads) Subsystem string
Driver string
VramTotal string
VramUsed string
} }
func GetGPUModels() (ret []string) { type Monitor struct {
cmd := exec.Command("sh", "-c", "lspci -v -m | grep 'VGA' -A6 | grep '^Device:'") Width int
bytes, err := cmd.Output() Height int
if err != nil { RefreshRate int
return nil
} }
for i, gpu := range strings.Split(string(bytes), "\n") { func GetCPUs(hiddenCPUs []int) []CPU {
if slices.Contains(config.HiddenGPUS, i+1) { ret := make([]CPU, 0)
cpus, err := ghw.CPU()
if err != nil {
return ret
}
for i, cpu := range cpus.Processors {
if slices.Contains(hiddenCPUs, i+1) {
continue continue
} }
if gpu == "" {
// Remove unnecessary information from the CPU model
model := cpu.Model
stringsToRemove := []string{
" CPU", " FPU", " APU", " Processor",
" Dual-Core", " Quad-Core", " Six-Core", " Eight-Core", " Ten-Core",
" 2-Core", " 4-Core", " 6-Core", " 8-Core", " 10-Core", " 12-Core", " 14-Core", " 16-Core",
}
for _, str := range stringsToRemove {
model = strings.ReplaceAll(model, str, "")
}
model = strings.Split(model, "w/ Radeon ")[0]
model = strings.Split(model, "with Radeon ")[0]
model = strings.Split(model, "@")[0]
model = strings.TrimSpace(model)
ret = append(ret, CPU{
Model: model,
Cores: int(cpu.NumCores),
Threads: int(cpu.NumThreads),
})
}
return ret
}
func GetGPUModels(hiddenGPUs []int) []GPU {
ret := make([]GPU, 0)
// Set stderr to nil to avoid warnings
stderr := os.Stderr
os.Stderr = nil
gpus, err := ghw.GPU()
if err != nil {
return ret
}
// Restore stderr
os.Stderr = stderr
for i, gpu := range gpus.GraphicsCards {
if slices.Contains(hiddenGPUs, i+1) {
continue continue
} }
gpu = strings.TrimPrefix(strings.TrimSpace(gpu), "Device:\t")
ret = append(ret, gpu) // Set alternative names for vendors
var vendor string
switch gpu.DeviceInfo.Vendor.ID {
case "1002":
vendor = "AMD"
case "10de":
vendor = "Nvidia"
case "8086":
vendor = "Intel"
default:
vendor = gpu.DeviceInfo.Vendor.Name
}
// Set GPU name
name := ""
// Use GPU name from amdgpu.ids database
if vendor == "AMD" {
fetchedName, err := fetchAmdGpuName(gpu.DeviceInfo.Product.ID, gpu.DeviceInfo.Revision)
if err == nil && !config.DisableAmdgpuIdsWarning {
name = fetchedName
} else {
fmt.Println("Warning: could not fetch GPU name from amdgpu.ids database! Error: " + err.Error())
fmt.Println(" You can disable this warning in the configuration file")
}
}
if name == "" {
if gpu.DeviceInfo.Subsystem.Name == "" || gpu.DeviceInfo.Subsystem.Name == "unknown" {
// Set GPU name to product name
name = gpu.DeviceInfo.Product.Name
} else {
// Set GPU name to subsystem name
name = gpu.DeviceInfo.Subsystem.Name
}
// Use GPU name in brackets
leftBracket := strings.IndexByte(name, '[')
rightBracket := strings.IndexByte(name, ']')
if leftBracket != -1 && rightBracket != -1 {
name = name[leftBracket+1 : rightBracket]
}
}
// Get VRAM
vramTotal := "Unknown"
bytes, err := os.ReadFile("/sys/class/drm/card" + strconv.Itoa(gpu.Index) + "/device/mem_info_vram_total")
if err == nil {
vramUint, _ := strconv.ParseUint(strings.TrimSpace(string(bytes)), 10, 64)
vramTotal = FormatBytes(vramUint)
}
vramUsed := "Unknown"
bytes, err = os.ReadFile("/sys/class/drm/card" + strconv.Itoa(gpu.Index) + "/device/mem_info_vram_used")
if err == nil {
vramUint, _ := strconv.ParseUint(strings.TrimSpace(string(bytes)), 10, 64)
vramUsed = FormatBytes(vramUint)
}
ret = append(ret, GPU{
PCIAddress: gpu.Address,
Vendor: vendor,
Name: name,
Product: gpu.DeviceInfo.Product.Name,
Subsystem: gpu.DeviceInfo.Subsystem.Name,
Driver: gpu.DeviceInfo.Driver,
VramTotal: vramTotal,
VramUsed: vramUsed,
})
} }
return ret return ret
@@ -55,19 +170,29 @@ func GetMotherboardModel() string {
if err != nil { if err != nil {
return "" return ""
} }
return strings.TrimSpace(string(bytes))
// Remove duplicate whitespaces
ret := strings.Join(strings.Fields(string(bytes)), " ")
return ret
} }
func GetMonitorResolution() []string { func GetMonitors() []Monitor {
var monitors []string var monitors []Monitor
if GetDisplayProtocol() != "" { if GetDisplayProtocol() != "" {
err := glfw.Init() err := glfw.Init()
if err != nil { if err != nil {
panic(err) panic(err)
} }
for _, monitor := range glfw.GetMonitors() { for _, monitor := range glfw.GetMonitors() {
mode := monitor.GetVideoMode() mode := monitor.GetVideoMode()
monitors = append(monitors, fmt.Sprintf("%dx%d %dHz", mode.Width, mode.Height, mode.RefreshRate))
monitors = append(monitors, Monitor{
Width: mode.Width,
Height: mode.Height,
RefreshRate: mode.RefreshRate,
})
} }
defer glfw.Terminate() defer glfw.Terminate()
} }
+120 -221
View File
@@ -3,258 +3,157 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"log"
"os"
"os/exec"
"path"
"regexp"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"unicode"
"gopkg.in/yaml.v3"
) )
var systemConfigDir = "/etc/" // Build-time variables
var StormfetchVersion = "dev"
var SystemConfigDir = "/etc/"
var configPath = "" // Flag variables
var fetchScriptPath = "" var ShowVersion = false
var ConfigPath = ""
var TimeTaken = false var Ascii = ""
var ShowModuleTimeTaken = false
var config = StormfetchConfig{
Ascii: "auto",
FetchScript: "auto",
ShowFSType: false,
HiddenPartitions: make([]string, 0),
HiddenGPUS: make([]int, 0),
}
type StormfetchConfig struct {
Ascii string `yaml:"distro_ascii"`
DistroName string `yaml:"distro_name"`
FetchScript string `yaml:"fetch_script"`
AnsiiColors []int `yaml:"ansii_colors"`
ForceConfigAnsii bool `yaml:"force_config_ansii"`
ShowFSType bool `yaml:"show_fs_type"`
HiddenPartitions []string `yaml:"hidden_partitions"`
HiddenFilesystems []string `yaml:"hidden_filesystems"`
HiddenGPUS []int `yaml:"hidden_gpus"`
}
func main() { func main() {
parseFlags()
readConfig() readConfig()
readFlags() initializeModuleMap()
runStormfetch() run()
} }
func readConfig() { func parseFlags() {
// Get home directory flag.BoolVar(&ShowVersion, "version", false, "Show Stormfetch version")
userConfigDir, _ := os.UserConfigDir() flag.StringVar(&ConfigPath, "config", "", "Use the specified config file")
// Find valid config directory flag.BoolVar(&ShowModuleTimeTaken, "time-taken", false, "Show time taken to execute each module")
if _, err := os.Stat(path.Join(userConfigDir, "stormfetch/config.yaml")); err == nil { flag.StringVar(&Ascii, "ascii", "", "Set distro ascii")
configPath = path.Join(userConfigDir, "stormfetch/config.yaml")
} else if _, err := os.Stat(path.Join(systemConfigDir, "stormfetch/config.yaml")); err == nil {
configPath = path.Join(systemConfigDir, "stormfetch/config.yaml")
} else {
log.Fatalf("Config file not found: %s", err.Error())
}
// Parse config
bytes, err := os.ReadFile(configPath)
if err != nil {
log.Fatal(err)
}
err = yaml.Unmarshal(bytes, &config)
if err != nil {
log.Fatal(err)
}
if config.FetchScript == "" {
log.Fatalf("Fetch script path is empty")
} else if config.FetchScript != "auto" {
stat, err := os.Stat(config.FetchScript)
if err != nil {
log.Fatalf("Fetch script file not found: %s", err.Error())
} else if stat.IsDir() {
log.Fatalf("Fetch script path points to a directory")
}
}
if _, err := os.Stat(path.Join(userConfigDir, "stormfetch/fetch_script.sh")); err == nil {
fetchScriptPath = path.Join(userConfigDir, "stormfetch/fetch_script.sh")
} else if _, err := os.Stat(path.Join(systemConfigDir, "stormfetch/fetch_script.sh")); err == nil {
fetchScriptPath = path.Join(systemConfigDir, "stormfetch/fetch_script.sh")
} else {
log.Fatalf("Fetch script file not found: %s", err.Error())
}
}
func readFlags() {
flag.StringVar(&config.Ascii, "ascii", config.Ascii, "Set distro ascii")
flag.StringVar(&config.DistroName, "distro-name", config.DistroName, "Set distro name")
flag.BoolVar(&TimeTaken, "time-taken", false, "Show time taken for fetched information")
flag.Parse() flag.Parse()
} }
func SetupFetchEnv(showTimeTaken bool) []string { func run() {
var env = make(map[string]string) if ShowVersion {
setVariable := func(key string, setter func() string) { fmt.Printf("Stormfetch version %s\n", StormfetchVersion)
start := time.Now().UnixMilli() return
env[key] = setter()
end := time.Now().UnixMilli()
if showTimeTaken {
fmt.Printf("Setting '%s' took %d milliseconds\n", key, end-start)
}
}
setVariable("PACKAGES", func() string { return GetInstalledPackages() })
setVariable("DISTRO_LONG_NAME", func() string { return GetDistroInfo().LongName })
setVariable("DISTRO_SHORT_NAME", func() string { return GetDistroInfo().ShortName })
setVariable("CPU_MODEL", func() string { return GetCPUModel() })
setVariable("MOTHERBOARD", func() string { return GetMotherboardModel() })
setVariable("CPU_THREADS", func() string { return strconv.Itoa(GetCPUThreads()) })
start := time.Now().UnixMilli()
memory := GetMemoryInfo()
end := time.Now().UnixMilli()
if showTimeTaken {
fmt.Printf("Setting '%s' took %d milliseconds\n", "MEM_*", end-start)
}
if memory != nil {
env["MEM_TOTAL"] = strconv.Itoa(memory.MemTotal)
env["MEM_USED"] = strconv.Itoa(memory.MemTotal - memory.MemAvailable)
env["MEM_FREE"] = strconv.Itoa(memory.MemAvailable)
}
start = time.Now().UnixMilli()
partitions := GetMountedPartitions(config.HiddenPartitions, config.HiddenFilesystems)
end = time.Now().UnixMilli()
if showTimeTaken {
fmt.Printf("Setting '%s' took %d milliseconds\n", "PARTITION_*", end-start)
}
if len(partitions) != 0 {
env["MOUNTED_PARTITIONS"] = strconv.Itoa(len(partitions))
for i, part := range partitions {
env["PARTITION"+strconv.Itoa(i+1)+"_DEVICE"] = part.Device
env["PARTITION"+strconv.Itoa(i+1)+"_MOUNTPOINT"] = part.MountPoint
if part.Label != "" {
env["PARTITION"+strconv.Itoa(i+1)+"_LABEL"] = part.Label
}
if part.FileystemType != "" && config.ShowFSType {
env["PARTITION"+strconv.Itoa(i+1)+"_TYPE"] = part.FileystemType
}
env["PARTITION"+strconv.Itoa(i+1)+"_TOTAL_SIZE"] = FormatBytes(part.TotalSize)
env["PARTITION"+strconv.Itoa(i+1)+"_USED_SIZE"] = FormatBytes(part.UsedSize)
env["PARTITION"+strconv.Itoa(i+1)+"_FREE_SIZE"] = FormatBytes(part.FreeSize)
}
}
setVariable("DE_WM", func() string { return GetDEWM() })
setVariable("USER_SHELL", func() string { return GetShell() })
setVariable("DISPLAY_PROTOCOL", func() string { return GetDisplayProtocol() })
setVariable("LIBC", func() string { return GetLibc() })
setVariable("INIT_SYSTEM", func() string { return GetInitSystem() })
setVariable("LOCAL_IPV4", func() string { return GetLocalIP() })
start = time.Now().UnixMilli()
monitors := GetMonitorResolution()
end = time.Now().UnixMilli()
if showTimeTaken {
fmt.Printf("Setting '%s' took %d milliseconds\n", "MONITOR_*", end-start)
}
if len(monitors) != 0 {
env["CONNECTED_MONITORS"] = strconv.Itoa(len(monitors))
for i, monitor := range monitors {
env["MONITOR"+strconv.Itoa(i+1)] = monitor
}
}
start = time.Now().UnixMilli()
gpus := GetGPUModels()
end = time.Now().UnixMilli()
if showTimeTaken {
fmt.Printf("Setting '%s' took %d milliseconds\n", "GPU_*", end-start)
}
if len(gpus) != 0 {
env["CONNECTED_GPUS"] = strconv.Itoa(len(gpus))
for i, gpu := range gpus {
if gpu == "" {
continue
}
env["GPU"+strconv.Itoa(i+1)] = gpu
}
} }
var ret = make([]string, len(env)) // Fetch ascii art and remove header
i := 0
for key, value := range env {
ret[i] = fmt.Sprintf("%s=%s", key, value)
i++
}
return ret
}
func runStormfetch() {
// Fetch ascii art
asciiArt := GetDistroAsciiArt() asciiArt := GetDistroAsciiArt()
asciiArtHeader := ""
// Setup color map if strings.HasPrefix(asciiArt, "#/") {
colorMap := setupColorMap(asciiArt) asciiArtHeader = strings.SplitN(asciiArt, "\n", 2)[0]
if len(colorMap) > 0 {
asciiArt = os.Expand(asciiArt, func(s string) string {
return colorMap[s]
})
asciiArt = strings.SplitN(asciiArt, "\n", 2)[1] asciiArt = strings.SplitN(asciiArt, "\n", 2)[1]
} }
asciiArtNoColor := asciiArt
asciiSplit := strings.Split(asciiArt, "\n") // Rewrite last color code to the start of the next line
asciiNoColor := StripAnsii(asciiArt) lastColor := "0"
for i := 1; i < len(asciiArt); i++ {
rune := rune(asciiArt[i])
// Execute fetch script if unicode.IsDigit(rune) && asciiArt[i-1] == '%' {
cmd := exec.Command("/bin/bash", fetchScriptPath) lastColor = string(rune)
cmd.Dir = path.Dir(fetchScriptPath) }
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, SetupFetchEnv(TimeTaken)...) if rune == '\n' {
cmd.Env = append(cmd.Env, "C0=\033[0m") asciiArt = asciiArt[:i+1] + "%" + lastColor + asciiArt[i+1:]
}
}
// Setup color map and replace colors in ascii art
colorMap := setupColorMap(asciiArtHeader)
for key, value := range colorMap { for key, value := range colorMap {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, value)) asciiArt = strings.ReplaceAll(asciiArt, "%"+strconv.Itoa(key), value)
asciiArtNoColor = strings.ReplaceAll(asciiArtNoColor, "%"+strconv.Itoa(key), "")
} }
out, err := cmd.Output()
if err != nil { // Execute modules in order
log.Fatalf("Error: Could not run fetch script: %s", err) modulesText := make([]string, 0)
for _, moduleConfig := range config.Modules {
module, ok := Modules[moduleConfig.Name]
if !ok {
continue
} }
// Print Distro Information
// Set module config options
if moduleConfig.Format != "" {
module.Format = moduleConfig.Format
}
if moduleConfig.Data != nil {
module.Data = moduleConfig.Data
}
// Execute module
start := time.Now().UnixMilli()
text := module.Execute(module)
end := time.Now().UnixMilli()
// Insert default color at the start of the module's text
text = colorMap[0] + text
// Show time taken
if ShowModuleTimeTaken {
fmt.Printf("Module '%s' took %d milliseconds\n", module.Name, end-start)
}
// Replace colors in returned string
textNoColor := text
for key, value := range colorMap {
text = strings.ReplaceAll(text, "%"+strconv.Itoa(key), value)
textNoColor = strings.ReplaceAll(textNoColor, "%"+strconv.Itoa(key), value)
}
// Continue if text length is 0
if len(textNoColor)-len(colorMap[0]) == 0 {
continue
}
// Add text to slice
for _, line := range strings.Split(strings.TrimSpace(text), "\n") {
modulesText = append(modulesText, line)
}
}
// Get longest line in ascii art
maxWidth := 0 maxWidth := 0
for _, line := range strings.Split(asciiNoColor, "\n") { for _, line := range strings.Split(asciiArtNoColor, "\n") {
if len(line) > maxWidth { if len(line) > maxWidth {
maxWidth = len(line) maxWidth = len(line)
} }
} }
final := ""
y := len(asciiSplit) // Split ascii art into each line
if len(asciiSplit) < len(strings.Split(string(out), "\n")) { asciiArtSplit := strings.Split(asciiArt, "\n")
y = len(strings.Split(string(out), "\n")) asciiArtNoColorSplit := strings.Split(asciiArtNoColor, "\n")
// Get amount of lines to print
lineCount := max(len(asciiArtSplit), len(modulesText))
// Combine ascii art and module text
final := strings.Builder{}
for i := range lineCount {
// Write ascii art
currentLineLength := 0
if i < len(asciiArtSplit) {
final.WriteString(asciiArtSplit[i])
currentLineLength += len(asciiArtNoColorSplit[i])
} }
for lineIndex := 0; lineIndex < y; lineIndex++ {
line := "" // Write blank space between ascii art and module text
for i := 0; i < maxWidth+5; i++ { for i := currentLineLength; i < maxWidth+3; i++ {
line = line + " " final.WriteString(" ")
} }
lastAsciiColor := ""
if lineIndex < len(asciiSplit) { // Write module text
line = asciiSplit[lineIndex] if i < len(modulesText) {
lineVisibleLength := len(strings.Split(asciiNoColor, "\n")[lineIndex]) final.WriteString(modulesText[i])
if lineIndex != 0 {
r := regexp.MustCompile("\033[38;5;[0-9]+m")
matches := r.FindAllString(asciiSplit[lineIndex-1], -1)
if len(matches) != 0 {
lastAsciiColor = r.FindAllString(asciiSplit[lineIndex-1], -1)[len(matches)-1]
} }
final.WriteString("\n")
} }
for i := lineVisibleLength; i < maxWidth+5; i++ {
line = line + " " fmt.Println(strings.TrimRight(final.String(), "\n") + "\033[0m")
}
asciiSplit[lineIndex] = lastAsciiColor + line
}
str := string(out)
if lineIndex < len(strings.Split(str, "\n")) {
line = line + colorMap["C0"] + strings.Split(str, "\n")[lineIndex]
}
final += lastAsciiColor + line + "\n"
}
final = strings.TrimRight(final, "\n\t ")
fmt.Println(final + "\033[0m")
} }
+469
View File
@@ -0,0 +1,469 @@
package main
import (
"os"
"reflect"
"strconv"
"strings"
)
// Used to declare modules in config files
type stormfetchModuleConfig struct {
Name string `yaml:"name"`
Format string `yaml:"format"`
Data map[string]any `yaml:"data"`
}
type StormfetchModule struct {
Execute func(StormfetchModule) string
stormfetchModuleConfig
}
var Modules map[string]StormfetchModule = make(map[string]StormfetchModule)
func (sm StormfetchModule) GetData(key string, defaultValue any) (any, bool) {
if sm.Data == nil {
return defaultValue, false
}
data, ok := sm.Data[key]
if !ok {
return defaultValue, false
}
if reflect.ValueOf(data).Kind() != reflect.ValueOf(defaultValue).Kind() {
return defaultValue, false
}
return data, true
}
func RegisterModule(module StormfetchModule) bool {
if _, ok := Modules[module.Name]; ok {
return false
}
Modules[module.Name] = module
return true
}
func initializeModuleMap() {
// Distribution Module
distributionModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "distribution", Format: "%3Distribution: %4$DISTRO_SHORT ($ARCH)"}, Execute: func(sm StormfetchModule) string {
distroInfo := GetDistroInfo()
return os.Expand(sm.Format, func(s string) string {
switch s {
case "DISTRO_ID":
return distroInfo.ID
case "DISTRO_SHORT":
return distroInfo.ShortName
case "DISTRO_LONG":
return distroInfo.LongName
case "ARCH":
return GetArch()
default:
return ""
}
})
}}
RegisterModule(distributionModule)
// Hostname module
hostnameModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "hostname", Format: "%3Hostname: %4$HOSTNAME"}, Execute: func(sm StormfetchModule) string {
hostname, _ := os.Hostname()
return os.Expand(sm.Format, func(s string) string {
switch s {
case "HOSTNAME":
return hostname
default:
return ""
}
})
}}
RegisterModule(hostnameModule)
// Kernel module
kernelModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "kernel", Format: "%3Kernel: %4$KERNEL_NAME $KERNEL_RELEASE"}, Execute: func(sm StormfetchModule) string {
kernelName, kernelRelease := GetKernel()
return os.Expand(sm.Format, func(s string) string {
switch s {
case "KERNEL_NAME":
return kernelName
case "KERNEL_RELEASE":
return kernelRelease
default:
return ""
}
})
}}
RegisterModule(kernelModule)
// Packages module
packagesModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "packages", Format: "%3Packages: %4$PACKAGES"}, Execute: func(sm StormfetchModule) string {
packages := GetInstalledPackages()
return os.Expand(sm.Format, func(s string) string {
switch s {
case "PACKAGES":
return packages
default:
return ""
}
})
}}
RegisterModule(packagesModule)
// Shell module
shellModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "shell", Format: "%3Shell: %4$SHELL"}, Execute: func(sm StormfetchModule) string {
shell := GetShell()
return os.Expand(sm.Format, func(s string) string {
switch s {
case "SHELL":
return shell
default:
return ""
}
})
}}
RegisterModule(shellModule)
// Init system module
initSystemModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "init_system", Format: "%3Init: %4$INIT"}, Execute: func(sm StormfetchModule) string {
initSystem := GetInitSystem()
if initSystem == "" {
return ""
}
return os.Expand(sm.Format, func(s string) string {
switch s {
case "INIT":
return initSystem
default:
return ""
}
})
}}
RegisterModule(initSystemModule)
// Libc module
libcModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "libc", Format: "%3Libc: %4$LIBC"}, Execute: func(sm StormfetchModule) string {
libc := GetLibc()
return os.Expand(sm.Format, func(s string) string {
switch s {
case "LIBC":
return libc
default:
return ""
}
})
}}
RegisterModule(libcModule)
// Motherboard module
MotherboardModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "motherboard", Format: "%3Motherboard: %4$MOTHERBOARD"}, Execute: func(sm StormfetchModule) string {
motherboard := GetMotherboardModel()
// Return empty string if can't detect motherboard model
if motherboard == "" {
return ""
}
return os.Expand(sm.Format, func(s string) string {
switch s {
case "MOTHERBOARD":
return motherboard
default:
return ""
}
})
}}
RegisterModule(MotherboardModule)
// Motherboard module
cpusModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "cpus", Format: "%3CPU: %4$CPU_MODEL ($CPU_THREADS threads)"}, Execute: func(sm StormfetchModule) string {
hiddenCPUsInterface, _ := sm.GetData("hidden_cpus", make([]any, 0))
// Convert interface slices to string slices
hiddenCPUs := make([]int, 0)
for _, value := range hiddenCPUsInterface.([]any) {
hiddenCPUs = append(hiddenCPUs, value.(int))
}
builder := strings.Builder{}
cpus := GetCPUs(hiddenCPUs)
for i, cpu := range cpus {
expanded := os.Expand(sm.Format, func(s string) string {
switch s {
case "CPU_NUM":
return strconv.Itoa(i + 1)
case "CPU_MODEL":
return cpu.Model
case "CPU_CORES":
return strconv.Itoa(cpu.Cores)
case "CPU_THREADS":
return strconv.Itoa(cpu.Threads)
default:
return ""
}
})
builder.WriteString(expanded + "\n")
}
return builder.String()
}}
RegisterModule(cpusModule)
// GPUs module
gpusModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "gpus", Format: "%3GPU: %4$GPU_VENDOR $GPU_NAME"}, Execute: func(sm StormfetchModule) string {
hiddenGPUsInterface, _ := sm.GetData("hidden_gpus", make([]any, 0))
// Convert interface slices to string slices
hiddenGPUs := make([]int, 0)
for _, value := range hiddenGPUsInterface.([]any) {
hiddenGPUs = append(hiddenGPUs, value.(int))
}
builder := strings.Builder{}
gpus := GetGPUModels(hiddenGPUs)
for i, gpu := range gpus {
expanded := os.Expand(sm.Format, func(s string) string {
switch s {
case "GPU_NUM":
return strconv.Itoa(i + 1)
case "GPU_VENDOR":
return gpu.Vendor
case "GPU_NAME":
return gpu.Name
case "GPU_PRODUCT":
return gpu.Product
case "GPU_SUBSYSTEM":
return gpu.Subsystem
case "GPU_DRIVER":
return gpu.Driver
case "GPU_VRAM_TOTAL":
return gpu.VramTotal
case "GPU_VRAM_USED":
return gpu.VramUsed
default:
return ""
}
})
builder.WriteString(expanded + "\n")
}
return builder.String()
}}
RegisterModule(gpusModule)
// Memory module
memoryModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "memory", Format: "%3Memory: %4$MEM_USED MiB / $MEM_TOTAL MiB"}, Execute: func(sm StormfetchModule) string {
memoryInfo := GetMemoryInfo()
if memoryInfo == nil {
return ""
}
return os.Expand(sm.Format, func(s string) string {
switch s {
case "MEM_TOTAL":
return strconv.Itoa(memoryInfo.MemTotal)
case "MEM_AVAILABLE":
return strconv.Itoa(memoryInfo.MemAvailable)
case "MEM_FREE":
return strconv.Itoa(memoryInfo.MemFree)
case "MEM_USED":
return strconv.Itoa(memoryInfo.MemTotal - memoryInfo.MemAvailable)
default:
return ""
}
})
}}
RegisterModule(memoryModule)
// Partitions module
partitionsModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "partitions", Format: "%3Partition ${PART_AUTONAME} (${PART_FS}): %4${PART_USED} / ${PART_TOTAL}"}, Execute: func(sm StormfetchModule) string {
hiddenPartitionsInterface, _ := sm.GetData("hidden_partitions", make([]any, 0))
hiddenFilesystemsInterface, _ := sm.GetData("hidden_filesystems", make([]any, 0))
alternativeNamesInterface, _ := sm.GetData("alternative_names", make(map[string]any, 0))
// Convert interface slices to string slices
hiddenPartitions := make([]string, 0)
for _, value := range hiddenPartitionsInterface.([]any) {
hiddenPartitions = append(hiddenPartitions, value.(string))
}
hiddenFilesystems := make([]string, 0)
for _, value := range hiddenFilesystemsInterface.([]any) {
hiddenFilesystems = append(hiddenFilesystems, value.(string))
}
// Convert interface map to map[string]string
alternativeNames := make(map[string]string)
for key, value := range alternativeNamesInterface.(map[string]any) {
alternativeNames[key] = value.(string)
}
builder := strings.Builder{}
partitions := GetMountedPartitions(hiddenPartitions, hiddenFilesystems)
for i, partition := range partitions {
partitionAutoname := ""
if altName, ok := alternativeNames[partition.Device]; ok {
partitionAutoname = altName
} else if partition.Label != "" {
partitionAutoname = partition.Label
} else {
partitionAutoname = partition.MountPoint
}
expanded := os.Expand(sm.Format, func(s string) string {
switch s {
case "PART_NUM":
return strconv.Itoa(i + 1)
case "PART_FS":
return partition.FileystemType
case "PART_DEVICE":
return partition.Device
case "PART_AUTONAME":
return partitionAutoname
case "PART_LABEL":
return partition.Label
case "PART_MOUNTPOINT":
return partition.MountPoint
case "PART_FREE":
return FormatBytes(partition.FreeSize)
case "PART_USED":
return FormatBytes(partition.UsedSize)
case "PART_TOTAL":
return FormatBytes(partition.TotalSize)
default:
return ""
}
})
builder.WriteString(expanded + "\n")
}
return builder.String()
}}
RegisterModule(partitionsModule)
// Local IP module
localIpModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "local_ip", Format: "%3Local IP: %4$LOCAL_IP"}, Execute: func(sm StormfetchModule) string {
localIP := GetLocalIP()
// Return empty string if local IP is unavailable
if localIP == "Unknown" {
return ""
}
return os.Expand(sm.Format, func(s string) string {
switch s {
case "LOCAL_IP":
return localIP
default:
return ""
}
})
}}
RegisterModule(localIpModule)
// DEWM module
dewmModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "de_wm", Format: "%3${DEWM_TYPE}: %4${DEWM_NAME} ${DEWM_VERSION} ($DISPLAY_PROTOCOL)"}, Execute: func(sm StormfetchModule) string {
// Return empty string if currently in TTY
if os.Getenv("XDG_SESSION_TYPE") == "" || os.Getenv("XDG_SESSION_TYPE") == "tty" {
return ""
}
dewm := GetDEWM()
displayProtocol := GetDisplayProtocol()
// Return empty string if can't detect DE/WM
if dewm.Name == "Unknown" {
return ""
}
return os.Expand(sm.Format, func(s string) string {
switch s {
case "DEWM_NAME":
return dewm.Name
case "DEWM_TYPE":
return dewm.Type
case "DEWM_VERSION":
return dewm.Version
case "DISPLAY_PROTOCOL":
return displayProtocol
default:
return ""
}
})
}}
RegisterModule(dewmModule)
// Monitors module
monitorsModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "monitors", Format: "%3Monitor: %4${MONITOR_WIDTH}x${MONITOR_HEIGHT} ${MONITOR_REFRESH_RATE}Hz"}, Execute: func(sm StormfetchModule) string {
builder := strings.Builder{}
monitors := GetMonitors()
for i, monitor := range monitors {
expanded := os.Expand(sm.Format, func(s string) string {
switch s {
case "MONITOR_NUM":
return strconv.Itoa(i + 1)
case "MONITOR_WIDTH":
return strconv.Itoa(monitor.Width)
case "MONITOR_HEIGHT":
return strconv.Itoa(monitor.Height)
case "MONITOR_REFRESH_RATE":
return strconv.Itoa(monitor.RefreshRate)
default:
return ""
}
})
builder.WriteString(expanded + "\n")
}
return builder.String()
}}
RegisterModule(monitorsModule)
// Custom module
customModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "custom"}, Execute: func(sm StormfetchModule) string {
shell, _ := sm.GetData("shell", "/bin/sh")
commandList, _ := sm.GetData("commands", make([]any, 0))
// Exeucte all commands
commandOutput := make(map[int]string)
for i, value := range commandList.([]any) {
command, ok := value.(string)
if !ok {
continue
}
commandOutput[i+1] = runCommand(command, shell.(string))
}
return os.Expand(sm.Format, func(s string) string {
if len(s) <= 4 || !strings.HasPrefix(s, "CMD_") {
return ""
}
commandIndexStr := strings.Split(s, "CMD_")[1]
commandIndex, err := strconv.Atoi(commandIndexStr)
if err != nil {
return ""
}
return commandOutput[commandIndex]
})
}}
RegisterModule(customModule)
}
+13 -8
View File
@@ -72,14 +72,19 @@ func GetMountedPartitions(hiddenPartitions, hiddenFilesystems []string) []partit
continue continue
} }
device, err := filepath.EvalSymlinks(fields[0])
if err != nil {
device = fields[0]
}
p := partition{ p := partition{
fields[0], Device: device,
fields[1], MountPoint: fields[1],
"", Label: "",
fields[2], FileystemType: fields[2],
0, TotalSize: 0,
0, UsedSize: 0,
0, FreeSize: 0,
} }
// Skip already added partitions // Skip already added partitions
@@ -95,7 +100,7 @@ func GetMountedPartitions(hiddenPartitions, hiddenFilesystems []string) []partit
// Set partition label if available // Set partition label if available
if value, ok := labels[p.Device]; ok { if value, ok := labels[p.Device]; ok {
p.Label = value p.Label = strings.ReplaceAll(value, "\\x20", " ")
} }
// Get partition total, used and free space // Get partition total, used and free space
+139 -15
View File
@@ -2,25 +2,29 @@ package main
import ( import (
"fmt" "fmt"
"os"
"os/exec" "os/exec"
"path"
"strconv"
"strings" "strings"
) )
type PackageManager struct { type PackageManager struct {
Name string Name string
ExecutableName string ExecutableName string
PackageListCommand string GetPackages func(...any) int
FunctionInput []any
} }
var PackageManagers = []PackageManager{ var PackageManagers = []PackageManager{
{Name: "dpkg", ExecutableName: "dpkg", PackageListCommand: "dpkg-query -f '${Package}\\n' -W"}, {Name: "dpkg", ExecutableName: "dpkg", GetPackages: pmFileLines, FunctionInput: []any{"/var/lib/dpkg/status", "Status: install ok installed"}},
{Name: "pacman", ExecutableName: "pacman", PackageListCommand: "pacman -Q"}, {Name: "pacman", ExecutableName: "pacman", GetPackages: pmDirectoryElements, FunctionInput: []any{"/var/lib/pacman/local/", true}},
{Name: "rpm", ExecutableName: "rpm", PackageListCommand: "rpm -qa"}, {Name: "rpm", ExecutableName: "rpm", GetPackages: pmShellCommandLines, FunctionInput: []any{"rpm -qa"}},
{Name: "xbps", ExecutableName: "xbps-query", PackageListCommand: "xbps-query -l"}, {Name: "xbps", ExecutableName: "xbps-query", GetPackages: pmFileLines, FunctionInput: []any{"/var/db/xbps/pkgdb-0.38.plist", "<string>installed</string>"}},
{Name: "bpm", ExecutableName: "bpm", PackageListCommand: "bpm list -n"}, {Name: "bpm", ExecutableName: "bpm", GetPackages: pmDirectoryElements, FunctionInput: []any{"/var/lib/bpm/installed/"}},
{Name: "portage", ExecutableName: "emerge", PackageListCommand: "find /var/db/pkg/*/ -mindepth 1 -maxdepth 1"}, {Name: "portage", ExecutableName: "emerge", GetPackages: pmPortage},
{Name: "flatpak", ExecutableName: "flatpak", PackageListCommand: "flatpak list"}, {Name: "flatpak", ExecutableName: "flatpak", GetPackages: pmFlatpak},
{Name: "snap", ExecutableName: "snap", PackageListCommand: "snap list | tail +2"}, {Name: "snap", ExecutableName: "snap", GetPackages: pmSnap},
} }
func (pm *PackageManager) CountPackages() int { func (pm *PackageManager) CountPackages() int {
@@ -29,12 +33,7 @@ func (pm *PackageManager) CountPackages() int {
return 0 return 0
} }
output, err := exec.Command("/bin/sh", "-c", pm.PackageListCommand).Output() return pm.GetPackages(pm.FunctionInput...)
if err != nil {
return 0
}
return strings.Count(string(output), "\n")
} }
func GetInstalledPackages() (ret string) { func GetInstalledPackages() (ret string) {
@@ -51,3 +50,128 @@ func GetInstalledPackages() (ret string) {
return ret return ret
} }
func pmDirectoryElements(args ...any) int {
directory := args[0].(string)
dirsOnly := false
if len(args) >= 2 {
dirsOnly = args[1].(bool)
}
total := 0
dirEntries, _ := os.ReadDir(directory)
for _, entry := range dirEntries {
if !entry.IsDir() && dirsOnly {
continue
}
total++
}
return total
}
func pmFileLines(args ...any) int {
filepath := args[0].(string)
mustContain := ""
if len(args) >= 2 {
mustContain = args[1].(string)
}
total := 0
content, err := os.ReadFile(filepath)
if err == nil {
for _, line := range strings.Split(strings.TrimSpace(string(content)), "\n") {
if mustContain != "" && !strings.Contains(line, mustContain) {
continue
}
total++
}
}
return total
}
func pmShellCommandLines(args ...any) int {
command := args[0].(string)
output := runCommand(command, "/bin/sh")
return len(strings.Split(output, "\n"))
}
func pmShellCommandOutput(args ...any) int {
command := args[0].(string)
output := runCommand(command, "/bin/sh")
packageCount, _ := strconv.Atoi(output)
return packageCount
}
func pmPortage(args ...any) int {
portageDir := "/var/db/pkg"
total := 0
dirEntries, _ := os.ReadDir(portageDir)
for _, repo := range dirEntries {
if !repo.IsDir() {
continue
}
packages, _ := os.ReadDir(path.Join(portageDir, repo.Name()))
total += len(packages)
}
return total
}
func pmFlatpak(args ...any) int {
arch := GetArch()
flatpakDir := "/var/lib/flatpak"
total := 0
// Count applications
apps, err := os.ReadDir(path.Join(flatpakDir, "app"))
if err == nil {
for _, app := range apps {
if strings.HasSuffix(app.Name(), ".Locale") || strings.HasSuffix(app.Name(), ".Debug") {
continue
}
dirEntries, _ := os.ReadDir(path.Join(flatpakDir, "app", app.Name(), arch))
total += len(dirEntries)
}
}
// Count runtimes
runtimes, err := os.ReadDir(path.Join(flatpakDir, "runtime"))
if err == nil {
for _, runtime := range runtimes {
if strings.HasSuffix(runtime.Name(), ".Locale") || strings.HasSuffix(runtime.Name(), ".Debug") {
continue
}
dirEntries, _ := os.ReadDir(path.Join(flatpakDir, "runtime", runtime.Name(), arch))
total += len(dirEntries)
}
}
return total
}
func pmSnap(args ...any) int {
total := pmDirectoryElements("/snap", true)
if total > 0 {
return total - 1
}
total = pmDirectoryElements("/var/lib/snapd/snap", true)
if total > 0 {
return total - 1
}
return 0
}
+73 -50
View File
@@ -5,6 +5,7 @@ import (
"os/exec" "os/exec"
"path" "path"
"strings" "strings"
"syscall"
"github.com/mitchellh/go-ps" "github.com/mitchellh/go-ps"
) )
@@ -21,10 +22,6 @@ func GetDistroInfo() DistroInfo {
LongName: "Unknown", LongName: "Unknown",
ShortName: "Unknown", ShortName: "Unknown",
} }
if strings.TrimSpace(config.DistroName) != "" {
info.LongName = strings.TrimSpace(config.DistroName)
info.ShortName = strings.TrimSpace(config.DistroName)
}
// Detect release file location // Detect release file location
var releaseFile string var releaseFile string
@@ -38,20 +35,23 @@ func GetDistroInfo() DistroInfo {
return info return info
} }
// Read release file
releaseMap, err := ReadKeyValueFile(releaseFile) releaseMap, err := ReadKeyValueFile(releaseFile)
if err != nil { if err != nil {
return info return info
} }
// Set struct fields
if id, ok := releaseMap["ID"]; ok { if id, ok := releaseMap["ID"]; ok {
info.ID = id info.ID = id
} }
if longName, ok := releaseMap["PRETTY_NAME"]; ok && info.LongName == "Unknown" { if longName, ok := releaseMap["PRETTY_NAME"]; ok {
info.LongName = longName info.LongName = longName
} }
if shortName, ok := releaseMap["NAME"]; ok && info.ShortName == "Unknown" { if shortName, ok := releaseMap["NAME"]; ok {
info.ShortName = shortName info.ShortName = shortName
} }
return info return info
} }
@@ -64,78 +64,101 @@ func GetDistroAsciiArt() string {
(| | ) (| | )
/'\_ _/'\ /'\_ _/'\
\___)=(___/` \___)=(___/`
var id string
if config.Ascii == "auto" { // Get ascii name to use
id = GetDistroInfo().ID var asciiName string
if Ascii != "" {
asciiName = Ascii
} else if config.Ascii == "auto" {
asciiName = GetDistroInfo().ID
} else { } else {
id = config.Ascii asciiName = config.Ascii
} }
// Check for ascii art in home directory
userConfDir, err := os.UserConfigDir() userConfDir, err := os.UserConfigDir()
if err == nil {
if _, err := os.Stat(path.Join(userConfDir, "stormfetch/ascii/", asciiName)); err == nil {
if bytes, err := os.ReadFile(path.Join(userConfDir, "stormfetch/ascii/", asciiName)); err == nil {
return strings.TrimRight(string(bytes), "\n")
}
}
}
// Check for ascii art in system config directory
if _, err := os.Stat(path.Join(SystemConfigDir, "stormfetch/ascii/", asciiName)); err == nil {
if bytes, err := os.ReadFile(path.Join(SystemConfigDir, "stormfetch/ascii/", asciiName)); err == nil {
return strings.TrimRight(string(bytes), "\n")
}
}
return defaultAscii
}
func GetArch() string {
uname := syscall.Utsname{}
err := syscall.Uname(&uname)
if err != nil { if err != nil {
if _, err := os.Stat(path.Join(systemConfigDir, "stormfetch/ascii/", id)); err == nil { return "unknown"
bytes, err := os.ReadFile(path.Join(systemConfigDir, "stormfetch/ascii/", id)) }
var byteString [65]byte
var indexLength int
for ; uname.Machine[indexLength] != 0; indexLength++ {
byteString[indexLength] = uint8(uname.Machine[indexLength])
}
return string(byteString[:indexLength])
}
func GetKernel() (string, string) {
uname := syscall.Utsname{}
err := syscall.Uname(&uname)
if err != nil { if err != nil {
return defaultAscii return "unknown", "unknown"
} }
return string(bytes)
} else { var kernelNameByteString [65]byte
return defaultAscii var kernelNameLength int
for ; uname.Sysname[kernelNameLength] != 0; kernelNameLength++ {
kernelNameByteString[kernelNameLength] = uint8(uname.Sysname[kernelNameLength])
} }
var kernelReleaseByteString [65]byte
var kernelReleaseLength int
for ; uname.Release[kernelReleaseLength] != 0; kernelReleaseLength++ {
kernelReleaseByteString[kernelReleaseLength] = uint8(uname.Release[kernelReleaseLength])
} }
if _, err := os.Stat(path.Join(userConfDir, "stormfetch/ascii/", id)); err == nil {
bytes, err := os.ReadFile(path.Join(userConfDir, "stormfetch/ascii/", id)) return string(kernelNameByteString[:kernelNameLength]), string(kernelReleaseByteString[:kernelReleaseLength])
if err != nil {
return defaultAscii
}
return string(bytes)
} else if _, err := os.Stat(path.Join(systemConfigDir, "stormfetch/ascii/", id)); err == nil {
bytes, err := os.ReadFile(path.Join(systemConfigDir, "stormfetch/ascii/", id))
if err != nil {
return defaultAscii
}
return strings.TrimRight(string(bytes), "\n\t ")
} else {
return defaultAscii
}
} }
func GetInitSystem() string { func GetInitSystem() string {
runCommand := func(command string) string {
cmd := exec.Command("/bin/bash", "-c", command)
workdir, err := os.Getwd()
if err != nil {
return ""
}
cmd.Dir = workdir
cmd.Env = os.Environ()
out, err := cmd.Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(out))
}
process, err := ps.FindProcess(1) process, err := ps.FindProcess(1)
if err != nil { if err != nil {
return "" return ""
} }
// Return if init system can't be found
if process == nil {
return ""
}
// Special cases // Special cases
// OpenRC check // OpenRC check
if _, err := os.Stat("/usr/sbin/openrc"); err == nil { if _, err := os.Stat("/usr/sbin/openrc"); err == nil {
return "OpenRC " + runCommand("openrc --version | awk '{print $3}'") return "OpenRC " + runCommand("openrc --version | awk '{print $3}'", "/bin/sh")
} }
// Default PID 1 process name checking // Default PID 1 process name checking
switch process.Executable() { switch process.Executable() {
case "systemd": case "systemd":
return "Systemd " + runCommand("systemctl --version | head -n1 | awk '{print $2}'") return "Systemd " + runCommand("systemctl --version | head -n1 | awk '{print $2}'", "/bin/sh")
case "runit": case "runit":
return "Runit" return "Runit"
case "dinit": case "dinit":
return "Dinit " + runCommand("dinit --version | head -n1 | awk '{print substr($3, 1, length($3)-1)}'") return "Dinit " + runCommand("dinit --version | head -n1 | awk '{print substr($3, 1, length($3)-1)}'", "/bin/sh")
case "enit": case "enit":
return "Enit " + runCommand("enit --version | awk '{print $3}'") return "Enit " + runCommand("enit --version | awk '{print $3}'", "/bin/sh")
default: default:
return process.Executable() return process.Executable()
} }
+94 -49
View File
@@ -1,31 +1,23 @@
package main package main
import ( import (
"github.com/mitchellh/go-ps"
"log" "log"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"slices" "slices"
"strconv" "strconv"
"strings" "strings"
"github.com/mitchellh/go-ps"
) )
type DEWM struct {
Name string
Type string
Version string
}
func GetShell() string { func GetShell() string {
runCommand := func(command string) string {
cmd := exec.Command("/bin/bash", "-c", command)
workdir, err := os.Getwd()
if err != nil {
return ""
}
cmd.Dir = workdir
cmd.Env = os.Environ()
out, err := cmd.Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(out))
}
file, err := os.ReadFile("/etc/passwd") file, err := os.ReadFile("/etc/passwd")
if err != nil { if err != nil {
return "" return ""
@@ -47,19 +39,19 @@ func GetShell() string {
case "dash": case "dash":
return "Dash" return "Dash"
case "bash": case "bash":
return "Bash " + runCommand("echo $BASH_VERSION") return "Bash " + runCommand("echo $BASH_VERSION", "/bin/sh")
case "zsh": case "zsh":
return "Zsh " + runCommand("$SHELL --version | awk '{print $2}'") return "Zsh " + runCommand("$SHELL --version | awk '{print $2}'", "/bin/sh")
case "fish": case "fish":
return "Fish " + runCommand("$SHELL --version | awk '{print $3}'") return "Fish " + runCommand("$SHELL --version | awk '{print $3}'", "/bin/sh")
case "nu": case "nu":
return "Nushell " + runCommand("$SHELL --version") return "Nushell " + runCommand("$SHELL --version", "/bin/sh")
default: default:
return "Unknown" return "Unknown"
} }
} }
func GetDEWM() string { func GetDEWM() DEWM {
processes, err := ps.Processes() processes, err := ps.Processes()
if err != nil { if err != nil {
log.Fatalf("Error: could not get processes: %s", err) log.Fatalf("Error: could not get processes: %s", err)
@@ -72,48 +64,101 @@ func GetDEWM() string {
processExists := func(process string) bool { processExists := func(process string) bool {
return slices.Contains(executables, process) return slices.Contains(executables, process)
} }
runCommand := func(command string) string {
cmd := exec.Command("/bin/bash", "-c", command)
workdir, err := os.Getwd()
if err != nil {
return ""
}
cmd.Dir = workdir
cmd.Env = os.Environ()
out, err := cmd.Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(out))
}
if processExists("plasmashell") { if processExists("plasmashell") {
return "KDE Plasma " + runCommand("plasmashell --version | awk '{print $2}'") dewm := DEWM{
Name: "KDE Plasma",
Type: "DE",
Version: runCommand("plasmashell --version | awk '{print $2}'", "/bin/sh"),
}
return dewm
} else if processExists("gnome-session") { } else if processExists("gnome-session") {
return "Gnome " + runCommand("gnome-shell --version | awk '{print $3}'") dewm := DEWM{
Name: "Gnome",
Type: "DE",
Version: runCommand("gnome-shell --version | awk '{print $3}'", "/bin/sh"),
}
return dewm
} else if processExists("xfce4-session") { } else if processExists("xfce4-session") {
return "XFCE " + runCommand("xfce4-session --version | head -n1 | awk '{print $2}'") dewm := DEWM{
Name: "XFCE",
Type: "DE",
Version: runCommand("xfce4-session --version | head -n1 | awk '{print $2}'", "/bin/sh"),
}
return dewm
} else if processExists("cinnamon") { } else if processExists("cinnamon") {
return "Cinnamon " + runCommand("cinnamon --version | awk '{print $3}'") dewm := DEWM{
Name: "Cinnamon",
Type: "DE",
Version: runCommand("cinnamon --version | awk '{print $3}'", "/bin/sh"),
}
return dewm
} else if processExists("mate-panel") { } else if processExists("mate-panel") {
return "MATE " + runCommand("mate-about --version | awk '{print $4}'") dewm := DEWM{
Name: "MATE",
Type: "DE",
Version: runCommand("mate-about --version | awk '{print $4}'", "/bin/sh"),
}
return dewm
} else if processExists("lxsession") { } else if processExists("lxsession") {
return "LXDE" dewm := DEWM{
Name: "LXDE",
Type: "DE",
Version: "",
}
return dewm
} else if processExists("lxqt-session") {
dewm := DEWM{
Name: "LXQt",
Type: "DE",
Version: runCommand("lxqt-session --version | head -n1 | awk '{print $2}'", "/bin/sh"),
}
return dewm
} else if processExists("i3") || processExists("i3-with-shmlog") { } else if processExists("i3") || processExists("i3-with-shmlog") {
return "i3 " + runCommand("i3 --version | awk '{print $3}'") dewm := DEWM{
Name: "i3",
Type: "WM",
Version: runCommand("i3 --version | awk '{print $3}'", "/bin/sh"),
}
return dewm
} else if processExists("sway") { } else if processExists("sway") {
if runCommand("sway --version | awk '{print $1}'") == "swayfx" { dewm := DEWM{
return "SwayFX " + runCommand("sway --version | awk '{print $3}'") Name: "Sway",
Type: "WM",
Version: runCommand("sway --version | awk '{print $3}'", "/bin/sh"),
}
if runCommand("sway --version | awk '{print $1}'", "/bin/sh") == "swayfx" {
dewm.Name = "SwayFX"
} else { } else {
return "Sway " + runCommand("sway --version | awk '{print $3}'") dewm.Name = "Sway"
} }
return dewm
} else if processExists("bspwm") { } else if processExists("bspwm") {
return "Bspwm " + runCommand("bspwm -v") dewm := DEWM{
Name: "Bspwm",
Type: "WM",
Version: runCommand("bspwm -v", "/bin/sh"),
}
return dewm
} else if processExists("Hyprland") { } else if processExists("Hyprland") {
return "Hyprland " + runCommand("hyprctl version | sed -n 3p | awk '{print $2}' | tr -d 'v,'") dewm := DEWM{
Name: "Hyprland",
Type: "WM",
Version: runCommand("hyprctl version | sed -n 3p | awk '{print $2}' | tr -d 'v,'", "/bin/sh"),
}
return dewm
} else if processExists("icewm-session") { } else if processExists("icewm-session") {
return "IceWM " + runCommand("icewm --version | awk '{print $2}'") dewm := DEWM{
Name: "IceWM",
Type: "WM",
Version: runCommand("icewm --version | awk '{print $2}'", "/bin/sh"),
}
return dewm
}
return DEWM{
Name: "Unknown",
Type: "Unknown",
Version: "Unknown",
} }
return ""
} }
func GetDisplayProtocol() string { func GetDisplayProtocol() string {
+57
View File
@@ -4,6 +4,8 @@ import (
"fmt" "fmt"
"math" "math"
"os" "os"
"os/exec"
"path"
"regexp" "regexp"
"strings" "strings"
) )
@@ -56,3 +58,58 @@ func ReadKeyValueFile(filepath string) (map[string]string, error) {
} }
return ret, nil return ret, nil
} }
func fetchAmdGpuName(productId, revision string) (string, error) {
productId = strings.ToUpper(productId)
revision = strings.ToUpper(revision[2:])
// Get cache directory
cachedir, err := os.UserCacheDir()
if err != nil {
return "", err
}
// Ensure amdgpu.ids file exists and download it if it doesn't
if _, err := os.Stat(path.Join(cachedir, "amdgpu.ids")); err != nil {
cmd := exec.Command("curl", "-o", path.Join(cachedir, "amdgpu.ids"), "https://gitlab.freedesktop.org/mesa/libdrm/-/raw/main/data/amdgpu.ids")
err = cmd.Run()
if err != nil {
return "", fmt.Errorf("Could not fetch amdgpu.ids using curl")
}
}
// Read amdgpu.ids file
amdgpuIds, err := os.ReadFile(path.Join(cachedir, "amdgpu.ids"))
if err != nil {
return "", err
}
// Parse read data and find GPU
for _, line := range strings.Split(string(amdgpuIds), "\n") {
if len(line) < 2 || line[0] == '#' {
continue
}
fields := strings.Split(line, ",\t")
if fields[0] == productId && fields[1] == revision {
return strings.TrimPrefix(fields[2], "AMD "), nil
}
}
return "", nil
}
func runCommand(command string, shell string) string {
cmd := exec.Command(shell, "-c", command)
workdir, err := os.Getwd()
if err != nil {
return ""
}
cmd.Dir = workdir
cmd.Env = os.Environ()
out, err := cmd.Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(out))
}