mirror of
https://github.com/EnumeratedDev/stormfetch.git
synced 2026-09-16 07:56:12 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ba86fdf1b
|
||
|
|
26af051000
|
||
|
|
b77dd0f621
|
||
|
|
47fb9b0e64
|
||
|
|
c712f1528a
|
||
|
|
33b346785b
|
||
|
|
f04a4b000a
|
||
|
|
3fdafd5c0c
|
||
|
|
da463b35e3
|
||
|
|
a919ffb71b
|
||
|
|
8147d302c8
|
||
|
|
8dfe7b3a0e
|
||
|
|
8cafe93b33
|
||
|
|
adcf06c149
|
||
|
|
5ef39f89cc
|
||
|
|
738d2c2086
|
||
|
|
637bd2c3e3 | ||
|
|
08035010c9
|
||
|
|
983b347d88
|
||
|
|
db96cd7e5e
|
||
|
|
96a2dee0e1
|
||
|
|
85d96fceec
|
||
|
|
d83440491b
|
||
|
|
16b27c4002
|
||
|
|
701585bd5e
|
||
|
|
b60c9df495
|
||
|
|
428ee7ceee
|
||
|
|
fbc9977bf4
|
||
|
|
2f71da0673
|
||
|
|
d4e324d79f
|
||
|
|
7f7c7d646d
|
||
|
|
cecce857e1
|
||
|
|
fabc1adbf7
|
||
|
|
3b7cb5e4e2
|
||
|
|
1b0ad32b6b
|
||
|
|
b52145614f
|
||
|
|
705a40047b
|
||
|
|
341138116b
|
||
|
|
051301aacd
|
||
|
|
3131d94429
|
||
|
|
c7bfaf7a31
|
||
|
|
9ce8e5ef0b
|
||
|
|
92ea038c77
|
||
|
|
9680c024b9
|
||
|
|
13cd6f1d45
|
||
|
|
e400e338a0
|
||
|
|
d1b095ba73
|
||
|
|
663e69638b
|
||
|
|
4e296b5104
|
||
|
|
eef69eed9a
|
||
|
|
489e6e2143
|
||
|
|
ba43a98926
|
||
|
|
1439a22378 | ||
|
|
74eb84e706 | ||
|
|
5ab8a6ee68 | ||
|
|
1ee2691b44 | ||
|
|
b6dba0061d | ||
|
|
d41c00f972 | ||
|
|
d51cfdf363 | ||
|
|
0f46e313e6 | ||
|
|
a76295c455 | ||
|
|
47fd83f8cd | ||
|
|
ca17578cac | ||
|
|
7d121c330d | ||
|
|
6185af37d5 | ||
|
|
cd0faaefe5 | ||
|
|
4b9bc59810 | ||
|
|
0ed976a06e | ||
|
|
ce9813a2af | ||
|
|
0d3d3fafb2 | ||
|
|
a99defd401 | ||
|
|
f34537eb5a | ||
|
|
120a1cf8e4 | ||
|
|
76974ee789 | ||
|
|
20cf7fe869 | ||
|
|
c5e02c07b5 | ||
|
|
e8a95e5313 | ||
|
|
0fd198d83e | ||
|
|
673f231c42 | ||
|
|
e311649a48 | ||
|
|
712fd78e21 | ||
|
|
7d83b0996d | ||
|
|
f3a016b674 | ||
|
|
0e2631d5b0 | ||
|
|
153758b68e | ||
|
|
ed45b739a5 | ||
|
|
8c380dbec4 | ||
|
|
7c493dc9f9 | ||
|
|
b8d7017299 | ||
|
|
cd08003752 | ||
|
|
c4c71fc49a | ||
|
|
efdbe94193 | ||
|
|
7b8c017ed0 | ||
|
|
66f3891b40 | ||
|
|
efae7c4b93 | ||
|
|
24da6ce083 | ||
|
|
b90b869643 | ||
|
|
720eb845ca | ||
|
|
81c4006e48 | ||
|
|
58b3e82346 | ||
|
|
ba67c074db | ||
|
|
c8b3f9f4a4 | ||
|
|
c05f34eb7d | ||
|
|
39b901e6fd | ||
|
|
2af3ab1156 | ||
|
|
aba10e3e9e | ||
|
|
0c376702c7 | ||
|
|
a5ffb4b32e | ||
|
|
4de0d34e28 | ||
|
|
7ed54ec1a5 | ||
|
|
2984a7da5f | ||
|
|
4a2833afff | ||
|
|
c13c730e4c | ||
|
|
4c72295972 | ||
|
|
a29a8f1dd9 | ||
|
|
1fc9a9ea79 | ||
|
|
01b23277fd | ||
|
|
75deb165ad | ||
|
|
56511c90db | ||
|
|
42a60296a9 | ||
|
|
af4c534685 | ||
|
|
25a596cd5e | ||
|
|
4d414daccc | ||
|
|
664b639fb9 |
@@ -0,0 +1,61 @@
|
||||
name: CI/CD pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "**"
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Fetch repository
|
||||
uses: actions/checkout@v6
|
||||
- name: Upgrade packages
|
||||
run: sudo apt update -y && sudo apt upgrade -y
|
||||
- name: Download and install dependencies
|
||||
run: sudo apt install -y make golang libgl-dev libx11-dev libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev libxxf86vm-dev
|
||||
- name: Build using make
|
||||
run: make PREFIX=/usr SYSCONFDIR=/etc
|
||||
- name: Install to root using make
|
||||
run: sudo make PREFIX=/usr SYSCONFDIR=/etc install
|
||||
- name: Run stormfetch
|
||||
run: stormfetch
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
needs: test
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && needs.test.result == 'success'
|
||||
steps:
|
||||
- name: Fetch repository
|
||||
uses: actions/checkout@v6
|
||||
- name: Check debian changelog version
|
||||
run: test $(grep '^stormfetch' debian/changelog | head -n1 | grep -Po '(?<=\().*(?=\))' | cut -d '-' -f1) == ${{ github.ref_name }}
|
||||
- name: Upgrade packages
|
||||
run: sudo apt update -y && sudo apt upgrade -y
|
||||
- name: Download and install dependencies
|
||||
run: sudo apt install -y make debhelper golang libgl-dev libx11-dev libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev libxxf86vm-dev
|
||||
- name: Build deb package
|
||||
run: dpkg-buildpackage -b
|
||||
- name: Read changelog
|
||||
id: read_changelog
|
||||
run: |
|
||||
r=$(cat CHANGELOG.md) # Read CHANGELOG.md
|
||||
r="${r//'%'/'%25'}" # Multiline escape sequences for %
|
||||
r="${r//$'\n'/'%0A'}" # Multiline escape sequences for '\n'
|
||||
r="${r//$'\r'/'%0D'}" # Multiline escape sequences for '\r'
|
||||
echo "RELEASE_BODY=$r" >> $GITHUB_OUTPUT # Set environment variable
|
||||
- name: Upload deb packages release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
release_name: v${{ github.ref_name }}
|
||||
file: ../*.deb
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
draft: true
|
||||
body: |
|
||||
${{ steps.read_changelog.outputs.RELEASE_BODY }}
|
||||
+10
-4
@@ -1,4 +1,10 @@
|
||||
.idea
|
||||
build
|
||||
stormfetch
|
||||
stormfetch.tar.gz
|
||||
# Ignore IDE directories
|
||||
/.idea
|
||||
|
||||
# Ignore build directories
|
||||
/build
|
||||
/deb/temp
|
||||
/deb/out
|
||||
|
||||
# Ignore act files
|
||||
event.json
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Stormfetch v8.0 Changelog
|
||||
### Changes:
|
||||
- [Add CI/CD pipeline](https://github.com/EnumeratedDev/stormfetch/commit/c712f1528af45fcd7859b28b084c3dce64d6e07c)
|
||||
- [Add debian package source](https://github.com/EnumeratedDev/stormfetch/commit/f04a4b000a419181dd5eb940440676260beaae5f)
|
||||
- [Separate monitor detection code into different program](https://github.com/EnumeratedDev/stormfetch/commit/da463b35e36eb4413d6ffc258f12398015fedb2b)
|
||||
### Fixes
|
||||
- [Return unknown GPU if device info is nil](https://github.com/EnumeratedDev/stormfetch/commit/8dfe7b3a0e0c1f95d34020b1f3fda5142f113671)
|
||||
- [Fix bash version checking](https://github.com/EnumeratedDev/stormfetch/commit/8147d302c8a8d170c6942378b4b74eac77d755ac)
|
||||
- [Fix multiple spaces when version number is empty](https://github.com/EnumeratedDev/stormfetch/commit/a919ffb71b78d706062030203029ef470b190e9c)
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 CapCreeperGR
|
||||
Copyright (c) 2024 EnumDev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,33 +1,56 @@
|
||||
ifeq ($(PREFIX),)
|
||||
PREFIX := /usr/local
|
||||
endif
|
||||
ifeq ($(BINDIR),)
|
||||
BINDIR := $(PREFIX)/bin
|
||||
endif
|
||||
ifeq ($(SYSCONFDIR),)
|
||||
# Installation paths
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
LIBEXECDIR ?= $(PREFIX)/libexec
|
||||
SYSCONFDIR := $(PREFIX)/etc
|
||||
endif
|
||||
ifeq ($(GO),)
|
||||
GO := /usr/bin/go
|
||||
endif
|
||||
|
||||
build:
|
||||
mkdir -p build
|
||||
$(GO) build -o build/stormfetch stormfetch
|
||||
# Compilers and tools
|
||||
GO ?= go
|
||||
|
||||
install: build/stormfetch config/
|
||||
mkdir -p $(DESTDIR)$(BINDIR)
|
||||
mkdir -p $(DESTDIR)$(SYSCONFDIR)/stormfetch/
|
||||
cp build/stormfetch $(DESTDIR)$(BINDIR)/stormfetch
|
||||
cp -r config/. $(DESTDIR)$(SYSCONFDIR)/stormfetch/
|
||||
# Compiler flags
|
||||
GOOS ?= $(shell $(GO) env | grep '^GOOS' | cut -d'=' -f2 | tr -d "'")
|
||||
GOARCH ?= $(shell $(GO) env | grep '^GOARCH' | cut -d'=' -f2 | tr -d "'")
|
||||
LDFLAGS ?= -w
|
||||
|
||||
compress: build/stormfetch config/
|
||||
mkdir -p stormfetch/$(BINDIR)
|
||||
mkdir -p stormfetch/$(SYSCONFDIR)/stormfetch/
|
||||
cp build/stormfetch stormfetch/$(BINDIR)/stormfetch
|
||||
cp -r config/. stormfetch/$(SYSCONFDIR)/stormfetch/
|
||||
tar --owner=root --group=root -czf stormfetch.tar.gz stormfetch
|
||||
rm -r stormfetch
|
||||
# Build-time variables
|
||||
VERSION ?= $(shell git describe --tags --dirty)
|
||||
|
||||
build: build-stormfetch build-stormfetch-monitor-detection
|
||||
|
||||
build-stormfetch:
|
||||
install -d build
|
||||
cd src/stormfetch; GOOS=$(GOOS) GOARCH=$(GOARCH) $(GO) build -ldflags "$(LDFLAGS) -X 'main.StormfetchVersion=$(VERSION)' -X 'main.SystemConfigDir=$(SYSCONFDIR)' -X 'main.LibexecDir=$(LIBEXECDIR)'" -o ../../build/stormfetch
|
||||
|
||||
build-stormfetch-monitor-detection:
|
||||
install -d build
|
||||
cd src/stormfetch-monitor-detection; GOOS=$(GOOS) GOARCH=$(GOARCH) $(GO) build -ldflags "$(LDFLAGS)" -o ../../build/stormfetch-monitor-detection
|
||||
|
||||
install: install-stormfetch install-stormfetch-monitor-detection install-config
|
||||
|
||||
install-stormfetch: build/stormfetch
|
||||
# Create directory
|
||||
install -dm755 $(DESTDIR)$(BINDIR)
|
||||
# Install binary
|
||||
install -m755 build/stormfetch $(DESTDIR)$(BINDIR)/stormfetch
|
||||
|
||||
install-stormfetch-monitor-detection: build/stormfetch-monitor-detection
|
||||
# Create directory
|
||||
install -dm755 $(DESTDIR)$(LIBEXECDIR)
|
||||
# Install binary
|
||||
install -m755 build/stormfetch-monitor-detection $(DESTDIR)$(LIBEXECDIR)/stormfetch-monitor-detection
|
||||
|
||||
install-config:
|
||||
# Create directory
|
||||
install -dm755 $(DESTDIR)$(SYSCONFDIR)
|
||||
# Install files
|
||||
cp -r config $(DESTDIR)$(SYSCONFDIR)/stormfetch/
|
||||
|
||||
uninstall:
|
||||
-rm -f $(DESTDIR)$(BINDIR)/stormfetch
|
||||
-rm -f $(DESTDIR)$(LIBEXEDIR)/stormfetch-monitor-detection
|
||||
-rm -rf $(DESTDIR)$(SYSCONFDIR)/stormfetch
|
||||
|
||||
clean:
|
||||
rm -r build/
|
||||
|
||||
.PHONY: build build-stormfetch build-stormfetch-monitor-detection install install-config uninstall clean
|
||||
|
||||
@@ -1,25 +1,32 @@
|
||||
|
||||
# Stormfetch
|
||||
## A simple linux fetch program written in go and bash
|
||||
|
||||
### Developers:
|
||||
- [CapCreeperGR ](https://gitlab.com/CapCreeperGR)
|
||||
## A simple linux fetch program written in go
|
||||
|
||||
### Project Information
|
||||
Stormfetch is a program that can read your system's information and display it in the terminal along with ascii art of the linux distribution you are running.
|
||||
Stormfetch is still in beta and 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
|
||||
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.
|
||||
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
|
||||

|
||||
|
||||
### Installation Guide
|
||||
#### Using a package manager
|
||||
| Distribution | Package name |
|
||||
|:----------------------:|:------------------------------------------ |
|
||||
| Arch Linux/Artix Linux | `stormfetch` from the AUR |
|
||||
| Debian/Ubuntu | latest .deb file from the releases section |
|
||||
| Tide Linux | `stormfetch` from the main repository |
|
||||
#### Building from source
|
||||
- Download `go` from your package manager or from the go website
|
||||
- Download `make` from your package manager
|
||||
- (Optional) Download `xorg-xhost` from your package manager to display DE/WM and monitor information
|
||||
- (Optional) Download `xorg-xdpyinfo` from your package manager to display Screen Resolution
|
||||
- (Optional) Download `lshw` from your package manager to display GPU information
|
||||
- Run the following command to compile the project
|
||||
```
|
||||
make
|
||||
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
|
||||
|
||||
+5
-4
@@ -1,4 +1,5 @@
|
||||
-`
|
||||
#/4;27;4;11
|
||||
%1 -`
|
||||
.o+`
|
||||
`ooo/
|
||||
`+oooo:
|
||||
@@ -7,9 +8,9 @@
|
||||
`/:-:++oooo+:
|
||||
`/++++/+++++++:
|
||||
`/++++++++++++++:
|
||||
`/+++ooooooooooooo/`
|
||||
./ooosssso++osssssso+`
|
||||
.oossssso-````/ossssss+`
|
||||
`/+++o%2oooooooo%1oooo/`
|
||||
%2 %1./%2ooosssso++osssssso%1+`
|
||||
%2 .oossssso-````/ossssss+`
|
||||
-osssssso. :ssssssso.
|
||||
:osssssss/ osssso+++.
|
||||
/ossssssss/ +ssssooo/-
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#/45;27;45;7
|
||||
%1 'o'
|
||||
'ooo'
|
||||
'ooxoo'
|
||||
'ooxxxoo'
|
||||
'oookkxxoo'
|
||||
'oiioxkkxxoo'
|
||||
':;:iiiioxxxoo'
|
||||
`'.;::ioxxoo'
|
||||
'-. `':;jiooo'
|
||||
'oooio-.. `'i:io'
|
||||
'ooooxxxxoio:,. `'-;'
|
||||
'ooooxxxxxkkxoooIi:-. `'
|
||||
'ooooxxxxxkkkkxoiiiiiji'
|
||||
'ooooxxxxxkxxoiiii:'` .i'
|
||||
'ooooxxxxxoi:::'` .;ioxo'
|
||||
'ooooxooi::'` .:iiixkxxo'
|
||||
'ooooi:'` `'';ioxxo'
|
||||
'i:'` '':io'
|
||||
'` `'
|
||||
@@ -0,0 +1,12 @@
|
||||
#/7;8;8;7
|
||||
%1 \\\\\\\\\\\\
|
||||
\\\ \\\
|
||||
\\\ \\\
|
||||
\\\ \\\\\\\\\\\\\\\\\
|
||||
\\\ \\\
|
||||
\\\ \\\
|
||||
\\\ %2______ \\\
|
||||
\\\ ///
|
||||
\\\ ///
|
||||
\\\ ///
|
||||
\\\////////////////
|
||||
@@ -0,0 +1,23 @@
|
||||
#/43;45;45;15
|
||||
%3.%1-------------------------:
|
||||
.%2+=%1========================.
|
||||
:%2++%1===%2++===%1===============- :%2++%1-
|
||||
:%2*++%1====%2+++++==%1===========- .==:
|
||||
-%2*+++%1=====%2+***++=%1=========:
|
||||
=%2*++++=%1=======------------:
|
||||
=%2*+++++=%1====- %3...%1
|
||||
.%2+*+++++%1=-===: .%2=+++=%1:
|
||||
:%2++++%1=====-==: -***%2**%1+
|
||||
:%2++=%1=======-=. .=+**+%3.%1
|
||||
.%2+%1==========-. %3.%1
|
||||
:%2+++++++%1====- %3.%1--==-%3.%1
|
||||
:%2++%1==========. %3:%2+++++++%1%3:
|
||||
%1.-===========. =*****+*+
|
||||
%1.-===========: .+*****+:
|
||||
%1-=======%2++++%1:::::::::::::::::::::::::-: %3.%1---:
|
||||
:======%2++++%1====%2+++******************=.
|
||||
%1:=====%2+++%1==========%2++++++++++++++*-
|
||||
%1.====%2++%1==============%2++++++++++*-
|
||||
%1.===%2+%1==================%2+++++++:
|
||||
%1.-=======================%2+++:
|
||||
%3..........................
|
||||
+17
-16
@@ -1,16 +1,17 @@
|
||||
_,met$$$$$gg.
|
||||
,g$$$$$$$$$$$$$$$P.
|
||||
,g$$P" """Y$$.".
|
||||
,$$P' `$$$.
|
||||
',$$P ,ggs. `$$b:
|
||||
`d$$' ,$P"' . $$$
|
||||
$$P d$' , $$P
|
||||
$$: $$. - ,d$$'
|
||||
$$; Y$b._ _,d$P'
|
||||
Y$$. `.`"Y$$$$P"'
|
||||
`$$b "-.__
|
||||
`Y$$
|
||||
`Y$$.
|
||||
`$$b.
|
||||
`Y$$b.
|
||||
`"Y$b._
|
||||
#/1;7;9;15
|
||||
%2 _,met&&&&&gg.
|
||||
,g&&&&&&&&&&&&&&&P.
|
||||
,g&&P" """Y&&.".
|
||||
,&&P' `&&&.
|
||||
',&&P ,ggs. `&&b:
|
||||
`d&&' ,&P"' %1.%2 &&&
|
||||
&&P d&' %1,%2 &&P
|
||||
&&: &&. %1-%2 ,d&&'
|
||||
&&; Y&b._ _,d&P'
|
||||
Y&&. %1`.%2`"Y&&&&P"'
|
||||
%2 `&&b %1"-.__
|
||||
%2 `Y&&
|
||||
`Y&&.
|
||||
`&&b.
|
||||
`Y&&b.
|
||||
`"Y&b._
|
||||
|
||||
+14
-13
@@ -1,19 +1,20 @@
|
||||
.',;::::;,'.
|
||||
#/12;7;4;15
|
||||
%1 .',;::::;,'.
|
||||
.';:cccccccccccc:;,.
|
||||
.;cccccccccccccccccccccc;.
|
||||
.:cccccccccccccccccccccccccc:.
|
||||
.;ccccccccccccc;.:dddl:.;ccccccc;.
|
||||
.:ccccccccccccc;OWMKOOXMWd;ccccccc:.
|
||||
.:ccccccccccccc;KMMc;cc;xMMc;ccccccc:.
|
||||
,cccccccccccccc;MMM.;cc;;WW:;cccccccc,
|
||||
:cccccccccccccc;MMM.;cccccccccccccccc:
|
||||
:ccccccc;oxOOOo;MMM0OOk.;cccccccccccc:
|
||||
cccccc;0MMKxdd:;MMMkddc.;cccccccccccc;
|
||||
ccccc;XM0';cccc;MMM.;cccccccccccccccc'
|
||||
ccccc;MMo;ccccc;MMW.;ccccccccccccccc;
|
||||
ccccc;0MNc.ccc.xMMd;ccccccccccccccc;
|
||||
cccccc;dNMWXXXWM0:;cccccccccccccc:,
|
||||
cccccccc;.:odl:.;cccccccccccccc:,.
|
||||
.;ccccccccccccc;%2.:dddl:.%1;ccccccc;.
|
||||
.:ccccccccccccc;%2OWMKOOXMWd%1;ccccccc:.
|
||||
.:ccccccccccccc;%2KMMc%1;cc;%2xMMc%1;ccccccc:.
|
||||
,cccccccccccccc;%2MMM.%1;cc;%2;WW:%1;cccccccc,
|
||||
:cccccccccccccc;%2MMM.%1;cccccccccccccccc:
|
||||
:ccccccc;%2oxOOOo%1;%2MMM0OOk.%1;cccccccccccc:
|
||||
cccccc;%20MMKxdd:%1;%2MMMkddc.%1;cccccccccccc;
|
||||
ccccc;%2XM0'%1;cccc;%2MMM.%1;cccccccccccccccc'
|
||||
ccccc;%2MMo%1;ccccc;%2MMW.%1;ccccccccccccccc;
|
||||
ccccc;%20MNc.%1ccc%2.xMMd%1;ccccccccccccccc;
|
||||
cccccc;%2dNMWXXXWM0:%1;cccccccccccccc:,
|
||||
cccccccc;%2.:odl:.%1;cccccccccccccc:,.
|
||||
:cccccccccccccccccccccccccccc:'.
|
||||
.:cccccccccccccccccccccc:;,..
|
||||
'::cccccccccccccc::;,.
|
||||
|
||||
+18
-17
@@ -1,18 +1,19 @@
|
||||
-/oyddmdhs+:.
|
||||
-odNMMMMMMMMNNmhy+-`
|
||||
-yNMMMMMMMMMMMNNNmmdhy+-
|
||||
`omMMMMMMMMMMMMNmdmmmmddhhy/`
|
||||
omMMMMMMMMMMMNhhyyyohmdddhhhdo`
|
||||
.ydMMMMMMMMMMdhs++so/smdddhhhhdm+`
|
||||
oyhdmNMMMMMMMNdyooydmddddhhhhyhNd.
|
||||
:oyhhdNNMMMMMMMNNNmmdddhhhhhyymMh
|
||||
.:+sydNMMMMMNNNmmmdddhhhhhhmMmy
|
||||
/mMMMMMMNNNmmmdddhhhhhmMNhs:
|
||||
`oNMMMMMMMNNNmmmddddhhdmMNhs+`
|
||||
`sNMMMMMMMMNNNmmmdddddmNMmhs/.
|
||||
/NMMMMMMMMNNNNmmmdddmNMNdso:`
|
||||
+MMMMMMMNNNNNmmmmdmNMNdso/-
|
||||
yMMNNNNNNNmmmmmNNMmhs+/-`
|
||||
/hMMNNNNNNNNMNdhs++/-`
|
||||
`/ohdmmddhys+++/:.`
|
||||
#/5;7;13;69
|
||||
%1 -/oyddmdhs+:.
|
||||
-o%2dNMMMMMMMMNNmhy+%1-`
|
||||
-y%2NMMMMMMMMMMMNNNmmdhy%1+-
|
||||
`o%2mMMMMMMMMMMMMNmdmmmmddhhy%1/`
|
||||
om%2MMMMMMMMMMMN%1hhyyyo%2hmdddhhhd%1o`
|
||||
.y%2dMMMMMMMMMMd%1hs++so/s%2mdddhhhhdm%1+`
|
||||
oy%2hdmNMMMMMMMN%1dyooy%2dmddddhhhhyhN%1d.
|
||||
:o%2yhhdNNMMMMMMMNNNmmdddhhhhhyym%1Mh
|
||||
.:%2+sydNMMMMMNNNmmmdddhhhhhhmM%1my
|
||||
/m%2MMMMMMNNNmmmdddhhhhhmMNh%1s:
|
||||
`o%2NMMMMMMMNNNmmmddddhhdmMNhs%1+`
|
||||
`s%2NMMMMMMMMNNNmmmdddddmNMmhs%1/.
|
||||
/N%2MMMMMMMMNNNNmmmdddmNMNdso%1:`
|
||||
+M%2MMMMMMNNNNNmmmmdmNMNdso%1/-
|
||||
yM%2MNNNNNNNmmmmmNNMmhs+/%1-`
|
||||
/h%2MMNNNNNNNNMNdhs++/%1-`
|
||||
`/%2ohdmmddhys+++/:%1.`
|
||||
`-//////:--.
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#/34;15;46;252
|
||||
%2 ...-:::::-...
|
||||
%2 .-MMMMMMMMMMMMMMM-.
|
||||
.-MMMM%1`..-:::::::-..`%2MMMM-.
|
||||
.:MMMM%1.:MMMMMMMMMMMMMMM:.%2MMMM:.
|
||||
-MMM%1-M---MMMMMMMMMMMMMMMMMMM.%2MMM-
|
||||
`:MMM%1:MM` :MMMM:....::-...-MMMM:%2MMM:`
|
||||
:MMM%1:MMM` :MM:` `` `` `:MMM:%2MMM:
|
||||
.MMM%1.MMMM` :MM. -MM. .MM- `MMMM.%2MMM.
|
||||
:MMM%1:MMMM` :MM. -MM- .MM: `MMMM-%2MMM:
|
||||
:MMM%1:MMMM` :MM. -MM- .MM: `MMMM:%2MMM:
|
||||
:MMM%1:MMMM` :MM. -MM- .MM: `MMMM-%2MMM:
|
||||
.MMM%1.MMMM` :MM:--:MM:--:MM: `MMMM.%2MMM.
|
||||
:MMM%1:MMM- `-MMMMMMMMMMMM-` -MMM-%2MMM:
|
||||
:MMM%1:MMM:` `:MMM:%2MMM:
|
||||
.MMM%1.MMMM:--------------:MMMM.%2MMM.
|
||||
'-MMMM%1.-MMMMMMMMMMMMMMM-.%2MMMM-'
|
||||
'.-MMMM%1``--:::::--``%2MMMM-.'
|
||||
%2 '-MMMMMMMMMMMMM-'
|
||||
%2 ``-:::::-``
|
||||
+13
-12
@@ -1,17 +1,18 @@
|
||||
.;ldkO0000Okdl;.
|
||||
#/2;7;10;15
|
||||
%2 .;ldkO0000Okdl;.
|
||||
.;d00xl:^''''''^:ok00d;.
|
||||
.d00l' 'o00d.
|
||||
.d0Kd' Okxol:;,. :O0d.
|
||||
.OKKKK0kOKKKKKKKKKKOxo:, lKO.
|
||||
,0KKKKKKKKKKKKKKKK0P^,,,^dx: ;00,
|
||||
.OKKKKKKKKKKKKKKKKk'.oOPPb.'0k. cKO.
|
||||
:KKKKKKKKKKKKKKKKK: kKx..dd lKd 'OK:
|
||||
dKKKKKKKKKKKOx0KKKd ^0KKKO' kKKc dKd
|
||||
dKKKKKKKKKKKK;.;oOKx,..^..;kKKK0. dKd
|
||||
:KKKKKKKKKKKK0o;...^cdxxOK0O/^^' .0K:
|
||||
kKKKKKKKKKKKKKKK0x;,,......,;od lKk
|
||||
'0KKKKKKKKKKKKKKKKKKKKK00KKOo^ c00'
|
||||
'kKKKOxddxkOO00000Okxoc;'' .dKk'
|
||||
.d0Kd'%1 Okxol:;,. %2:O0d.
|
||||
.OK%1KKK0kOKKKKKKKKKKOxo:, %2lKO.
|
||||
,0K%1KKKKKKKKKKKKKKK0P^%2,,,%1^dx:%2 ;00,
|
||||
.OK%1KKKKKKKKKKKKKKKk'%2.oOPPb.%1'0k.%2 cKO.
|
||||
:KK%1KKKKKKKKKKKKKKK: %2kKx..dd %1lKd%2 'OK:
|
||||
dKK%1KKKKKKKKKOx0KKKd %2^0KKKO' %1kKKc%2 dKd
|
||||
dKK%1KKKKKKKKKK;.;oOKx,..%2^%1..;kKKK0.%2 dKd
|
||||
:KK%1KKKKKKKKKK0o;...^cdxxOK0O/^^' %2.0K:
|
||||
kKK%1KKKKKKKKKKKKK0x;,,......,;od %2lKk
|
||||
'0K%1KKKKKKKKKKKKKKKKKKKK00KKOo^ %2c00'
|
||||
'kK%1KKOxddxkOO00000Okxoc;'' %2.dKk'
|
||||
l0Ko. .c00l'
|
||||
'l0Kk:. .;xK0l'
|
||||
'lkK0xl:;,,,,;:ldO0kl'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
......
|
||||
#/2;7;10;15
|
||||
%1 ......
|
||||
.,cdxxxoc,. .:kKMMMNWMMMNk:.
|
||||
cKMMN0OOOKWMMXo. ; ;0MWk:. .:OMMk.
|
||||
;WMK;. .lKMMNM, :NMK, .OMW;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#/33;7;11;15
|
||||
%1 /////////////
|
||||
/////////////////////
|
||||
///////%2*767%1////////////////
|
||||
//////%27676767676*%1//////////////
|
||||
/////%276767%1//%27676767%1//////////////
|
||||
/////%2767676%1///%2*76767%1///////////////
|
||||
///////%2767676%1///%276767%1.///%27676*%1///////
|
||||
/////////%2767676%1//%276767%1///%2767676%1////////
|
||||
//////////%276767676767%1////%276767%1/////////
|
||||
///////////%276767676%1//////%27676%1//////////
|
||||
////////////,%27676%1,///////%2767%1///////////
|
||||
/////////////*%27676%1///////%276%1////////////
|
||||
///////////////%27676%1////////////////////
|
||||
///////////////%27676%1///%2767%1////////////
|
||||
//////////////////////%2'%1////////////
|
||||
//////%2.7676767676767676767,%1//////
|
||||
/////%2767676767676767676767%1/////
|
||||
///////////////////////////
|
||||
/////////////////////
|
||||
/////////////
|
||||
+15
-17
@@ -1,17 +1,15 @@
|
||||
@@@@@@@@@
|
||||
@@@~~~~~~~~~@@
|
||||
@@~~~ ~~@
|
||||
@~~ @
|
||||
@~ @@@ @
|
||||
@@~ @ @@
|
||||
@@~~ @
|
||||
@@@~~ ~@@ @@@
|
||||
~~~ ~~@@@ @@@@~~~
|
||||
~~~@@@@~~~~
|
||||
@@@@ @@@@@@@ ~~~~ @@@@
|
||||
~~~~@@@@ @@~~~~~~~@@ @@@@
|
||||
~~~~@@@@~~ ~~@@@@~~~~
|
||||
@@@ ~~~~ @@@@@ ~~~~ @@@
|
||||
~~~@@@@ @@@@~~~~~@@@@ @@@@~~~
|
||||
~~~~@@@~~~~ ~~~~@@@~~~
|
||||
~~~ ~~~
|
||||
#/27;39;5;12
|
||||
%1@@@@ %1@@@%2*
|
||||
%1@@%2~~~~%1@@ %1@@%2~~~
|
||||
%1@@%2~~ %2~~%1@ %1@@%2~~
|
||||
%1@@%2~~ %2~%1@ %1@@@%2~~
|
||||
%2*%1@@%2~~ %1@@@@ %2~%1@@@@%2~~~ %1@@@%2*
|
||||
%2~~ %1@@%2~~~~%1@@ %2~~~~ %1@@%2~~~
|
||||
%1@@%2~~ %2~~%1@ %1@@%2~~
|
||||
%1@@%2~~ %2~%1@ %1@@@%2~~
|
||||
%2*%1@@%2~~ %1@@@@ %2~%1@@@@%2~~~ %1@@@%2*
|
||||
%2~~ %1@@%2~~~~%1@@ %2~~~~ %1@@%2~~~
|
||||
%1@@%2~~ %2~~%1@ %1@@%2~~
|
||||
%1@@%2~~ %2~%1@ %1@@@%2~~
|
||||
%2*%1@@%2~~ %2~%1@@@@%2~~~
|
||||
%2~~ %2~~~~
|
||||
|
||||
+19
-18
@@ -1,20 +1,21 @@
|
||||
.-/+oossssoo+\-.
|
||||
´:+ssssssssssssssssss+:`
|
||||
#/1;7;3;15
|
||||
%1 .-/+oossssoo+\-.
|
||||
':+ssssssssssssssssss+:'
|
||||
-+ssssssssssssssssssyyssss+-
|
||||
.ossssssssssssssssssdMMMNysssso.
|
||||
/ssssssssssshdmmNNmmyNMMMMhssssss\
|
||||
+ssssssssshmydMMMMMMMNddddyssssssss+
|
||||
/sssssssshNMMMyhhyyyyhmNMMMNhssssssss\
|
||||
.ssssssssdMMMNhsssssssssshNMMMdssssssss.
|
||||
+sssshhhyNMMNyssssssssssssyNMMMysssssss+
|
||||
ossyNMMMNyMMhsssssssssssssshmmmhssssssso
|
||||
ossyNMMMNyMMhsssssssssssssshmmmhssssssso
|
||||
+sssshhhyNMMNyssssssssssssyNMMMysssssss+
|
||||
.ssssssssdMMMNhsssssssssshNMMMdssssssss.
|
||||
\sssssssshNMMMyhhyyyyhdNMMMNhssssssss/
|
||||
+sssssssssdmydMMMMMMMMddddyssssssss+
|
||||
\ssssssssssshdmNNNNmyNMMMMhssssss/
|
||||
.ossssssssssssssssssdMMMNysssso.
|
||||
-+sssssssssssssssssyyyssss+-
|
||||
`:+ssssssssssssssssss+:`
|
||||
.ossssssssssssssssss%2dMMMNy%1sssso.
|
||||
/sssssssssss%2hdmmNNmmyNMMMMh%1ssssss\
|
||||
+sssssssss%2hm%1yd%2MMMMMMMNddddy%1ssssssss+
|
||||
/ssssssss%2hNMMM%1yh%2hyyyyhmNMMMNh%1ssssssss\
|
||||
.ssssssss%2dMMMNh%1ssssssssss%2hNMMMd%1ssssssss.
|
||||
+ssss%2hhhyNMMNy%1ssssssssssss%2yNMMMy%1sssssss+
|
||||
oss%2yNMMMNyMMh%1ssssssssssssss%2hmmmh%1ssssssso
|
||||
oss%2yNMMMNyMMh%1sssssssssssssshmmmh%1ssssssso
|
||||
+ssss%2hhhyNMMNy%1ssssssssssss%2yNMMMy%1sssssss+
|
||||
.ssssssss%2dMMMNh%1ssssssssss%2hNMMMd%1ssssssss.
|
||||
\ssssssss%2hNMMM%1yh%2hyyyyhdNMMMNh%1ssssssss/
|
||||
+sssssssss%2dm%1yd%2MMMMMMMMddddy%1ssssssss+
|
||||
\sssssssssss%2hdmNNNNmyNMMMMh%1ssssss/
|
||||
.ossssssssssssssssss%2dMMMNy%1sssso.
|
||||
-+sssssssssssssssss%2yyy%1ssss+-
|
||||
':+ssssssssssssssssss+:'
|
||||
.-\+oossssoo+/-.
|
||||
@@ -0,0 +1,19 @@
|
||||
#/2;36;72;15
|
||||
%2:::::----:::::
|
||||
%2::----------------::=
|
||||
%2:-------------------::
|
||||
%1 : %2:::. ..:-------:
|
||||
%1 =*=. %2.:-----:
|
||||
%1 =****- %2:-----:
|
||||
%1=*****- %2:::::: :-----:
|
||||
%1=****+ %2:--------: :----:
|
||||
%1+****- %2.----------. :----:
|
||||
%1+****- %2.----------. :-----
|
||||
%1=****+ %2:--------: :----:
|
||||
%1=*****- %2:::::: :-----:
|
||||
%1 =*****- %2:----:
|
||||
%1 =*****+: %2:-:
|
||||
%1 =*******=:: ::=-. %2.
|
||||
%1 ==*******************-
|
||||
%1 ===****************=-
|
||||
%1 ===+=****++===
|
||||
@@ -1,3 +0,0 @@
|
||||
distro_ascii: auto
|
||||
fetch_script: auto
|
||||
dependency_warning: true
|
||||
@@ -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
|
||||
@@ -1,21 +0,0 @@
|
||||
source fetch_script_functions.sh
|
||||
|
||||
echo "Distribution: ${DISTRO_LONG_NAME} ($(uname -m))"
|
||||
echo "Hostname: $(cat /etc/hostname)"
|
||||
echo "Kernel: $(uname -s) $(uname -r)"
|
||||
echo "Packages: $(get_packages)"
|
||||
echo "Shell: $(get_shell)"
|
||||
echo "CPU: $(get_cpu_name) ($(nproc) threads)"
|
||||
if command_exists lshw; then
|
||||
echo "GPU: $(lshw -class display 2> /dev/null | grep 'product' | cut -d":" -f2 | xargs)"
|
||||
fi
|
||||
echo "Memory: $(get_used_mem) MiB / $(get_total_mem) MiB"
|
||||
if xhost >& /dev/null ; then
|
||||
if get_de_wm &> /dev/null; then
|
||||
echo "DE/WM: $(get_de_wm)"
|
||||
fi
|
||||
if command_exists xdpyinfo ; then
|
||||
echo "Screen Resolution: $(get_screen_resolution)"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
command_exists() {
|
||||
if [ -z "$1" ]; then
|
||||
return 1
|
||||
fi
|
||||
if command -v "$1" &> /dev/null; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
get_shell() {
|
||||
case ${SHELL##*/} in
|
||||
"")
|
||||
echo "Unknown"
|
||||
;;
|
||||
sh|ash|dash|es)
|
||||
echo "${SHELL##*/} $(${SHELL##*/} --version)"
|
||||
;;
|
||||
bash)
|
||||
echo "${SHELL##*/} $(${SHELL##*/} -c "echo "'$BASH_VERSION')"
|
||||
;;
|
||||
*)
|
||||
SHELL_NAME=${SHELL##*/}
|
||||
SHELL_VERSION="$($SHELL --version)"
|
||||
SHELL_VERSION=${SHELL_VERSION//","}
|
||||
SHELL_VERSION=${SHELL_VERSION//" "}
|
||||
SHELL_VERSION=${SHELL_VERSION//"version"}
|
||||
SHELL_VERSION=${SHELL_VERSION//"$SHELL_NAME"}
|
||||
echo "$SHELL_NAME $SHELL_VERSION"
|
||||
unset SHELL_NAME
|
||||
unset SHELL_VERSION
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
get_cpu_name() {
|
||||
grep -m1 "model name" /proc/cpuinfo | cut -d: -f2 | xargs
|
||||
}
|
||||
|
||||
get_total_mem() {
|
||||
free --mebi -t | grep 'Total' | tr -s ' ' | cut -d" " -f2
|
||||
}
|
||||
|
||||
get_free_mem() {
|
||||
free --mebi -t | grep 'Total' | tr -s ' ' | cut -d" " -f4
|
||||
}
|
||||
|
||||
get_used_mem() {
|
||||
free --mebi -t | grep 'Total' | tr -s ' ' | cut -d" " -f3
|
||||
}
|
||||
|
||||
get_de_wm() {
|
||||
if ps -e | grep "plasmashell" &> /dev/null ; then
|
||||
echo "KDE Plasma $(plasmashell --version | awk '{print $2}')"
|
||||
elif ps -e | grep "gnome-session" &> /dev/null ; then
|
||||
echo "Gnome $(gnome-shell --version | awk '{print $3}')"
|
||||
elif ps -e | grep "xfce4-session" &> /dev/null ; then
|
||||
echo "XFCE $(xfce4-session --version | grep xfce4-session | awk '{print $2}')"
|
||||
elif ps -e | grep "cinnamon" &> /dev/null ; then
|
||||
echo "Cinnamon $(cinnamon --version | awk '{print $2}')"
|
||||
elif ps -e | grep "mate-panel" &> /dev/null ; then
|
||||
echo "Mate $(mate-about --version | awk '{print $4}')"
|
||||
elif ps -e | grep "lxsession" &> /dev/null ; then
|
||||
echo "LXDE"
|
||||
elif ps -e | grep "sway" &> /dev/null ; then
|
||||
echo "Sway $(sway --version | awk '{print $2}')"
|
||||
elif ps -e | grep "icewm-session" &> /dev/null ; then
|
||||
echo "IceWM $(icewm --version | awk '{print $2}' | sed 's/,//g')"
|
||||
elif [ ! -z $DESKTOP_SESSION ]; then
|
||||
echo "$DESKTOP_SESSION"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
get_screen_resolution() {
|
||||
if xhost >& /dev/null && command_exists xdpyinfo; then
|
||||
xdpyinfo | grep dimensions | tr -s ' ' | cut -d " " -f3
|
||||
fi
|
||||
}
|
||||
|
||||
get_packages() {
|
||||
ARRAY=()
|
||||
if command_exists dpkg; then
|
||||
ARRAY+=("$(dpkg-query -f '.\n' -W | wc -l) (dpkg)")
|
||||
fi
|
||||
if command_exists pacman; then
|
||||
ARRAY+=("$(pacman -Q | wc -l) (pacman)")
|
||||
fi
|
||||
if command_exists rpm; then
|
||||
ARRAY+=("$(rpm -qa | wc -l) (rpm)")
|
||||
fi
|
||||
if command_exists bpm; then
|
||||
ARRAY+=("$(bpm list -n) (bpm)")
|
||||
fi
|
||||
if command_exists emerge; then
|
||||
ARRAY+=("$(ls -l /var/db/pkg/ | wc -l) (emerge)")
|
||||
fi
|
||||
if command_exists flatpak; then
|
||||
ARRAY+=("$(flatpak list | wc -l) (flatpak)")
|
||||
fi
|
||||
if command_exists snap; then
|
||||
ARRAY+=("$(snap list | wc -l) (snap)")
|
||||
fi
|
||||
echo "${ARRAY[@]}"
|
||||
unset ARRAY
|
||||
}
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
stormfetch (7.4-1) trixie; urgency=medium
|
||||
|
||||
* Fix version number
|
||||
* Update debian codename
|
||||
* Fix debhelper dependency
|
||||
|
||||
-- EnumDev <[email protected]> Sat, 18 Apr 2026 14:18:37 +0300
|
||||
|
||||
stormfetch (7.4.3-1) stretch; urgency=medium
|
||||
|
||||
* Initial packaging work with dpkg-buildpackage.
|
||||
|
||||
-- EnumDev <[email protected]> Fri, 17 Apr 2026 16:54:39 +0300
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
9
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
Source: stormfetch
|
||||
Priority: optional
|
||||
Maintainer: EnumDev <[email protected]>
|
||||
Build-Depends:
|
||||
debhelper (>= 9),
|
||||
libgl-dev,
|
||||
libx11-dev,
|
||||
libxcursor-dev,
|
||||
libxi-dev,
|
||||
libxinerama-dev,
|
||||
libxrandr-dev,
|
||||
libxxf86vm-dev
|
||||
Vcs-Git: https://github.com/EnumeratedDev/stormfetch.git
|
||||
Vcs-Browser: https://github.com/EnumeratedDev/stormfetch
|
||||
|
||||
Package: stormfetch
|
||||
Section: universe/utils
|
||||
Architecture: any
|
||||
Depends: bash
|
||||
Suggests: stormfetch-monitor-detection
|
||||
Description: A linux fetch program written in go
|
||||
|
||||
Package: stormfetch-monitor-detection
|
||||
Section: universe/utils
|
||||
Architecture: any
|
||||
Depends: stormfetch,libgl1,libx11-6,libxcursor1,libxi6,libxinerama1,libxrandr2,libxxf86vm1
|
||||
Description: Monitor detection utility for stormfetch
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
clean:
|
||||
-rm -r debian/stormfetch
|
||||
-rm -r debian/stormfetch-monitor-detection
|
||||
|
||||
build:
|
||||
# Build program
|
||||
make PREFIX=/usr SYSCONFDIR=/etc
|
||||
|
||||
binary:
|
||||
# Create directories
|
||||
mkdir -p debian/stormfetch
|
||||
mkdir -p debian/stormfetch-monitor-detection
|
||||
# Install program
|
||||
make DESTDIR=debian/stormfetch PREFIX=/usr SYSCONFDIR=/etc install-stormfetch
|
||||
make DESTDIR=debian/stormfetch PREFIX=/usr SYSCONFDIR=/etc install-config
|
||||
# Install monitor detection program
|
||||
make DESTDIR=debian/stormfetch-monitor-detection PREFIX=/usr SYSCONFDIR=/etc install-stormfetch-monitor-detection
|
||||
# Finalize packages
|
||||
dh_gencontrol
|
||||
dh_builddeb
|
||||
@@ -1,5 +0,0 @@
|
||||
module stormfetch
|
||||
|
||||
go 1.22
|
||||
|
||||
require gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
@@ -1,3 +0,0 @@
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -1,221 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var configPath = ""
|
||||
var fetchScriptPath = ""
|
||||
|
||||
var config = StormfetchConfig{}
|
||||
|
||||
type StormfetchConfig struct {
|
||||
Ascii string `yaml:"distro_ascii"`
|
||||
FetchScript string `yaml:"fetch_script"`
|
||||
DependencyWarning bool `yaml:"dependency_warning"`
|
||||
}
|
||||
|
||||
type DistroInfo struct {
|
||||
ID string
|
||||
LongName string
|
||||
ShortName string
|
||||
}
|
||||
|
||||
func main() {
|
||||
readConfig()
|
||||
}
|
||||
|
||||
func readConfig() {
|
||||
// Get home directory
|
||||
configDir, _ := os.UserConfigDir()
|
||||
// Find valid config directory
|
||||
if _, err := os.Stat(path.Join(configDir, "stormfetch/config.yaml")); err == nil {
|
||||
configPath = path.Join(configDir, "stormfetch/config.yaml")
|
||||
} else if _, err := os.Stat("/etc/stormfetch/config.yaml"); err == nil {
|
||||
configPath = "/etc/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(configDir, "stormfetch/fetch_script.sh")); err == nil {
|
||||
fetchScriptPath = path.Join(configDir, "stormfetch/fetch_script.sh")
|
||||
} else if _, err := os.Stat("/etc/stormfetch/fetch_script.sh"); err == nil {
|
||||
fetchScriptPath = "/etc/stormfetch/fetch_script.sh"
|
||||
} else {
|
||||
log.Fatalf("Fetch script file not found: %s", err.Error())
|
||||
}
|
||||
// Show Dependency warning if enabled
|
||||
if config.DependencyWarning {
|
||||
dependencies := []string{"lshw", "xhost", "xdpyinfo"}
|
||||
var missing []string
|
||||
for _, depend := range dependencies {
|
||||
if _, err := os.Stat(path.Join("/usr/bin/", depend)); err != nil {
|
||||
missing = append(missing, depend)
|
||||
}
|
||||
}
|
||||
if len(missing) != 0 {
|
||||
fmt.Println("[WARNING] Stormfetch functionality may be limited due to the following dependencies not being installed:")
|
||||
for _, depend := range missing {
|
||||
fmt.Println(depend)
|
||||
}
|
||||
fmt.Println("You can disable this warning through your stormfetch config")
|
||||
}
|
||||
}
|
||||
//Execute fetch script
|
||||
cmd := exec.Command("/bin/bash", fetchScriptPath)
|
||||
cmd.Dir = path.Dir(fetchScriptPath)
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "DISTRO_LONG_NAME="+getDistroInfo().LongName)
|
||||
cmd.Env = append(cmd.Env, "DISTRO_SHORT_NAME="+getDistroInfo().ShortName)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Print Distro Information
|
||||
ascii := getDistroAscii()
|
||||
maxWidth := 0
|
||||
for _, line := range strings.Split(ascii, "\n") {
|
||||
if len(line) > maxWidth {
|
||||
maxWidth = len(line)
|
||||
}
|
||||
}
|
||||
for lineIndex, line := range strings.Split(ascii, "\n") {
|
||||
for i := len(line); i < maxWidth+5; i++ {
|
||||
line = line + " "
|
||||
}
|
||||
if lineIndex < len(strings.Split(string(out), "\n")) {
|
||||
line = line + strings.Split(string(out), "\n")[lineIndex]
|
||||
}
|
||||
fmt.Println(line)
|
||||
}
|
||||
}
|
||||
|
||||
func readKeyValueFile(filepath string) (map[string]string, error) {
|
||||
ret := make(map[string]string)
|
||||
if _, err := os.Stat(filepath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bytes, err := os.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
str := string(bytes)
|
||||
lines := strings.Split(str, "\n")
|
||||
for _, line := range lines {
|
||||
if len(strings.Split(line, "=")) >= 2 {
|
||||
key := strings.SplitN(line, "=", 2)[0]
|
||||
value := strings.SplitN(line, "=", 2)[1]
|
||||
if strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"") {
|
||||
value = value[1 : len(value)-1]
|
||||
}
|
||||
ret[key] = value
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getDistroInfo() DistroInfo {
|
||||
distroID := ""
|
||||
var releaseMap = make(map[string]string)
|
||||
if _, err := os.Stat("/etc/os-release"); err == nil {
|
||||
releaseMap, err = readKeyValueFile("/etc/os-release")
|
||||
if err != nil {
|
||||
return DistroInfo{
|
||||
ID: "unknown",
|
||||
LongName: "Unknown",
|
||||
ShortName: "Unknown",
|
||||
}
|
||||
}
|
||||
if value, ok := releaseMap["ID"]; ok {
|
||||
distroID = value
|
||||
}
|
||||
}
|
||||
|
||||
switch distroID {
|
||||
default:
|
||||
if id, ok := releaseMap["ID"]; ok {
|
||||
if longName, ok := releaseMap["PRETTY_NAME"]; ok {
|
||||
if shortName, ok := releaseMap["NAME"]; ok {
|
||||
return DistroInfo{
|
||||
ID: id,
|
||||
LongName: longName,
|
||||
ShortName: shortName,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return DistroInfo{
|
||||
ID: "unknown",
|
||||
LongName: "Unknown",
|
||||
ShortName: "Unknown",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getDistroAscii() string {
|
||||
defaultAscii :=
|
||||
` .--.
|
||||
|o_o |
|
||||
|:_/ |
|
||||
// \ \
|
||||
(| | )
|
||||
/'\_ _/'\
|
||||
\___)=(___/ `
|
||||
var id string
|
||||
if config.Ascii == "auto" {
|
||||
id = getDistroInfo().ID
|
||||
} else {
|
||||
id = config.Ascii
|
||||
}
|
||||
userConfDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
if _, err := os.Stat(path.Join("/etc/stormfetch/ascii/", id)); err == nil {
|
||||
bytes, err := os.ReadFile(path.Join("/etc/stormfetch/ascii/", id))
|
||||
if err != nil {
|
||||
return defaultAscii
|
||||
}
|
||||
return string(bytes)
|
||||
} else {
|
||||
return defaultAscii
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(path.Join(userConfDir, "stormfetch/ascii/", id)); err == nil {
|
||||
bytes, err := os.ReadFile(path.Join(userConfDir, "stormfetch/ascii/", id))
|
||||
if err != nil {
|
||||
return defaultAscii
|
||||
}
|
||||
return string(bytes)
|
||||
} else if _, err := os.Stat(path.Join("/etc/stormfetch/ascii/", id)); err == nil {
|
||||
bytes, err := os.ReadFile(path.Join("/etc/stormfetch/ascii/", id))
|
||||
if err != nil {
|
||||
return defaultAscii
|
||||
}
|
||||
return string(bytes)
|
||||
} else {
|
||||
return defaultAscii
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 MiB |
@@ -0,0 +1,5 @@
|
||||
module stormfetch-monitor-detection
|
||||
|
||||
go 1.24.0
|
||||
|
||||
require github.com/go-gl/glfw/v3.3/glfw v0.0.0-20260406072232-3ac4aa2bb164
|
||||
@@ -0,0 +1,2 @@
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20260406072232-3ac4aa2bb164 h1:ddtotcEXIT7dFhSeXIWjK8YMFYl5bB2NdYQwgv6Uqjk=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20260406072232-3ac4aa2bb164/go.mod h1:SyRD8YfuKk+ZXlDqYiqe1qMSqjNgtHzBTG810KUagMc=
|
||||
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-gl/glfw/v3.3/glfw"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
err := glfw.Init()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, monitor := range glfw.GetMonitors() {
|
||||
mode := monitor.GetVideoMode()
|
||||
|
||||
fmt.Print(monitor.GetName() + ",")
|
||||
fmt.Print(strconv.Itoa(mode.Width) + ",")
|
||||
fmt.Print(strconv.Itoa(mode.Height) + ",")
|
||||
fmt.Print(strconv.Itoa(mode.RefreshRate))
|
||||
fmt.Println()
|
||||
}
|
||||
defer glfw.Terminate()
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func setupColorMap(asciiArtHeader string) []string {
|
||||
colorMap := make([]string, 10)
|
||||
|
||||
// Set default color map values
|
||||
for i := range 10 {
|
||||
colorMap[i] = "\033[0m"
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
module stormfetch
|
||||
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/jackmordaunt/ghw v1.0.5
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/jackmordaunt/pcidb v1.0.1 // indirect
|
||||
github.com/jackmordaunt/wmi v1.2.4 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
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.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/go.mod h1:OMmhrZOZVu2hYXhBDZXddypxwKR/dp4DbIgzCkQDxdQ=
|
||||
github.com/jackmordaunt/wmi v1.2.4 h1:/XyuMiKby0qXNQp1j0uU0JqTWLM0QGOVok1Hf5Yagtg=
|
||||
github.com/jackmordaunt/wmi v1.2.4/go.mod h1:K/+FH4467Vk2fJXhYQTZzhctkCMMR7PwHqKbnizFySo=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
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/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
golang.org/x/sys v0.1.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 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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -0,0 +1,251 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jackmordaunt/ghw"
|
||||
)
|
||||
|
||||
type CPU struct {
|
||||
Model string
|
||||
Cores int
|
||||
Threads int
|
||||
}
|
||||
|
||||
type GPU struct {
|
||||
PCIAddress string
|
||||
Vendor string
|
||||
Name string
|
||||
Product string
|
||||
Subsystem string
|
||||
Driver string
|
||||
VramTotal string
|
||||
VramUsed string
|
||||
}
|
||||
|
||||
type Monitor struct {
|
||||
Name string
|
||||
Width int
|
||||
Height int
|
||||
RefreshRate int
|
||||
}
|
||||
|
||||
func GetCPUs(hiddenCPUs []int) []CPU {
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Check if device info is set
|
||||
if gpu.DeviceInfo == nil {
|
||||
ret = append(ret, GPU{
|
||||
PCIAddress: gpu.Address,
|
||||
Vendor: "Unknown",
|
||||
Name: "GPU",
|
||||
Product: "Unknown",
|
||||
Subsystem: "Unknown",
|
||||
Driver: "Unknown",
|
||||
VramTotal: "Unknown",
|
||||
VramUsed: "Unknown",
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func GetMotherboardModel() string {
|
||||
bytes, err := os.ReadFile("/sys/devices/virtual/dmi/id/board_name")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Remove duplicate whitespaces
|
||||
ret := strings.Join(strings.Fields(string(bytes)), " ")
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func GetMonitors() []Monitor {
|
||||
var monitors []Monitor
|
||||
|
||||
if GetDisplayProtocol() != "" {
|
||||
// Find stormfetch-monitor-detection program
|
||||
execPath, _ := os.Executable()
|
||||
possiblePaths := []string{
|
||||
path.Join(path.Dir(execPath), "stormfetch-monitor-detection"),
|
||||
path.Join(LibexecDir, "stormfetch-monitor-detection"),
|
||||
}
|
||||
|
||||
monitorDetectionPath := ""
|
||||
for _, path := range possiblePaths {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
monitorDetectionPath = path
|
||||
break
|
||||
}
|
||||
}
|
||||
if monitorDetectionPath == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get monitor detection program output
|
||||
output, err := exec.Command(monitorDetectionPath).Output()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse output
|
||||
for line := range strings.SplitSeq(strings.TrimSpace(string(output)), "\n") {
|
||||
lineSplit := strings.Split(line, ",")
|
||||
if len(lineSplit) != 4 {
|
||||
continue
|
||||
}
|
||||
|
||||
name := lineSplit[0]
|
||||
width, err := strconv.Atoi(lineSplit[1])
|
||||
height, err := strconv.Atoi(lineSplit[2])
|
||||
refreshRate, err := strconv.Atoi(lineSplit[3])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
monitors = append(monitors, Monitor{
|
||||
Name: name,
|
||||
Width: width,
|
||||
Height: height,
|
||||
RefreshRate: refreshRate,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return monitors
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Build-time variables
|
||||
var StormfetchVersion = "dev"
|
||||
var SystemConfigDir = "/etc/"
|
||||
var LibexecDir = "/libexec/"
|
||||
|
||||
// Flag variables
|
||||
var ShowVersion = false
|
||||
var ConfigPath = ""
|
||||
var Ascii = ""
|
||||
var ShowModuleTimeTaken = false
|
||||
|
||||
func main() {
|
||||
parseFlags()
|
||||
readConfig()
|
||||
initializeModuleMap()
|
||||
run()
|
||||
}
|
||||
|
||||
func parseFlags() {
|
||||
flag.BoolVar(&ShowVersion, "version", false, "Show Stormfetch version")
|
||||
flag.StringVar(&ConfigPath, "config", "", "Use the specified config file")
|
||||
flag.BoolVar(&ShowModuleTimeTaken, "time-taken", false, "Show time taken to execute each module")
|
||||
flag.StringVar(&Ascii, "ascii", "", "Set distro ascii")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func run() {
|
||||
if ShowVersion {
|
||||
fmt.Printf("Stormfetch version %s\n", StormfetchVersion)
|
||||
return
|
||||
}
|
||||
|
||||
// Fetch ascii art and remove header
|
||||
asciiArt := GetDistroAsciiArt()
|
||||
asciiArtHeader := ""
|
||||
if strings.HasPrefix(asciiArt, "#/") {
|
||||
asciiArtHeader = strings.SplitN(asciiArt, "\n", 2)[0]
|
||||
asciiArt = strings.SplitN(asciiArt, "\n", 2)[1]
|
||||
}
|
||||
asciiArtNoColor := asciiArt
|
||||
|
||||
// Rewrite last color code to the start of the next line
|
||||
lastColor := "0"
|
||||
for i := 1; i < len(asciiArt); i++ {
|
||||
rune := rune(asciiArt[i])
|
||||
|
||||
if unicode.IsDigit(rune) && asciiArt[i-1] == '%' {
|
||||
lastColor = string(rune)
|
||||
}
|
||||
|
||||
if rune == '\n' {
|
||||
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 {
|
||||
asciiArt = strings.ReplaceAll(asciiArt, "%"+strconv.Itoa(key), value)
|
||||
asciiArtNoColor = strings.ReplaceAll(asciiArtNoColor, "%"+strconv.Itoa(key), "")
|
||||
}
|
||||
|
||||
// Execute modules in order
|
||||
modulesText := make([]string, 0)
|
||||
for _, moduleConfig := range config.Modules {
|
||||
module, ok := Modules[moduleConfig.Name]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// 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
|
||||
for _, line := range strings.Split(asciiArtNoColor, "\n") {
|
||||
if len(line) > maxWidth {
|
||||
maxWidth = len(line)
|
||||
}
|
||||
}
|
||||
|
||||
// Split ascii art into each line
|
||||
asciiArtSplit := strings.Split(asciiArt, "\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])
|
||||
}
|
||||
|
||||
// Write blank space between ascii art and module text
|
||||
for i := currentLineLength; i < maxWidth+3; i++ {
|
||||
final.WriteString(" ")
|
||||
}
|
||||
|
||||
// Write module text
|
||||
if i < len(modulesText) {
|
||||
final.WriteString(modulesText[i])
|
||||
}
|
||||
|
||||
final.WriteString("\n")
|
||||
}
|
||||
|
||||
fmt.Println(strings.TrimRight(final.String(), "\n") + "\033[0m")
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Memory struct {
|
||||
MemTotal int
|
||||
MemFree int
|
||||
MemAvailable int
|
||||
}
|
||||
|
||||
func GetMemoryInfo() *Memory {
|
||||
toInt := func(raw string) int {
|
||||
if raw == "" {
|
||||
return 0
|
||||
}
|
||||
res, err := strconv.Atoi(raw)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
parseLine := func(raw string) (key string, value int) {
|
||||
text := strings.ReplaceAll(raw[:len(raw)-2], " ", "")
|
||||
keyValue := strings.Split(text, ":")
|
||||
return keyValue[0], toInt(keyValue[1])
|
||||
}
|
||||
|
||||
if _, err := os.Stat("/proc/meminfo"); err != nil {
|
||||
return nil
|
||||
}
|
||||
file, err := os.Open("/proc/meminfo")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
bufio.NewScanner(file)
|
||||
scanner := bufio.NewScanner(file)
|
||||
res := Memory{}
|
||||
for scanner.Scan() {
|
||||
key, value := parseLine(scanner.Text())
|
||||
switch key {
|
||||
case "MemTotal":
|
||||
res.MemTotal = value / 1024
|
||||
case "MemFree":
|
||||
res.MemFree = value / 1024
|
||||
case "MemAvailable":
|
||||
res.MemAvailable = value / 1024
|
||||
}
|
||||
}
|
||||
return &res
|
||||
}
|
||||
@@ -0,0 +1,467 @@
|
||||
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} ($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 "DISPLAY_PROTOCOL":
|
||||
return displayProtocol
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
})
|
||||
}}
|
||||
RegisterModule(dewmModule)
|
||||
|
||||
// Monitors module
|
||||
monitorsModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "monitors", Format: "%3Monitor ${MONITOR_NAME}: %4${MONITOR_WIDTH}x${MONITOR_HEIGHT} ${MONITOR_REFRESH_RATE}Hz"}, Execute: func(sm StormfetchModule) string {
|
||||
builder := strings.Builder{}
|
||||
monitors := GetMonitors()
|
||||
|
||||
for _, monitor := range monitors {
|
||||
expanded := os.Expand(sm.Format, func(s string) string {
|
||||
switch s {
|
||||
case "MONITOR_NAME":
|
||||
return monitor.Name
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import "net"
|
||||
|
||||
func GetLocalIP() string {
|
||||
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||
|
||||
return localAddr.IP.String()
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type partition struct {
|
||||
Device string
|
||||
MountPoint string
|
||||
Label string
|
||||
FileystemType string
|
||||
TotalSize uint64
|
||||
UsedSize uint64
|
||||
FreeSize uint64
|
||||
}
|
||||
|
||||
func GetMountedPartitions(hiddenPartitions, hiddenFilesystems []string) []partition {
|
||||
// Get all filesystem and partition labels
|
||||
fslabels, err := os.ReadDir("/dev/disk/by-label")
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
partlabels, err := os.ReadDir("/dev/disk/by-partlabel")
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
labels := make(map[string]string)
|
||||
for _, entry := range partlabels {
|
||||
link, err := filepath.EvalSymlinks(filepath.Join("/dev/disk/by-partlabel/", entry.Name()))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
labels[link] = entry.Name()
|
||||
}
|
||||
for _, entry := range fslabels {
|
||||
link, err := filepath.EvalSymlinks(filepath.Join("/dev/disk/by-label/", entry.Name()))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
labels[link] = entry.Name()
|
||||
}
|
||||
|
||||
// Get all mounted partitions
|
||||
file, err := os.ReadFile("/proc/mounts")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var partitions []partition
|
||||
for _, entry := range strings.Split(string(file), "\n") {
|
||||
fields := strings.Fields(entry)
|
||||
if entry == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip virtual partitions not under /dev
|
||||
if !strings.HasPrefix(fields[0], "/dev") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip partition if explicitly hidden
|
||||
if slices.Contains(hiddenPartitions, fields[0]) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip filesystem if explicitely hidden
|
||||
if slices.Contains(hiddenFilesystems, fields[2]) {
|
||||
continue
|
||||
}
|
||||
|
||||
device, err := filepath.EvalSymlinks(fields[0])
|
||||
if err != nil {
|
||||
device = fields[0]
|
||||
}
|
||||
|
||||
p := partition{
|
||||
Device: device,
|
||||
MountPoint: fields[1],
|
||||
Label: "",
|
||||
FileystemType: fields[2],
|
||||
TotalSize: 0,
|
||||
UsedSize: 0,
|
||||
FreeSize: 0,
|
||||
}
|
||||
|
||||
// Skip already added partitions
|
||||
skip := false
|
||||
for _, part := range partitions {
|
||||
if part.Device == p.Device {
|
||||
skip = true
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
continue
|
||||
}
|
||||
|
||||
// Set partition label if available
|
||||
if value, ok := labels[p.Device]; ok {
|
||||
p.Label = strings.ReplaceAll(value, "\\x20", " ")
|
||||
}
|
||||
|
||||
// Get partition total, used and free space
|
||||
buf := new(syscall.Statfs_t)
|
||||
err = syscall.Statfs(p.MountPoint, buf)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
totalBlocks := buf.Blocks
|
||||
freeBlocks := buf.Bfree
|
||||
usedBlocks := totalBlocks - freeBlocks
|
||||
blockSize := uint64(buf.Bsize)
|
||||
|
||||
p.TotalSize = totalBlocks * blockSize
|
||||
p.FreeSize = freeBlocks * blockSize
|
||||
p.UsedSize = usedBlocks * blockSize
|
||||
|
||||
partitions = append(partitions, p)
|
||||
}
|
||||
return partitions
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PackageManager struct {
|
||||
Name string
|
||||
ExecutableName string
|
||||
GetPackages func(...any) int
|
||||
FunctionInput []any
|
||||
}
|
||||
|
||||
var PackageManagers = []PackageManager{
|
||||
{Name: "dpkg", ExecutableName: "dpkg", GetPackages: pmFileLines, FunctionInput: []any{"/var/lib/dpkg/status", "Status: install ok installed"}},
|
||||
{Name: "pacman", ExecutableName: "pacman", GetPackages: pmDirectoryElements, FunctionInput: []any{"/var/lib/pacman/local/", true}},
|
||||
{Name: "rpm", ExecutableName: "rpm", GetPackages: pmShellCommandLines, FunctionInput: []any{"rpm -qa"}},
|
||||
{Name: "xbps", ExecutableName: "xbps-query", GetPackages: pmFileLines, FunctionInput: []any{"/var/db/xbps/pkgdb-0.38.plist", "<string>installed</string>"}},
|
||||
{Name: "bpm", ExecutableName: "bpm", GetPackages: pmDirectoryElements, FunctionInput: []any{"/var/lib/bpm/installed/"}},
|
||||
{Name: "portage", ExecutableName: "emerge", GetPackages: pmPortage},
|
||||
{Name: "flatpak", ExecutableName: "flatpak", GetPackages: pmFlatpak},
|
||||
{Name: "snap", ExecutableName: "snap", GetPackages: pmSnap},
|
||||
}
|
||||
|
||||
func (pm *PackageManager) CountPackages() int {
|
||||
// Return 0 if package manager is not found
|
||||
if _, err := exec.LookPath(pm.ExecutableName); err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return pm.GetPackages(pm.FunctionInput...)
|
||||
}
|
||||
|
||||
func GetInstalledPackages() (ret string) {
|
||||
for _, pm := range PackageManagers {
|
||||
count := pm.CountPackages()
|
||||
if count > 0 {
|
||||
if ret == "" {
|
||||
ret += fmt.Sprintf("%d (%s)", count, pm.Name)
|
||||
} else {
|
||||
ret += fmt.Sprintf(" %d (%s)", count, pm.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/mitchellh/go-ps"
|
||||
)
|
||||
|
||||
type DistroInfo struct {
|
||||
ID string
|
||||
LongName string
|
||||
ShortName string
|
||||
}
|
||||
|
||||
func GetDistroInfo() DistroInfo {
|
||||
info := DistroInfo{
|
||||
ID: "unknown",
|
||||
LongName: "Unknown",
|
||||
ShortName: "Unknown",
|
||||
}
|
||||
|
||||
// Detect release file location
|
||||
var releaseFile string
|
||||
if _, err := os.Stat("/bedrock/etc/os-release"); os.Getenv("BEDROCK_RESTRICT") == "" && err == nil {
|
||||
// Using Bedrock Linux
|
||||
releaseFile = "/bedrock/etc/os-release"
|
||||
} else if _, err := os.Stat("/etc/os-release"); err == nil {
|
||||
// Using a regular linux distribution
|
||||
releaseFile = "/etc/os-release"
|
||||
} else {
|
||||
return info
|
||||
}
|
||||
|
||||
// Read release file
|
||||
releaseMap, err := ReadKeyValueFile(releaseFile)
|
||||
if err != nil {
|
||||
return info
|
||||
}
|
||||
|
||||
// Set struct fields
|
||||
if id, ok := releaseMap["ID"]; ok {
|
||||
info.ID = id
|
||||
}
|
||||
if longName, ok := releaseMap["PRETTY_NAME"]; ok {
|
||||
info.LongName = longName
|
||||
}
|
||||
if shortName, ok := releaseMap["NAME"]; ok {
|
||||
info.ShortName = shortName
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
func GetDistroAsciiArt() string {
|
||||
defaultAscii :=
|
||||
` .--.
|
||||
|o_o |
|
||||
|:_/ |
|
||||
// \ \
|
||||
(| | )
|
||||
/'\_ _/'\
|
||||
\___)=(___/`
|
||||
|
||||
// Get ascii name to use
|
||||
var asciiName string
|
||||
if Ascii != "" {
|
||||
asciiName = Ascii
|
||||
} else if config.Ascii == "auto" {
|
||||
asciiName = GetDistroInfo().ID
|
||||
} else {
|
||||
asciiName = config.Ascii
|
||||
}
|
||||
|
||||
// Check for ascii art in home directory
|
||||
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 {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
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 {
|
||||
return "unknown", "unknown"
|
||||
}
|
||||
|
||||
var kernelNameByteString [65]byte
|
||||
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])
|
||||
}
|
||||
|
||||
return string(kernelNameByteString[:kernelNameLength]), string(kernelReleaseByteString[:kernelReleaseLength])
|
||||
}
|
||||
|
||||
func GetInitSystem() string {
|
||||
process, err := ps.FindProcess(1)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Return if init system can't be found
|
||||
if process == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Special cases
|
||||
// OpenRC check
|
||||
if _, err := os.Stat("/usr/sbin/openrc"); err == nil {
|
||||
openrcVersion := runCommand("openrc --version | awk '{print $3}'", "/bin/sh")
|
||||
if openrcVersion != "" {
|
||||
return "OpenRC " + openrcVersion
|
||||
} else {
|
||||
return "OpenRC"
|
||||
}
|
||||
}
|
||||
|
||||
// Default PID 1 process name checking
|
||||
initName := process.Executable()
|
||||
initVersion := ""
|
||||
switch initName {
|
||||
case "systemd":
|
||||
initName = "Systemd"
|
||||
initVersion = runCommand("systemctl --version | head -n1 | awk '{print $2}'", "/bin/sh")
|
||||
case "runit":
|
||||
initName = "Runit"
|
||||
case "dinit":
|
||||
initName = "Dinit"
|
||||
initVersion = runCommand("dinit --version | head -n1 | awk '{print substr($3, 1, length($3)-1)}'", "/bin/sh")
|
||||
case "enit":
|
||||
initName = "Enit"
|
||||
initVersion = runCommand("enit --version | awk '{print $3}'", "/bin/sh")
|
||||
}
|
||||
|
||||
if initVersion != "" {
|
||||
return initName + " " + initVersion
|
||||
} else {
|
||||
return initName
|
||||
}
|
||||
}
|
||||
|
||||
func GetLibc() string {
|
||||
checkLibcOutput, err := exec.Command("ldd", "/usr/bin/ls").Output()
|
||||
if err != nil {
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
if strings.Contains(string(checkLibcOutput), "ld-musl") {
|
||||
// Using Musl Libc
|
||||
output, _ := exec.Command("ldd").CombinedOutput()
|
||||
return "Musl " + strings.TrimPrefix(strings.Split(strings.TrimSpace(string(output)), "\n")[1], "Version ")
|
||||
} else {
|
||||
// Using Glibc
|
||||
cmd := exec.Command("ldd", "--version")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "Glibc"
|
||||
}
|
||||
outputSplit := strings.Split(strings.Split(strings.TrimSpace(string(output)), "\n")[0], " ")
|
||||
ver := outputSplit[len(outputSplit)-1]
|
||||
return "Glibc " + ver
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/go-ps"
|
||||
)
|
||||
|
||||
type DEWM struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
func GetShell() string {
|
||||
file, err := os.ReadFile("/etc/passwd")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
str := string(file)
|
||||
shell := ""
|
||||
|
||||
for _, line := range strings.Split(str, "\n") {
|
||||
if strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
userInfo := strings.Split(line, ":")
|
||||
if userInfo[2] == strconv.Itoa(os.Getuid()) {
|
||||
shell = userInfo[6]
|
||||
}
|
||||
}
|
||||
shellName := "Unknown"
|
||||
shellVersion := ""
|
||||
switch filepath.Base(shell) {
|
||||
case "bash":
|
||||
shellName = "Bash"
|
||||
shellVersion = runCommand("$SHELL --version | head -n1 | awk '{print $4}'", "/bin/sh")
|
||||
case "zsh":
|
||||
shellName = "Zsh"
|
||||
shellVersion = runCommand("$SHELL --version | awk '{print $2}'", "/bin/sh")
|
||||
case "fish":
|
||||
shellName = "Fish"
|
||||
shellVersion = runCommand("$SHELL --version | awk '{print $3}'", "/bin/sh")
|
||||
case "nu":
|
||||
shellName = "Nushell"
|
||||
shellVersion = runCommand("$SHELL --version", "/bin/sh")
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
if shellVersion != "" {
|
||||
return shellName + " " + shellVersion
|
||||
} else {
|
||||
return shellName
|
||||
}
|
||||
}
|
||||
|
||||
func GetDEWM() DEWM {
|
||||
processes, err := ps.Processes()
|
||||
if err != nil {
|
||||
log.Fatalf("Error: could not get processes: %s", err)
|
||||
}
|
||||
var executables []string
|
||||
for _, process := range processes {
|
||||
executables = append(executables, process.Executable())
|
||||
}
|
||||
|
||||
processExists := func(process string) bool {
|
||||
return slices.Contains(executables, process)
|
||||
}
|
||||
if processExists("plasmashell") {
|
||||
dewm := DEWM{
|
||||
Name: "KDE Plasma",
|
||||
Type: "DE",
|
||||
}
|
||||
|
||||
if version := runCommand("plasmashell --version | awk '{print $2}'", "/bin/sh"); version != "" {
|
||||
dewm.Name += " " + version
|
||||
}
|
||||
|
||||
return dewm
|
||||
} else if processExists("gnome-session") {
|
||||
dewm := DEWM{
|
||||
Name: "Gnome",
|
||||
Type: "DE",
|
||||
}
|
||||
|
||||
if version := runCommand("gnome-shell --version | awk '{print $3}'", "/bin/sh"); version != "" {
|
||||
dewm.Name += " " + version
|
||||
}
|
||||
|
||||
return dewm
|
||||
} else if processExists("xfce4-session") {
|
||||
dewm := DEWM{
|
||||
Name: "XFCE",
|
||||
Type: "DE",
|
||||
}
|
||||
|
||||
if version := runCommand("xfce4-session --version | head -n1 | awk '{print $2}'", "/bin/sh"); version != "" {
|
||||
dewm.Name += " " + version
|
||||
}
|
||||
|
||||
return dewm
|
||||
} else if processExists("cinnamon") {
|
||||
dewm := DEWM{
|
||||
Name: "Cinnamon",
|
||||
Type: "DE",
|
||||
}
|
||||
|
||||
if version := runCommand("cinnamon --version | awk '{print $3}'", "/bin/sh"); version != "" {
|
||||
dewm.Name += " " + version
|
||||
}
|
||||
|
||||
return dewm
|
||||
} else if processExists("mate-panel") {
|
||||
dewm := DEWM{
|
||||
Name: "MATE",
|
||||
Type: "DE",
|
||||
}
|
||||
|
||||
if version := runCommand("mate-about --version | awk '{print $4}'", "/bin/sh"); version != "" {
|
||||
dewm.Name += " " + version
|
||||
}
|
||||
|
||||
return dewm
|
||||
} else if processExists("lxsession") {
|
||||
dewm := DEWM{
|
||||
Name: "LXDE",
|
||||
Type: "DE",
|
||||
}
|
||||
|
||||
return dewm
|
||||
} else if processExists("lxqt-session") {
|
||||
dewm := DEWM{
|
||||
Name: "LXQt",
|
||||
Type: "DE",
|
||||
}
|
||||
|
||||
if version := runCommand("lxqt-session --version | head -n1 | awk '{print $2}'", "/bin/sh"); version != "" {
|
||||
dewm.Name += " " + version
|
||||
}
|
||||
|
||||
return dewm
|
||||
} else if processExists("i3") || processExists("i3-with-shmlog") {
|
||||
dewm := DEWM{
|
||||
Name: "i3",
|
||||
Type: "WM",
|
||||
}
|
||||
|
||||
if version := runCommand("i3 --version | awk '{print $3}'", "/bin/sh"); version != "" {
|
||||
dewm.Name += " " + version
|
||||
}
|
||||
|
||||
return dewm
|
||||
} else if processExists("sway") {
|
||||
dewm := DEWM{
|
||||
Name: "Sway",
|
||||
Type: "WM",
|
||||
}
|
||||
|
||||
if runCommand("sway --version | awk '{print $1}'", "/bin/sh") == "swayfx" {
|
||||
dewm.Name = "SwayFX"
|
||||
} else {
|
||||
dewm.Name = "Sway"
|
||||
}
|
||||
|
||||
if version := runCommand("sway --version | awk '{print $3}'", "/bin/sh"); version != "" {
|
||||
dewm.Name += " " + version
|
||||
}
|
||||
|
||||
return dewm
|
||||
} else if processExists("bspwm") {
|
||||
dewm := DEWM{
|
||||
Name: "Bspwm",
|
||||
Type: "WM",
|
||||
}
|
||||
|
||||
if version := runCommand("bspwm -v", "/bin/sh"); version != "" {
|
||||
dewm.Name += " " + version
|
||||
}
|
||||
|
||||
return dewm
|
||||
} else if processExists("Hyprland") {
|
||||
dewm := DEWM{
|
||||
Name: "Hyprland",
|
||||
Type: "WM",
|
||||
}
|
||||
|
||||
if version := runCommand("hyprctl version | sed -n 3p | awk '{print $2}' | tr -d 'v,'", "/bin/sh"); version != "" {
|
||||
dewm.Name += " " + version
|
||||
}
|
||||
|
||||
return dewm
|
||||
} else if processExists("icewm-session") {
|
||||
dewm := DEWM{
|
||||
Name: "IceWM",
|
||||
Type: "WM",
|
||||
}
|
||||
|
||||
if version := runCommand("icewm --version | awk '{print $2}'", "/bin/sh"); version != "" {
|
||||
dewm.Name += " " + version
|
||||
}
|
||||
|
||||
return dewm
|
||||
}
|
||||
return DEWM{
|
||||
Name: "Unknown",
|
||||
Type: "Unknown",
|
||||
}
|
||||
}
|
||||
|
||||
func GetDisplayProtocol() string {
|
||||
protocol := os.Getenv("XDG_SESSION_TYPE")
|
||||
if protocol == "x11" {
|
||||
return "X11"
|
||||
} else if protocol == "wayland" {
|
||||
return "Wayland"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func FormatBytes(bytes uint64) string {
|
||||
var suffixes [6]string
|
||||
suffixes[0] = "B"
|
||||
suffixes[1] = "KiB"
|
||||
suffixes[2] = "MiB"
|
||||
suffixes[3] = "GiB"
|
||||
suffixes[4] = "TiB"
|
||||
suffixes[5] = "PiB"
|
||||
|
||||
bf := float64(bytes)
|
||||
for _, unit := range suffixes {
|
||||
if math.Abs(bf) < 1024.0 {
|
||||
return fmt.Sprintf("%3.1f %s", bf, unit)
|
||||
}
|
||||
bf /= 1024.0
|
||||
}
|
||||
return fmt.Sprintf("%.1fYiB", bf)
|
||||
}
|
||||
|
||||
func StripAnsii(str string) string {
|
||||
const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
|
||||
var re = regexp.MustCompile(ansi)
|
||||
return re.ReplaceAllString(str, "")
|
||||
}
|
||||
|
||||
func ReadKeyValueFile(filepath string) (map[string]string, error) {
|
||||
ret := make(map[string]string)
|
||||
if _, err := os.Stat(filepath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bytes, err := os.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
str := string(bytes)
|
||||
lines := strings.Split(str, "\n")
|
||||
for _, line := range lines {
|
||||
if len(strings.Split(line, "=")) >= 2 {
|
||||
key := strings.SplitN(line, "=", 2)[0]
|
||||
value := strings.SplitN(line, "=", 2)[1]
|
||||
if strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"") {
|
||||
value = value[1 : len(value)-1]
|
||||
}
|
||||
ret[key] = value
|
||||
}
|
||||
}
|
||||
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))
|
||||
}
|
||||
Reference in New Issue
Block a user