summaryrefslogtreecommitdiff
path: root/scripts/vfetch
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/vfetch')
-rwxr-xr-xscripts/vfetch288
1 files changed, 288 insertions, 0 deletions
diff --git a/scripts/vfetch b/scripts/vfetch
new file mode 100755
index 0000000..e0018e3
--- /dev/null
+++ b/scripts/vfetch
@@ -0,0 +1,288 @@
+#!/usr/bin/env python3
+
+from xdg.BaseDirectory import xdg_config_home
+from enum import Enum
+import subprocess
+import platform
+import distro
+import json
+import re
+import os
+import wmctrl
+
+colors = [
+ # Regular colors.
+ "\u001b[30m",
+ "\u001b[31m",
+ "\u001b[32m",
+ "\u001b[33m",
+ "\u001b[34m",
+ "\u001b[35m",
+ "\u001b[36m",
+ "\u001b[37m",
+
+ # Bright colors.
+ "\u001b[30;1m",
+ "\u001b[31;1m",
+ "\u001b[32;1m",
+ "\u001b[33;1m",
+ "\u001b[34;1m",
+ "\u001b[35;1m",
+ "\u001b[36;1m",
+ "\u001b[37;1m",
+
+ # Reset.
+ "\u001b[0m"
+]
+
+decorations = [
+ "\u001b[1m", # Bold.
+ "\u001b[4m", # Underline.
+ "\u001b[7m" # Reversed.
+]
+
+# Creates a copy of the specified string with color and decorations added.
+def colored(string, colorIndex, decorationIndices=[]):
+ newString = colors[colorIndex]
+ for decorationIndex in decorationIndices:
+ newString += decorations[decorationIndex]
+ newString += string + colors[len(colors)-1]
+ return newString
+
+# Enum for the different data types.
+class Type(str, Enum):
+ os = 'os'
+ kernel = 'kernel'
+ wm = 'wm'
+ packages = 'packages'
+ uptime = 'uptime'
+
+# Enum for the different align modes.
+class AlignMode(str, Enum):
+ spaces = 'spaces'
+ center = 'center'
+
+# Loads the settings from the configuration file.
+# First checks for a configuration file in ~/.config/vfetch/vfetch.conf,
+# else it defaults to the configuration file in the same folder as the script.
+def loadSettings():
+ try:
+ file = open(xdg_config_home + '/vfetch/vfetch.conf', 'r')
+ except FileNotFoundError:
+ file = open(os.path.dirname(os.path.realpath(__file__)) + '/vfetch.conf', 'r')
+ content = file.read()
+ settings = json.loads(content)
+ file.close()
+ return settings
+
+# Prints string without ending with a new line.
+def printn(string):
+ print(string, end="")
+
+# Prints string at a specified position.
+def printAt(string, *position):
+ if len(position) == 1:
+ x = position[0][0]
+ y = position[0][1]
+ else:
+ x = position[0]
+ y = position[1]
+ printn("\x1b7\x1b[%d;%df%s\x1b8" % (y+1, x+1, string))
+
+# Prints the data lines.
+def printLines(lines, colorIndex, offsetX, offsetY, alignMode, alignSpace):
+ longestName = 0
+ dataPosition = 0
+
+ if alignMode is AlignMode.spaces:
+ for line in lines:
+ position = len(line[0]) + alignSpace
+ if position > dataPosition:
+ dataPosition = position
+ else:
+ # Finds the length of the longest name.
+ longestName = len(max(lines, key = lambda data: len(data[0]))[0])
+
+ y = 0
+ x = offsetX
+ # Prints the lines and formats them accordingly.
+ for line in lines:
+ if alignMode is AlignMode.spaces:
+ printAt(line[1], x + dataPosition, y+offsetY)
+ elif alignMode is AlignMode.center:
+ line[0] = ' ' * (longestName - len(line[0])) + line[0]
+
+ printAt(colored(line[0], colorIndex, [0]), x, y+offsetY)
+ if alignMode is AlignMode.center:
+ printAt(' ~ ' + line[1], x+len(line[0]), y+offsetY)
+ y += 1
+
+# Sets the cursor position.
+def setCursorPosition(*position, newLine=False):
+ if len(position) == 1:
+ x = position[0][0]
+ y = position[0][1]
+ else:
+ x = position[0]
+ y = position[1]
+ string = '\033[%d;%dH' % (y, x)
+ if newLine:
+ print(string)
+ else:
+ printn(string)
+
+# Runs the specified terminal command.
+def termRun(command, arguments):
+ output = subprocess.run([command, arguments], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ return output.stdout
+
+# Prints ascii image.
+def printAscii(position, asciiImage):
+ setCursorPosition(position)
+ lines = asciiImage.split('\n')
+ for line in lines:
+ print(line)
+
+# Gets the operating system.
+def getOS(architecture=False, removeLinux=False):
+ os = distro.linux_distribution()[0]
+ if removeLinux:
+ os = re.sub('linux', '', os, flags=re.IGNORECASE)
+ os = os.rstrip()
+ if architecture:
+ os += ' ' + platform.machine()
+ return os
+
+# Gets the kernel.
+def getKernel(fullName=True):
+ kernel = platform.release()
+ if not fullName:
+ kernel = kernel.split('-')[0]
+ return kernel
+
+# Gets the window manager.
+def getWM():
+ try:
+ return wmctrl.os.environ.get('DESKTOP_SESSION')
+ except:
+ pass
+ try:
+ return wmctrl.os.environ.get('XDG_SESSION_DESKTOP')
+ except:
+ return None
+
+# Gets the number of packages.
+def getPackages(displayPackageManager=False):
+ try:
+ packages = termRun('pacman', '-Qq')
+ string = str(len(packages.split('\n')))
+ if displayPackageManager:
+ string += ' (pacman)'
+ return string
+ except:
+ return None
+
+# Gets the machine uptime.
+def getUptime():
+ with open('/proc/uptime', 'r') as f:
+ uptime_seconds = float(f.readline().split()[0])
+ hours = uptime_seconds / 3600
+ minutes = (hours - int(hours)) * 60
+ hours = int(hours)
+ minutes = int(minutes)
+ string = ''
+ if hours != 0:
+ string += str(hours) + 'h '
+ if minutes != 0 or hours == 0:
+ string += str(minutes) + 'm'
+ return string
+
+# Gets the data for the specified data type.
+def getData(type, settings):
+ data = {
+ Type.os: getOS(settings['displayArchitecture'], settings['removeLinux']),
+ Type.kernel: getKernel(settings['kernelFullName']),
+ Type.wm: getWM(),
+ Type.packages: getPackages(settings['displayPackageManager']),
+ Type.uptime: getUptime()
+ }.get(type, None)
+
+ if data is None:
+ return None
+
+ name = {
+ Type.os: [ 'OS', '' ],
+ Type.kernel: [ 'Kernel', '' ],
+ Type.wm: [ 'WM', '缾' ],
+ Type.packages: [ 'Packages', '' ],
+ Type.uptime: [ 'Uptime', '' ]
+ }.get(type, None)[int(settings['iconMode'])]
+
+ if settings['lowercase']:
+ name = name.lower()
+ data = data.lower()
+
+ return [name, data]
+
+# Gets the size of the specified ascii image.
+def asciiSize(asciiImage):
+ x = 0
+ split = asciiImage.split('\n')
+ for line in split:
+ if len(line) > x:
+ x = len(line)
+ return [x, len(split)]
+
+# Trims the specified ascii image of empty lines and trailing whitespaces.
+def trimAscii(asciiImage):
+ lines = asciiImage.split('\n')
+ string = ''
+ for line in lines:
+ trimmedString = line.rstrip()
+ if len(trimmedString) != 0:
+ string += trimmedString + '\n'
+ string = string[:-1] # Removes last newline.
+ return string
+
+# Loads the ascii image at the specified path.
+def loadAsciiImage(path):
+ file = open(path, 'r')
+ asciiImage = trimAscii(file.read())
+ file.close()
+ return asciiImage
+
+settings = loadSettings()
+
+displayAscii = settings['displayAscii']
+offset = settings['offset']
+
+# Loads the data lines. If the data is invalid (None) it does not get added.
+lines = []
+for dataType in settings['data']:
+ data = getData(dataType, settings)
+ if data is not None:
+ lines.append(data)
+
+# Loads the ascii image if the option is set for it.
+if displayAscii:
+ asciiImage = loadAsciiImage(settings['asciiImage'])
+ size = asciiSize(asciiImage)
+ offset[0] += size[0]
+ finalPosition = [0, size[1]]
+else:
+ finalPosition = [0, len(lines)+offset[1]]
+
+# Makes the prompt after the script finishes have a blank line before it.
+finalPosition[1] += 1
+
+os.system('clear')
+
+if displayAscii:
+ printAscii([0,0], asciiImage)
+
+alignMode = AlignMode(settings['alignMode'])
+
+printLines(lines, settings['colorIndex'], offset[0], offset[1], alignMode, settings['alignSpace'])
+
+# Sets the final cursor position for the prompt to end up at.
+setCursorPosition(finalPosition, newLine=True)