inital commit

This commit is contained in:
ClovertaTheTrilobita 2026-01-11 19:21:27 +00:00
commit c3002d4d11
77 changed files with 6397 additions and 0 deletions

View file

@ -0,0 +1,213 @@
## Configuration file for CAVA. Default values are commented out. Use either ';' or '#' for commenting.
[general]
bars = 10
framerate = 240
# Smoothing mode. Can be 'normal', 'scientific' or 'waves'. DEPRECATED as of 0.6.0
; mode = scientific
# Accepts only non-negative values.
; framerate = 30
# 'autosens' will attempt to decrease sensitivity if the bars peak. 1 = on, 0 = active
# new as of 0.6.0 autosens of low values (dynamic range)
# 'overshoot' allows bars to overshoot (in % of terminal height) without initiating autosens. DEPRECATED as of 0.6.0
; autosens = 1
; overshoot = 20
# Manual sensitivity in %. If autosens is enabled, this will only be the initial value.
# 200 means double height. Accepts only non-negative values.
; sensitivity = 120
# The number of bars (0-200). 0 sets it to auto (fill up console).
# Bars' width and space between bars in number of characters.
; bars = 10
; bar_width = 1
; bar_spacing = 100
# bar_height is only used for output in "noritake" format
; bar_height = 32
# For SDL width and space between bars is in pixels, defaults are:
; bar_width = 20
; bar_spacing = 5
# Lower and higher cutactive frequencies for lowest and highest bars
# the bandwidth of the visualizer.
# Note: there is a minimum total bandwidth of 43Mhz x number of bars.
# Cava will automatically increase the higher cutactive if a too low band is specified.
; lower_cutactive_freq = 50
; higher_cutactive_freq = 10000
# Seconds with no input before cava goes to sleep mode. Cava will not perform FFT or drawing and
# only check for input once per second. Cava will wake up once input is detected. 0 = disable.
sleep_timer = 10
[input]
# Audio capturing method. Possible methods are: 'pulse', 'alsa', 'fifo', 'sndio' or 'shmem'
# Defaults to 'pulse', 'alsa' or 'fifo', in that order, dependent on what support cava was built with.
#
# All input methods uses the same config variable 'source'
# to define where it should get the audio.
#
# For pulseaudio 'source' will be the source. Default: 'auto', which uses the monitor source of the default sink
# (all pulseaudio sinks(outputs) have 'monitor' sources(inputs) associated with them).
#
# For alsa 'source' will be the capture device.
# For fifo 'source' will be the path to fifo-file.
# For shmem 'source' will be /squeezelite-AA:BB:CC:DD:EE:FF where 'AA:BB:CC:DD:EE:FF' will be squeezelite's MAC address
; method = pulse
; source = auto
; method = alsa
; source = hw:Loopback,1
; method = fifo
; source = /tmp/mpd.fifo
; sample_rate = 44100
; sample_bits = 16
; method = shmem
; source = /squeezelite-AA:BB:CC:DD:EE:FF
; method = portaudio
; source = auto
[output]
# Output method. Can be 'ncurses', 'noncurses', 'raw', 'noritake' or 'sdl'.
# 'noncurses' uses a custom framebuffer technique and prints only changes
# from frame to frame in the terminal. 'ncurses' is default if supported.
#
# 'raw' is an 8 or 16 bit (configurable via the 'bit_format' option) data
# stream of the bar heights that can be used to send to other playerlications.
# 'raw' defaults to 200 bars, which can be adjusted in the 'bars' option above.
#
# 'noritake' outputs a bitmap in the format expected by a Noritake VFD display
# in graphic mode. It only support the 3000 series graphical VFDs for now.
#
# 'sdl' uses the Simple DirectMedia Layer to render in a graphical context.
method = raw
# Visual channels. Can be 'stereo' or 'mono'.
# 'stereo' mirrors both channels with low frequencies in center.
# 'mono' outputs left to right lowest to highest frequencies.
# 'mono_option' set mono to either take input from 'left', 'right' or 'average'.
# set 'reverse' to 1 to display frequencies the other way around.
; channels = stereo
; mono_option = average
; reverse = 0
# Raw output target. A fifo will be created if target does not exist.
; raw_target = /dev/stdout
# Raw data format. Can be 'binary' or 'ascii'.
data_format = ascii
# Binary bit format, can be '8bit' (0-255) or '16bit' (0-65530).
; bit_format = 16bit
# Ascii max value. In 'ascii' mode range will run from 0 to value specified here
ascii_max_range = 7
# Ascii delimiters. In ascii format each bar and frame is separated by a delimiters.
# Use decimal value in ascii table (i.e. 59 = ';' and 10 = '\n' (line feed)).
; bar_delimiter = 59
; frame_delimiter = 10
# sdl window size and position. -1,-1 is centered.
; sdl_width = 1000
; sdl_height = 500
; sdl_x = -1
; sdl_y= -1
# set label on bars on the x-axis. Can be 'frequency' or 'none'. Default: 'none'
# 'frequency' displays the lower cut active frequency of the bar above.
# Only supported on ncurses and noncurses output.
; xaxis = none
# enable alacritty synchronized updates. 1 = on, 0 = active
# removes flickering in alacritty terminal emeulator.
# defaults to active since the behaviour in other terminal emulators is unknown
; alacritty_sync = 1
[color]
# Colors can be one of seven predefined: black, blue, cyan, green, magenta, red, white, yellow.
# Or defined by hex code '#xxxxxx' (hex code must be within ''). User defined colors requires
# ncurses output method and a terminal that can change color definitions such as Gnome-terminal or rxvt.
# if supported, ncurses mode will be forced on if user defined colors are used.
# default is to keep current terminal color
; background = default
; foreground = default
# SDL only support hex code colors, these are the default:
; background = '#111111'
; foreground = '#33cccc'
# Gradient mode, only hex defined colors (and thereby ncurses mode) are supported,
# background must also be defined in hex or remain commented out. 1 = on, 0 = active.
# You can define as many as 8 different colors. They range from bottom to top of screen
; gradient = 0
; gradient_count = 8
; gradient_color_1 = '#59cc33'
; gradient_color_2 = '#80cc33'
; gradient_color_3 = '#a6cc33'
; gradient_color_4 = '#cccc33'
; gradient_color_5 = '#cca633'
; gradient_color_6 = '#cc8033'
; gradient_color_7 = '#cc5933'
; gradient_color_8 = '#cc3333'
[smoothing]
# Percentage value for integral smoothing. Takes values from 0 - 100.
# Higher values means smoother, but less precise. 0 to disable.
# DEPRECATED as of 0.8.0, use noise_reduction instead
; integral = 77
# Disables or enables the so-called "Monstercat smoothing" with or without "waves". Set to 0 to disable.
; monstercat = 0
; waves = 0
# Set gravity percentage for "drop active". Higher values means bars will drop faster.
# Accepts only non-negative values. 50 means half gravity, 200 means double. Set to 0 to disable "drop active".
# DEPRECATED as of 0.8.0, use noise_reduction instead
; gravity = 100
# In bar height, bars that would have been lower that this will not be drawn.
# DEPRECATED as of 0.8.0
; ignore = 0
# Noise reduction, float 0 - 1. default 0.77
# the raw visualization is very noisy, this factor adjusts the integral and gravity filters to keep the signal smooth
# 1 will be very slow and smooth, 0 will be fast but noisy.
; noise_reduction = 0.77
[eq]
# This one is tricky. You can have as much keys as you want.
# Remember to uncomment more then one key! More keys = more precision.
# Look at readme.md on github for further explanations and examples.
# DEPRECATED as of 0.8.0 can be brought back by popular request, open issue at:
# https://github.com/karlstav/cava
; 1 = 1 # bass
; 2 = 1
; 3 = 1 # midtone
; 4 = 1
; 5 = 1 # treble

321
.config/cava/config Normal file
View file

@ -0,0 +1,321 @@
## Configuration file for CAVA.
# Remove the ; to change parameters.
[general]
# Smoothing mode. Can be 'normal', 'scientific' or 'waves'. DEPRECATED as of 0.6.0
; mode = normal
# Accepts only non-negative values.
; framerate = 60
# 'autosens' will attempt to decrease sensitivity if the bars peak. 1 = on, 0 = off
# new as of 0.6.0 autosens of low values (dynamic range)
# 'overshoot' allows bars to overshoot (in % of terminal height) without initiating autosens. DEPRECATED as of 0.6.0
; autosens = 1
; overshoot = 20
# Manual sensitivity in %. If autosens is enabled, this will only be the initial value.
# 200 means double height. Accepts only non-negative values.
; sensitivity = 100
# The number of bars (0-512). 0 sets it to auto (fill up console).
# Bars' width and space between bars in number of characters.
; bars = 0
; bar_width = 2
; bar_spacing = 1
# bar_height is only used for output in "noritake" format
; bar_height = 32
# For SDL width and space between bars is in pixels, defaults are:
; bar_width = 20
; bar_spacing = 5
# sdl_glsl have these default values, they are only used to calculate max number of bars.
; bar_width = 1
; bar_spacing = 0
# ceter bars in terminal, if there is space.
; center_align = 1
# max height of bars in terminal, in percent of terminal height.
; max_height = 100
# Lower and higher cutoff frequencies for lowest and highest bars
# the bandwidth of the visualizer.
# Note: there is a minimum total bandwidth of 43Mhz x number of bars.
# Cava will automatically increase the higher cutoff if a too low band is specified.
; lower_cutoff_freq = 50
; higher_cutoff_freq = 10000
# Seconds with no input before cava goes to sleep mode. Cava will not perform FFT or drawing and
# only check for input once per second. Cava will wake up once input is detected. 0 = disable.
; sleep_timer = 0
[input]
# Audio capturing method. Possible methods are: 'fifo', 'portaudio', 'pipewire', 'alsa', 'pulse', 'sndio', 'oss', 'jack' or 'shmem'
# Defaults to 'oss', 'pipewire', 'sndio', 'jack', 'pulse', 'alsa', 'portaudio' or 'fifo', in that order, dependent on what support cava was built with.
# On Mac it defaults to 'portaudio' or 'fifo'
# On windows this is automatic and no input settings are needed.
#
# All input methods uses the same config variable 'source'
# to define where it should get the audio.
#
# For pulseaudio and pipewire 'source' will be the source. Default: 'auto', which uses the monitor source of the default sink
# (all pulseaudio sinks(outputs) have 'monitor' sources(inputs) associated with them).
#
# For pipewire 'source' will be the object name or object.serial of the device to capture from.
# Both input and output devices are supported. To capture the monitor source of a sink node, append '.monitor' to the sink's object name.
#
# For alsa 'source' will be the capture device.
# For fifo 'source' will be the path to fifo-file.
# For shmem 'source' will be /squeezelite-AA:BB:CC:DD:EE:FF where 'AA:BB:CC:DD:EE:FF' will be squeezelite's MAC address
#
# For sndio 'source' will be a raw recording audio descriptor or a monitoring sub-device, e.g. 'rsnd/2' or 'snd/1'. Default: 'default'.
# README.md contains further information on how to setup CAVA for sndio.
#
# For oss 'source' will be the path to a audio device, e.g. '/dev/dsp2'. Default: '/dev/dsp', i.e. the default audio device.
# README.md contains further information on how to setup CAVA for OSS on FreeBSD.
#
# For jack 'source' will be the name of the JACK server to connect to, e.g. 'foobar'. Default: 'default'.
# README.md contains further information on how to setup CAVA for JACK.
#
; method = pulse
; source = auto
; method = pipewire
; source = auto
; method = alsa
; source = hw:Loopback,1
; method = fifo
; source = /tmp/mpd.fifo
; method = shmem
; source = /squeezelite-AA:BB:CC:DD:EE:FF
; method = portaudio
; source = auto
; method = sndio
; source = default
; method = oss
; source = /dev/dsp
; method = jack
; source = default
# The options 'sample_rate', 'sample_bits', 'channels' and 'autoconnect' can be configured for some input methods:
# sample_rate: fifo, pipewire, sndio, oss
# sample_bits: fifo, pipewire, sndio, oss
# channels: sndio, oss, jack
# autoconnect: jack
# Other methods ignore these settings.
# For pipewire, sample_rate will default to 48000, for all other input methods, sample_rate will default to 44100.
#
# For 'sndio' and 'oss' they are only preferred values, i.e. if the values are not supported
# by the chosen audio device, the device will use other supported values instead.
# Example: 48000, 32 and 2, but the device only supports 44100, 16 and 1, then it
# will use 44100, 16 and 1.
#
#
# The 'pipewire' input method has three options to control linking and mixing:
# active: Force the node to always process. Useful for monitoring sources when no other application is active.
# remix: Allow pipewire to remix audio channels to match cava's channel count. Useful for surround sound.
# virtual: Set the node to virtual, to avoid recording notifications from the DE.
#
; sample_rate = 44100
; sample_bits = 16
; channels = 2
; autoconnect = 2
; active = 0
; remix = 1
; virtual = 1
[output]
# Output method. Can be 'ncurses', 'noncurses', 'raw', 'noritake', 'sdl'
# or 'sdl_glsl'.
# 'noncurses' (default) uses a buffer and cursor movements to only print
# changes from frame to frame in the terminal. Uses less resources and is less
# prone to tearing (vsync issues) than 'ncurses'.
#
# 'raw' is an 8 or 16 bit (configurable via the 'bit_format' option) data
# stream of the bar heights that can be used to send to other applications.
# 'raw' defaults to 1024 bars stereo (512 bars mono), which can be adjusted in the 'bars' option above.
#
# 'noritake' outputs a bitmap in the format expected by a Noritake VFD display
# in graphic mode. It only support the 3000 series graphical VFDs for now.
#
# 'sdl' uses the Simple DirectMedia Layer to render in a graphical context.
# 'sdl_glsl' uses SDL to create an OpenGL context. Write your own shaders or
# use one of the predefined ones.
; method = noncurses
# Orientation of the visualization. Can be 'bottom', 'top', 'left', 'right' or
# 'horizontal'. Default is 'bottom'. 'left and 'right' are only supported on sdl
# and ncruses output. 'horizontal' (bars go up and down from center) is only supported
# on noncurses output.
# Note: many fonts have weird or missing glyphs for characters used in orientations
# other than 'bottom', which can make output not look right.
; orientation = bottom
# Visual channels. Can be 'stereo' or 'mono'.
# 'stereo' mirrors both channels with low frequencies in center.
# 'mono' outputs left to right lowest to highest frequencies.
# 'mono_option' set mono to either take input from 'left', 'right' or 'average'.
# set 'reverse' to 1 to display frequencies the other way around.
; channels = stereo
; mono_option = average
; reverse = 0
# Raw output target.
# On Linux, a fifo will be created if target does not exist.
# On Windows, a named pipe will be created if target does not exist.
; raw_target = /dev/stdout
# Raw data format. Can be 'binary' or 'ascii'.
; data_format = binary
# Binary bit format, can be '8bit' (0-255) or '16bit' (0-65530).
; bit_format = 16bit
# Ascii max value. In 'ascii' mode range will run from 0 to value specified here
; ascii_max_range = 1000
# Ascii delimiters. In ascii format each bar and frame is separated by a delimiters.
# Use decimal value in ascii table (i.e. 59 = ';' and 10 = '\n' (line feed)).
; bar_delimiter = 59
; frame_delimiter = 10
# sdl window size and position. -1,-1 is centered.
; sdl_width = 1024
; sdl_height = 512
; sdl_x = -1
; sdl_y= -1
; sdl_full_screen = 0
# set label on bars on the x-axis. Can be 'frequency' or 'none'. Default: 'none'
# 'frequency' displays the lower cut off frequency of the bar above.
# Only supported on ncurses and noncurses output.
; xaxis = none
# enable synchronized sync. 1 = on, 0 = off
# removes flickering in alacritty terminal emulator.
# defaults to off since the behaviour in other terminal emulators is unknown
; synchronized_sync = 0
# Shaders for sdl_glsl, located in $HOME/.config/cava/shaders
; vertex_shader = pass_through.vert
; fragment_shader = bar_spectrum.frag
; for glsl output mode, keep rendering even if no audio
; continuous_rendering = 0
# disable console blank (screen saver) in tty
# (Not supported on FreeBSD)
; disable_blanking = 0
# show a flat bar at the bottom of the screen when idle, 1 = on, 0 = off
; show_idle_bar_heads = 1
# show waveform instead of frequency spectrum, 1 = on, 0 = off
; waveform = 0
[color]
# Colors can be one of seven predefined: black, blue, cyan, green, magenta, red, white, yellow.
# Or defined by hex code '#xxxxxx' (hex code must be within ''). User defined colors requires
# a terminal that can change color definitions such as Gnome-terminal or rxvt.
# default is to keep current terminal color
; background = default
; foreground = default
# SDL and sdl_glsl only support hex code colors, these are the default:
; background = '#111111'
; foreground = '#33ffff'
# Gradient mode, only hex defined colors are supported,
# background must also be defined in hex or remain commented out. 1 = on, 0 = off.
# You can define as many as 8 different colors. They range from bottom to top of screen
; gradient = 0
; gradient_color_1 = '#59cc33'
; gradient_color_2 = '#80cc33'
; gradient_color_3 = '#a6cc33'
; gradient_color_4 = '#cccc33'
; gradient_color_5 = '#cca633'
; gradient_color_6 = '#cc8033'
; gradient_color_7 = '#cc5933'
; gradient_color_8 = '#cc3333'
# Horizontal is only supported on noncurses output.
# Only one color will be calculated per bar.
; horizontal_gradient = 0
; horizontal_gradient_color_1 = '#c45161'
; horizontal_gradient_color_2 = '#e094a0'
; horizontal_gradient_color_3 = '#f2b6c0'
; horizontal_gradient_color_4 = '#f2dde1'
; horizontal_gradient_color_5 = '#cbc7d8'
; horizontal_gradient_color_6 = '#8db7d2'
; horizontal_gradient_color_7 = '#5e62a9'
; horizontal_gradient_color_8 = '#434279'
# If both vertical and horizontal gradient is enabled, vertical will be blended in this direction.
# Can be 'up', 'down', 'left' or 'right'. 'up' means the vertical gradient will be blended in from
# bottom to top. I.e. the bottom will be only the horizontal
# and top will be only the color of the vertical gradient.
; blend_direction = 'up'
# use theme file instead of defining colors in this file
# themes are located in $HOME/.config/cava/themes
; theme = 'none'
[smoothing]
# Percentage value for integral smoothing. Takes values from 0 - 100.
# Higher values means smoother, but less precise. 0 to disable.
# DEPRECATED as of 0.8.0, use noise_reduction instead
; integral = 77
# Disables or enables the so-called "Monstercat smoothing" with or without "waves". Set to 0 to disable.
; monstercat = 0
; waves = 0
# Set gravity percentage for "drop off". Higher values means bars will drop faster.
# Accepts only non-negative values. 50 means half gravity, 200 means double. Set to 0 to disable "drop off".
# DEPRECATED as of 0.8.0, use noise_reduction instead
; gravity = 100
# In bar height, bars that would have been lower that this will not be drawn.
# DEPRECATED as of 0.8.0
; ignore = 0
# Noise reduction, int 0 - 100. default 77
# the raw visualization is very noisy, this factor adjusts the integral and gravity filters to keep the signal smooth
# 100 will be very slow and smooth, 0 will be fast but noisy.
; noise_reduction = 77
[eq]
# This one is tricky. You can have as much keys as you want.
# Remember to uncomment more than one key! More keys = more precision.
# Look at readme.md on github for further explanations and examples.
; 1 = 1 # bass
; 2 = 1
; 3 = 1 # midtone
; 4 = 1
; 5 = 1 # treble

11
.config/cava/config.bak Normal file
View file

@ -0,0 +1,11 @@
[color]
gradient = 1
gradient_color_1 = '#81c8be'
gradient_color_2 = '#99d1db'
gradient_color_3 = '#85c1dc'
gradient_color_4 = '#8caaee'
gradient_color_5 = '#ca9ee6'
gradient_color_6 = '#f4b8e4'
gradient_color_7 = '#ea999c'
gradient_color_8 = '#e78284'

View file

@ -0,0 +1,73 @@
#version 330
in vec2 fragCoord;
out vec4 fragColor;
// bar values. defaults to left channels first (low to high), then right (high to low).
uniform float bars[512];
uniform int bars_count; // number of bars (left + right) (configurable)
uniform int bar_width; // bar width (configurable), not used here
uniform int bar_spacing; // space bewteen bars (configurable)
uniform vec3 u_resolution; // window resolution
// colors, configurable in cava config file (r,g,b) (0.0 - 1.0)
uniform vec3 bg_color; // background color
uniform vec3 fg_color; // foreground color
uniform int gradient_count;
uniform vec3 gradient_colors[8]; // gradient colors
uniform float shader_time; // shader execution time s (not used here)
uniform sampler2D inputTexture; // Texture from the last render pass (not used here)
vec3 normalize_C(float y, vec3 col_1, vec3 col_2, float y_min, float y_max) {
// create color based on fraction of this color and next color
float yr = (y - y_min) / (y_max - y_min);
return col_1 * (1.0 - yr) + col_2 * yr;
}
void main() {
// find which bar to use based on where we are on the x axis
float x = u_resolution.x * fragCoord.x;
int bar = int(bars_count * fragCoord.x);
// calculate a bar size
float bar_size = u_resolution.x / bars_count;
// the y coordinate and bar values are the same
float y = bars[bar];
// make sure there is a thin line at bottom
if (y * u_resolution.y < 1.0) {
y = 1.0 / u_resolution.y;
}
// draw the bar up to current height
if (y > fragCoord.y) {
// make some space between bars basen on settings
if (x > (bar + 1) * (bar_size)-bar_spacing) {
fragColor = vec4(bg_color, 1.0);
} else {
if (gradient_count == 0) {
fragColor = vec4(fg_color, 1.0);
} else {
// find which color in the configured gradient we are at
int color = int((gradient_count - 1) * fragCoord.y);
// find where on y this and next color is supposed to be
float y_min = color / (gradient_count - 1.0);
float y_max = (color + 1.0) / (gradient_count - 1.0);
// make color
fragColor = vec4(normalize_C(fragCoord.y, gradient_colors[color],
gradient_colors[color + 1], y_min, y_max),
1.0);
}
}
} else {
fragColor = vec4(bg_color, 1.0);
}
}

View file

@ -0,0 +1,117 @@
#version 330
// this shader was stolen from shadertoy user ChunderFPV
#define SCALE 8.0
#define PI radians(180.0)
#define TAU (PI * 2.0)
#define CS(a) vec2(cos(a), sin(a))
#define PT(u, r) smoothstep(0.0, r, r - length(u))
in vec2 fragCoord;
out vec4 fragColor;
uniform float bars[512];
uniform int bars_count; // number of bars (left + right) (configurable)
uniform float shader_time; // shader execution time s
uniform int bar_width; // bar width (configurable), not used here
uniform int bar_spacing; // space bewteen bars (configurable)
uniform vec3 u_resolution; // window resolution
// colors, configurable in cava config file (r,g,b) (0.0 - 1.0)
uniform vec3 bg_color; // background color
uniform vec3 fg_color; // foreground color
uniform int gradient_count;
uniform vec3 gradient_colors[8]; // gradient colors
// gradient map ( color, equation, time, width, shadow, reciprocal )
vec3 gm(vec3 c, float n, float t, float w, float d, bool i) {
float g = min(abs(n), 1.0 / abs(n));
float s = abs(sin(n * PI - t));
if (i)
s = min(s, abs(sin(PI / n + t)));
return (1.0 - pow(abs(s), w)) * c * pow(g, d) * 6.0;
}
// denominator spiral, use 1/n for numerator
// ( screen xy, spiral exponent, decimal, line width, hardness, rotation )
float ds(vec2 u, float e, float n, float w, float h, float ro) {
float ur = length(u); // unit radius
float sr = pow(ur, e); // spiral radius
float a = round(sr) * n * TAU; // arc
vec2 xy = CS(a + ro) * ur; // xy coords
float l = PT(u - xy, w); // line
float s = mod(sr + 0.5, 1.0); // gradient smooth
s = min(s, 1.0 - s); // darken filter
return l * s * h;
}
void main() {
float t = shader_time / PI * 2.0;
vec4 m = vec4(0, 0, 0, 0); // iMouse;
m.xy = m.xy * 2.0 / u_resolution.xy - 1.0; // ±1x, ±1y
if (m.z > 0.0)
t += m.y * SCALE; // move time with mouse y
float z = (m.z > 0.0) ? pow(1.0 - abs(m.y), sign(m.y)) : 1.0; // zoom (+)
float e = (m.z > 0.0) ? pow(1.0 - abs(m.x), -sign(m.x))
: 1.0; // screen exponent (+)
float se = (m.z > 0.0) ? e * -sign(m.y) : 1.0; // spiral exponent
vec3 bg = vec3(0); // black background
float aa = 3.0; // anti-aliasing
for (float j = 0.0; j < aa; j++)
for (float k = 0.0; k < aa; k++) {
vec3 c = vec3(0);
vec2 o = vec2(j, k) / aa;
vec2 uv = (fragCoord * u_resolution.xy - 0.5 * u_resolution.xy + o) /
u_resolution.y * SCALE * z; // apply cartesian, scale and zoom
if (m.z > 0.0)
uv =
exp(log(abs(uv)) * e) * sign(uv); // warp screen space with exponent
float px = length(fwidth(uv)); // pixel width
float x = uv.x; // every pixel on x
float y = uv.y; // every pixel on y
float l = length(uv); // hypot of xy: sqrt(x*x+y*y)
float mc = (x * x + y * y - 1.0) / y; // metallic circle at xy
float g = min(abs(mc), 1.0 / abs(mc)); // gradient
vec3 gold = vec3(1.0, 0.6, 0.0) * g * l;
vec3 blue = vec3(0.3, 0.5, 0.9) * (1.0 - g);
vec3 rgb = max(gold, blue);
float w = 0.1; // line width
float d = 0.4; // shadow depth
c = max(c, gm(rgb, mc, -t, w * bars[0], d, false)); // metallic
c = max(c, gm(rgb, abs(y / x) * sign(y), -t, w * bars[1], d,
false)); // tangent
c = max(c, gm(rgb, (x * x) / (y * y) * sign(y), -t, w * bars[2], d,
false)); // sqrt cotangent
c = max(c, gm(rgb, (x * x) + (y * y), t, w * bars[3], d,
true)); // sqrt circles
c += rgb * ds(uv, se, t / TAU, px * 2.0 * bars[4], 2.0, 0.0); // spiral 1a
c += rgb * ds(uv, se, t / TAU, px * 2.0 * bars[5], 2.0, PI); // spiral 1b
c +=
rgb * ds(uv, -se, t / TAU, px * 2.0 * bars[6], 2.0, 0.0); // spiral 2a
c += rgb * ds(uv, -se, t / TAU, px * 2.0 * bars[7], 2.0, PI); // spiral 2b
c = max(c, 0.0); // clear negative color
c += pow(max(1.0 - l, 0.0), 3.0 / z); // center glow
if (m.z > 0.0) // display grid on click
{
vec2 xyg = abs(fract(uv + 0.5) - 0.5) / px; // xy grid
c.gb += 0.2 * (1.0 - min(min(xyg.x, xyg.y), 1.0));
}
bg += c;
}
bg /= aa * aa;
bg *= sqrt(bg) * 1.5;
fragColor = vec4(bg, 1.0);
}

View file

@ -0,0 +1,34 @@
#version 330
in vec2 fragCoord;
out vec4 fragColor;
// bar values. defaults to left channels first (low to high), then right (high to low).
uniform float bars[512];
uniform int bars_count; // number of bars (left + right) (configurable)
uniform vec3 u_resolution; // window resolution, not used here
//colors, configurable in cava config file
uniform vec3 bg_color; // background color(r,g,b) (0.0 - 1.0), not used here
uniform vec3 fg_color; // foreground color, not used here
void main()
{
// find which bar to use based on where we are on the x axis
int bar = int(bars_count * fragCoord.x);
float bar_y = 1.0 - abs((fragCoord.y - 0.5)) * 2.0;
float y = (bars[bar]) * bar_y;
float bar_x = (fragCoord.x - float(bar) / float(bars_count)) * bars_count;
float bar_r = 1.0 - abs((bar_x - 0.5)) * 2;
bar_r = bar_r * bar_r * 2;
// set color
fragColor.r = fg_color.x * y * bar_r;
fragColor.g = fg_color.y * y * bar_r;
fragColor.b = fg_color.z * y * bar_r;
}

View file

@ -0,0 +1,14 @@
#version 330
// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;
// Output data ; will be interpolated for each fragment.
out vec2 fragCoord;
void main()
{
gl_Position = vec4(vertexPosition_modelspace,1);
fragCoord = (vertexPosition_modelspace.xy+vec2(1,1))/2.0;
}

View file

@ -0,0 +1,53 @@
#version 330
in vec2 fragCoord;
out vec4 fragColor;
// bar values. defaults to left channels first (low to high), then right (high
// to low).
uniform float bars[512];
uniform int bars_count; // number of bars (left + right) (configurable)
uniform int bar_width; // bar width (configurable), not used here
uniform int bar_spacing; // space bewteen bars (configurable)
uniform vec3 u_resolution; // window resolution
// colors, configurable in cava config file (r,g,b) (0.0 - 1.0)
uniform vec3 bg_color; // background color
uniform vec3 fg_color; // foreground color
uniform int gradient_count;
uniform vec3 gradient_colors[8]; // gradient colors
uniform sampler2D inputTexture; // Texture from the last render pass
vec3 normalize_C(float y, vec3 col_1, vec3 col_2, float y_min, float y_max) {
// create color based on fraction of this color and next color
float yr = (y - y_min) / (y_max - y_min);
return col_1 * (1.0 - yr) + col_2 * yr;
}
void main() {
// find which bar to use based on where we are on the y axis
int bar = int(bars_count * fragCoord.y);
float y = bars[bar];
float band_size = 1.0 / float(bars_count);
float current_band_min = bar * band_size;
float current_band_max = (bar + 1) * band_size;
int hist_length = 512;
float win_size = 1.0 / hist_length;
if (fragCoord.x > 1.0 - win_size) {
if (fragCoord.y > current_band_min && fragCoord.y < current_band_max) {
fragColor = vec4(fg_color * y, 1.0);
}
} else {
vec2 offsetCoord = fragCoord;
offsetCoord.x += float(win_size);
fragColor = texture(inputTexture, offsetCoord);
}
}

View file

@ -0,0 +1,112 @@
#version 330
// Emulate the "line style" spectrum analyzer from Winamp 2.
// Try this config for a demonstration:
/*
[general]
bar_width = 2
bar_spacing = 0
higher_cutoff_freq = 22000
[output]
method = sdl_glsl
channels = mono
fragment_shader = winamp_line_style_spectrum.frag
[color]
background = '#000000'
gradient = 1
gradient_color_1 = '#319C08'
gradient_color_2 = '#29CE10'
gradient_color_3 = '#BDDE29'
gradient_color_4 = '#DEA518'
gradient_color_5 = '#D66600'
gradient_color_6 = '#CE2910'
[smoothing]
noise_reduction = 10
*/
in vec2 fragCoord;
out vec4 fragColor;
// bar values. defaults to left channels first (low to high), then right (high to low).
uniform float bars[512];
uniform int bars_count; // number of bars (left + right) (configurable)
uniform int bar_width; // bar width (configurable), not used here
uniform int bar_spacing; // space bewteen bars (configurable)
uniform vec3 u_resolution; // window resolution
//colors, configurable in cava config file (r,g,b) (0.0 - 1.0)
uniform vec3 bg_color; // background color
uniform vec3 fg_color; // foreground color
uniform int gradient_count;
uniform vec3 gradient_colors[8]; // gradient colors
vec3 normalize_C(float y,vec3 col_1, vec3 col_2, float y_min, float y_max)
{
//create color based on fraction of this color and next color
float yr = (y - y_min) / (y_max - y_min);
return col_1 * (1.0 - yr) + col_2 * yr;
}
void main()
{
// find which bar to use based on where we are on the x axis
float x = u_resolution.x * fragCoord.x;
int bar = int(bars_count * fragCoord.x);
//calculate a bar size
float bar_size = u_resolution.x / bars_count;
//the y coordinate is stretched by 4X to resemble Winamp
float y = min(bars[bar] * 4.0, 1.0);
// make sure there is a thin line at bottom
if (y * u_resolution.y < 1.0)
{
y = 1.0 / u_resolution.y;
}
vec4 bar_color;
if (gradient_count == 0)
{
bar_color = vec4(fg_color,1.0);
}
else
{
//find color in the configured gradient for the top of the bar
int color = int((gradient_count - 1) * y);
//find where on y this and next color is supposed to be
float y_min = float(color) / (gradient_count - 1.0);
float y_max = float(color + 1) / (gradient_count - 1.0);
//make a solid color for the entire bar
bar_color = vec4(normalize_C(y, gradient_colors[color], gradient_colors[color + 1], y_min, y_max), 1.0);
}
//draw the bar up to current height
if (y > fragCoord.y)
{
//make some space between bars based on settings
if (x > (bar + 1) * (bar_size) - bar_spacing)
{
fragColor = vec4(bg_color,1.0);
}
else
{
fragColor = bar_color;
}
}
else
{
fragColor = vec4(bg_color,1.0);
}
}

View file

@ -0,0 +1,15 @@
[color]
background = '#001e26'
foreground = '#708183'
gradient = 1
gradient_color_1 = '#268bd2'
gradient_color_2 = '#6c71c4'
gradient_color_3 = '#cb4b16'
horizontal_gradient = 1
horizontal_gradient_color_1 = '#586e75'
horizontal_gradient_color_2 = '#b58900'
horizontal_gradient_color_3 = '#839496'
blend_direction = 'up'

View file

@ -0,0 +1,10 @@
[color]
horizontal_gradient = 1
horizontal_gradient_color_1 = '#c45161'
horizontal_gradient_color_2 = '#e094a0'
horizontal_gradient_color_3 = '#f2b6c0'
horizontal_gradient_color_4 = '#f2dde1'
horizontal_gradient_color_5 = '#cbc7d8'
horizontal_gradient_color_6 = '#8db7d2'
horizontal_gradient_color_7 = '#5e62a9'
horizontal_gradient_color_8 = '#434279'

1
.config/fastfetch Submodule

@ -0,0 +1 @@
Subproject commit 5125578d9ceb787b923a91f4416f4b49f18806f3

12
.config/fuzzel/fuzzel.ini Normal file
View file

@ -0,0 +1,12 @@
[colors]
background=eff1f5ff
text=4c4f69ff
prompt=5c5f77ff
placeholder=8c8fa1ff
input=4c4f69ff
match=1e66f5ff
selection=acb0beff
selection-text=4c4f69ff
selection-match=1e66f5ff
counter=8c8fa1ff
border=1e66f5ff

View file

@ -0,0 +1,21 @@
background #212733
foreground #d9d7ce
cursor #ffcc66
selection_background #343f4c
color0 #191e2a
color8 #686868
color1 #ed8274
color9 #f28779
color2 #a6cc70
color10 #bae67e
color3 #fad07b
color11 #ffd580
color4 #6dcbfa
color12 #73d0ff
color5 #cfbafa
color13 #d4bfff
color6 #90e1c6
color14 #95e6cb
color7 #c7c7c7
color15 #ffffff
selection_foreground #212733

2801
.config/kitty/kitty.conf Normal file

File diff suppressed because it is too large Load diff

12
.config/mako/config Normal file
View file

@ -0,0 +1,12 @@
# Colors
background-color=#303446
text-color=#c6d0f5
border-color=#8caaee
progress-color=over #414559
default-timeout=5000
border-radius=12
font=hack 11
[urgency=high]
border-color=#ef9f76

1
.config/niri Submodule

@ -0,0 +1 @@
Subproject commit 7648a2af7b3432e34a4fbcb5a2981f05a2c1f3b7

29
.config/swaylock/config Normal file
View file

@ -0,0 +1,29 @@
color=1e1e2e
bs-hl-color=f5e0dc
caps-lock-bs-hl-color=f5e0dc
caps-lock-key-hl-color=a6e3a1
inside-color=00000000
inside-clear-color=00000000
inside-caps-lock-color=00000000
inside-ver-color=00000000
inside-wrong-color=00000000
key-hl-color=a6e3a1
layout-bg-color=00000000
layout-border-color=00000000
layout-text-color=cdd6f4
line-color=00000000
line-clear-color=00000000
line-caps-lock-color=00000000
line-ver-color=00000000
line-wrong-color=00000000
ring-color=b4befe
ring-clear-color=f5e0dc
ring-caps-lock-color=fab387
ring-ver-color=89b4fa
ring-wrong-color=eba0ac
separator-color=00000000
text-color=cdd6f4
text-clear-color=f5e0dc
text-caps-lock-color=fab387
text-ver-color=89b4fa
text-wrong-color=eba0ac

100
.config/swaync/config.json Normal file
View file

@ -0,0 +1,100 @@
{
"$schema": "/etc/xdg/swaync/configSchema.json",
"ignore-gtk-theme": true,
"positionX": "right",
"positionY": "top",
"layer": "overlay",
"control-center-layer": "top",
"layer-shell": true,
"layer-shell-cover-screen": true,
"cssPriority": "user",
"control-center-margin-top": 0,
"control-center-margin-bottom": 0,
"control-center-margin-right": 0,
"control-center-margin-left": 0,
"notification-2fa-action": true,
"notification-inline-replies": false,
"notification-body-image-height": 100,
"notification-body-image-width": 200,
"timeout": 10,
"timeout-low": 5,
"timeout-critical": 0,
"fit-to-screen": true,
"relative-timestamps": true,
"control-center-width": 500,
"control-center-height": 600,
"notification-window-width": 500,
"keyboard-shortcuts": true,
"notification-grouping": true,
"image-visibility": "when-available",
"transition-time": 200,
"hide-on-clear": false,
"hide-on-action": true,
"text-empty": "No Notifications",
"script-fail-notify": true,
"scripts": {
"example-script": {
"exec": "echo 'Do something...'",
"urgency": "Normal"
},
"example-action-script": {
"exec": "echo 'Do something actionable!'",
"urgency": "Normal",
"run-on": "action"
}
},
"notification-visibility": {
"example-name": {
"state": "muted",
"urgency": "Low",
"app-name": "Spotify"
}
},
"widgets": [
"inhibitors",
"title",
"dnd",
"notifications"
],
"widget-config": {
"notifications": {
"vexpand": true
},
"inhibitors": {
"text": "Inhibitors",
"button-text": "Clear All",
"clear-all-button": true
},
"title": {
"text": "Notifications",
"clear-all-button": true,
"button-text": "Clear All"
},
"dnd": {
"text": "Do Not Disturb"
},
"label": {
"max-lines": 5,
"text": "Label Text"
},
"mpris": {
"blacklist": [],
"autohide": false,
"show-album-art": "always",
"loop-carousel": false
},
"buttons-grid": {
"buttons-per-row": 7,
"actions": [
{
"label": "直",
"type": "toggle",
"active": true,
"command": "sh -c '[[ $SWAYNC_TOGGLE_STATE == true ]] && nmcli radio wifi on || nmcli radio wifi off'",
"update-command": "sh -c '[[ $(nmcli radio wifi) == \"enabled\" ]] && echo true || echo false'"
}
]
},
}
"style": "~/.config/swaync/style.css"
}

328
.config/swaync/style.css Normal file
View file

@ -0,0 +1,328 @@
* {
all: unset;
font-size: 14px;
font-family: "Ubuntu Nerd Font";
transition: 200ms;
}
trough highlight {
background: #cdd6f4;
}
scale {
margin: 0 7px;
}
scale trough {
margin: 0rem 1rem;
min-height: 8px;
min-width: 70px;
border-radius: 12.6px;
}
trough slider {
margin: -10px;
border-radius: 12.6px;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
transition: all 0.2s ease;
background-color: #89b4fa;
}
trough slider:hover {
box-shadow: 0 0 2px rgba(0, 0, 0, 0.8), 0 0 8px #89b4fa;
}
trough {
background-color: #313244;
}
/* notifications */
.notification-background {
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.8), inset 0 0 0 1px #45475a;
border-radius: 12.6px;
margin: 18px;
background: #181825;
color: #cdd6f4;
padding: 0;
}
.notification-background .notification {
padding: 7px;
border-radius: 12.6px;
}
.notification-background .notification.critical {
box-shadow: inset 0 0 7px 0 #f38ba8;
}
.notification .notification-content {
margin: 7px;
}
.notification .notification-content overlay {
/* icons */
margin: 4px;
}
.notification-content .summary {
color: #cdd6f4;
}
.notification-content .time {
color: #a6adc8;
}
.notification-content .body {
color: #bac2de;
}
.notification > *:last-child > * {
min-height: 3.4em;
}
.notification-background .close-button {
margin: 7px;
padding: 2px;
border-radius: 6.3px;
color: #1e1e2e;
background-color: #f38ba8;
}
.notification-background .close-button:hover {
background-color: #eba0ac;
}
.notification-background .close-button:active {
background-color: #f5c2e7;
}
.notification .notification-action {
border-radius: 7px;
color: #cdd6f4;
box-shadow: inset 0 0 0 1px #45475a;
margin: 4px;
padding: 8px;
font-size: 0.2rem; /* controls the button size not text size*/
}
.notification .notification-action {
background-color: #313244;
}
.notification .notification-action:hover {
background-color: #45475a;
}
.notification .notification-action:active {
background-color: #585b70;
}
.notification.critical progress {
background-color: #f38ba8;
}
.notification.low progress,
.notification.normal progress {
background-color: #89b4fa;
}
.notification progress,
.notification trough,
.notification progressbar {
border-radius: 12.6px;
padding: 3px 0;
}
/* control center */
.control-center {
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.8), inset 0 0 0 1px #313244;
border-radius: 12.6px;
background-color: #1e1e2e;
color: #cdd6f4;
padding: 14px;
}
.control-center .notification-background {
border-radius: 7px;
box-shadow: inset 0 0 0 1px #45475a;
margin: 4px 10px;
}
.control-center .notification-background .notification {
border-radius: 7px;
}
.control-center .notification-background .notification.low {
opacity: 0.8;
}
.control-center .widget-title > label {
color: #cdd6f4;
font-size: 1.3em;
}
.control-center .widget-title button {
border-radius: 7px;
color: #cdd6f4;
background-color: #313244;
box-shadow: inset 0 0 0 1px #45475a;
padding: 8px;
}
.control-center .widget-title button:hover {
background-color: #45475a;
}
.control-center .widget-title button:active {
background-color: #585b70;
}
.control-center .notification-group {
margin-top: 10px;
}
.control-center .notification-group:focus .notification-background {
background-color: #313244;
}
scrollbar slider {
margin: -3px;
opacity: 0.8;
}
scrollbar trough {
margin: 2px 0;
}
/* dnd */
.widget-dnd {
margin-top: 5px;
border-radius: 8px;
font-size: 1.1rem;
}
.widget-dnd > switch {
font-size: initial;
border-radius: 8px;
background: #313244;
box-shadow: none;
}
.widget-dnd > switch:checked {
background: #89b4fa;
}
.widget-dnd > switch slider {
background: #45475a;
border-radius: 8px;
}
/* mpris */
.widget-mpris-player {
background: #313244;
border-radius: 12.6px;
color: #cdd6f4;
}
.mpris-overlay {
background-color: #313244;
opacity: 0.9;
padding: 15px 10px;
}
.widget-mpris-album-art {
-gtk-icon-size: 100px;
border-radius: 12.6px;
margin: 0 10px;
}
.widget-mpris-title {
font-size: 1.2rem;
color: #cdd6f4;
}
.widget-mpris-subtitle {
font-size: 1rem;
color: #bac2de;
}
.widget-mpris button {
border-radius: 12.6px;
color: #cdd6f4;
margin: 0 5px;
padding: 2px;
}
.widget-mpris button image {
-gtk-icon-size: 1.8rem;
}
.widget-mpris button:hover {
background-color: #313244;
}
.widget-mpris button:active {
background-color: #45475a;
}
.widget-mpris button:disabled {
opacity: 0.5;
}
.widget-menubar > box > .menu-button-bar > button > label {
font-size: 3rem;
padding: 0.5rem 2rem;
}
.widget-menubar > box > .menu-button-bar > :last-child {
color: #f38ba8;
}
.power-buttons button:hover,
.powermode-buttons button:hover,
.screenshot-buttons button:hover {
background: #313244;
}
.control-center .widget-label > label {
color: #cdd6f4;
font-size: 2rem;
}
.widget-buttons-grid {
padding-top: 1rem;
}
.widget-buttons-grid > flowbox > flowboxchild > button label {
font-size: 2.5rem;
}
.widget-volume {
padding: 1rem 0;
}
.widget-volume label {
color: #74c7ec;
padding: 0 1rem;
}
.widget-volume trough highlight {
background: #74c7ec;
}
.widget-backlight trough highlight {
background: #f9e2af;
}
.widget-backlight label {
font-size: 1.5rem;
color: #f9e2af;
}
.widget-backlight .KB {
padding-bottom: 1rem;
}
.image {
padding-right: 0.5rem;
}

1
.config/waybar Submodule

@ -0,0 +1 @@
Subproject commit d9bf08408520d01d221f7f1f49353ad5b98a623b

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#8aadf4" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g><g><path d="M500,10C229.4,10,10,229.4,10,500s219.4,490,490,490s490-219.4,490-490S770.6,10,500,10z M500,885.1c-212.7,0-385.1-172.4-385.1-385.1S287.3,114.9,500,114.9S885.1,287.3,885.1,500S712.7,885.1,500,885.1z M576.5,308.7v382.4c0,42.2-34.2,76.5-76.5,76.5c-42.3,0-76.5-34.2-76.5-76.5V308.7c0-42.2,34.2-76.5,76.5-76.5C542.2,232.3,576.5,266.5,576.5,308.7z"/></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g></g>
</svg>

After

Width:  |  Height:  |  Size: 969 B

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#8aadf4" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g><g><path d="M321.8,455.5h356.4V321.8c0-49.2-17.4-91.2-52.2-126c-34.8-34.8-76.8-52.2-126-52.2c-49.2,0-91.2,17.4-126,52.2c-34.8,34.8-52.2,76.8-52.2,126L321.8,455.5L321.8,455.5z M900.9,522.3v400.9c0,18.6-6.5,34.3-19.5,47.3c-13,13-28.8,19.5-47.3,19.5H165.9c-18.6,0-34.3-6.5-47.3-19.5s-19.5-28.8-19.5-47.3V522.3c0-18.6,6.5-34.3,19.5-47.3c13-13,28.8-19.5,47.3-19.5h22.3V321.8c0-85.4,30.6-158.7,91.9-219.9C341.3,40.6,414.6,10,500,10c85.4,0,158.7,30.6,219.9,91.9c61.3,61.3,91.9,134.6,91.9,219.9v133.6h22.3c18.6,0,34.3,6.5,47.3,19.5C894.4,487.9,900.9,503.7,900.9,522.3L900.9,522.3z"/></g></g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#8aadf4" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g><path d="M622.5,990H50.8C26.3,990,10,973.7,10,949.2V50.8C10,26.3,26.3,10,50.8,10h571.7c24.5,0,40.8,16.3,40.8,40.8v285.8c0,24.5-16.3,40.8-40.8,40.8s-40.8-16.3-40.8-40.8v-245h-490v816.7h490v-245c0-24.5,16.3-40.8,40.8-40.8s40.8,16.3,40.8,40.8v285.8C663.3,973.7,647,990,622.5,990z"/><path d="M949.2,540.8H336.7c-24.5,0-40.8-16.3-40.8-40.8c0-24.5,16.3-40.8,40.8-40.8h612.5c24.5,0,40.8,16.3,40.8,40.8C990,524.5,973.7,540.8,949.2,540.8z"/><path d="M949.2,540.8c-12.3,0-20.4-4.1-28.6-12.3L757.3,365.3c-16.3-16.3-16.3-40.8,0-57.2c16.3-16.3,40.8-16.3,57.2,0l163.3,163.3c16.3,16.3,16.3,40.8,0,57.2C969.6,536.8,961.4,540.8,949.2,540.8z"/><path d="M785.8,704.2c-12.3,0-20.4-4.1-28.6-12.3c-16.3-16.3-16.3-40.8,0-57.2l163.3-163.3c16.3-16.3,40.8-16.3,57.2,0c16.3,16.3,16.3,40.8,0,57.2L814.4,691.9C806.3,700.1,798.1,704.2,785.8,704.2z"/></g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#8aadf4" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g><path d="M134.6,285.6C64.9,420.7,60.1,590,137.1,723.4L42,668.5l-32,55.4c93.1,52.1,133.6,75.9,184,106.2c28.5-51.5,52.8-94.4,107.4-186.1L246,612l-53.4,92.5C65.4,502.7,167.2,200.3,398.8,126.2C638,29.3,929,223.5,931.5,481.5c19.6,236.7-208.9,443.6-439.3,416.2l-29.5,51c277.7,54.4,556.5-201.7,524.7-483.1C976.1,170.8,637.1-41.2,367.1,77.5C262.8,114.2,183.1,191.5,134.6,285.6z"/></g>
</svg>

After

Width:  |  Height:  |  Size: 877 B

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#8aadf4" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g><path d="M764,152.1c30.9,22,58.3,46.8,82.4,74.6c24,27.8,44.6,57.8,61.8,90.1c17.2,32.3,30.2,66.4,39.1,102.4c8.9,36,13.4,72.6,13.4,109.6c0,63.8-12.2,123.7-36.5,179.6c-24.4,55.9-57.3,104.7-98.8,146.2c-41.5,41.5-90.2,74.5-146.2,98.8C623.2,977.8,563.3,990,499.5,990c-63.1,0-122.7-12.2-178.6-36.5c-55.9-24.4-104.8-57.3-146.7-98.8c-41.9-41.5-74.8-90.2-98.8-146.2c-24-55.9-36-115.8-36-179.6c0-36.4,4.3-72.1,12.9-107.1c8.6-35,20.8-68.3,36.5-99.9c15.8-31.6,35.3-61.1,58.7-88.5c23.3-27.5,49.4-52.2,78.2-74.1c15.1-11,31.4-15.1,48.9-12.4c17.5,2.7,31.7,11.3,42.7,25.7c11,14.4,15.1,30.5,12.4,48.4c-2.7,17.8-11.3,32.3-25.7,43.2c-43.2,31.6-76.4,70.3-99.3,116.3c-23,46-34.5,95.4-34.5,148.2c0,45.3,8.6,88,25.7,128.2c17.2,40.1,40.7,75.1,70.5,105c29.9,29.9,64.9,53.5,105,71c40.1,17.5,82.9,26.3,128.2,26.3c45.3,0,88-8.7,128.2-26.3c40.1-17.5,75.1-41.2,105-71s53.5-64.9,71-105c17.5-40.1,26.3-82.9,26.3-128.2c0-53.5-12.4-104.1-37.1-151.8c-24.7-47.7-59.4-87-104-117.9c-15.1-10.3-24.2-24.4-27.3-42.2c-3.1-17.8,0.5-34.3,10.8-49.4c10.3-14.4,24.4-23.2,42.2-26.2C732.5,138.2,748.9,141.8,764,152.1L764,152.1z M499.5,531.9c-17.8,0-33.1-6.3-45.8-19c-12.7-12.7-19-28-19-45.8V75.9c0-17.8,6.3-33.3,19-46.3c12.7-13,28-19.6,45.8-19.6c18.5,0,34.1,6.5,46.8,19.6c12.7,13,19,28.5,19,46.3v391.2c0,17.8-6.3,33.1-19,45.8C533.6,525.6,518,531.9,499.5,531.9L499.5,531.9z"/></g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#8aadf4" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g><path d="M500,990c-66.1,0-130.3-13-190.7-38.5c-58.4-24.7-110.8-60-155.7-105s-80.3-97.4-105-155.7C23,630.3,10,566.1,10,500c0-66.1,13-130.3,38.5-190.7c24.7-58.4,60-110.8,105-155.7c45-45,97.4-80.3,155.7-105C369.7,23,433.9,10,500,10c66.1,0,130.3,13,190.7,38.5c58.4,24.7,110.8,60,155.7,105c45,45,80.3,97.4,105,155.7C977,369.7,990,433.9,990,500c0,66.1-13,130.3-38.5,190.7c-24.7,58.4-60,110.8-105,155.7s-97.4,80.3-155.7,105C630.3,977,566.1,990,500,990z M500,79.6c-112.3,0-217.9,43.7-297.3,123.1C123.3,282.1,79.6,387.7,79.6,500s43.7,217.9,123.1,297.3c79.4,79.4,185,123.1,297.3,123.1c112.3,0,217.9-43.7,297.3-123.1c79.4-79.4,123.1-185,123.1-297.3s-43.7-217.9-123.1-297.3C717.9,123.3,612.3,79.6,500,79.6z"/><path d="M322.5,290.6h108v412h-108V290.6z"/><path d="M561.6,290.6h107.9v412H561.6V290.6z"/></g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

36
.config/wlogout/layout Normal file
View file

@ -0,0 +1,36 @@
{
"label" : "lock",
"action" : "sh -c '( swaylock --screenshots --clock --indicator --indicator-radius 100 --indicator-thickness 7 --effect-blur 10x10)' & disown",
"text" : "Lock",
"keybind" : "l"
}
{
"label" : "hibernate",
"action" : "systemctl hibernate",
"text" : "Hibernate",
"keybind" : "h"
}
{
"label" : "logout",
"action" : "sh -c 'niri msg action quit skip-confirmation=true'",
"text" : "Logout",
"keybind" : "e"
}
{
"label" : "shutdown",
"action" : "systemctl poweroff",
"text" : "Shutdown",
"keybind" : "s"
}
{
"label" : "suspend",
"action" : "systemctl suspend",
"text" : "Suspend",
"keybind" : "u"
}
{
"label" : "reboot",
"action" : "systemctl reboot",
"text" : "Reboot",
"keybind" : "r"
}

54
.config/wlogout/style.css Normal file
View file

@ -0,0 +1,54 @@
* {
background-image: none;
box-shadow: none;
}
window {
background-color: rgba(30, 30, 46, 0.5);
}
button {
border-color: #89b4fa;
text-decoration-color: #cdd6f4;
color: #cdd6f4;
background-color: #181825;
border-style: solid;
border-width: 1.5px;
border-radius: 8px;
background-repeat: no-repeat;
background-position: 50% 45%;
background-size: 18%;
margin: 13px;
}
button:focus, button:active, button:hover {
/* 20% Overlay 2, 80% mantle */
background-color: rgb(48, 50, 66);
outline-style: none;
}
#lock {
background-image: url("./icons/lock.svg");
}
#logout {
background-image: url("./icons/logout.svg");
}
#suspend {
background-image: url("./icons/suspend.svg");
}
#hibernate {
background-image: url("./icons/hibernate.svg");
}
#shutdown {
background-image: url("./icons/shutdown.svg");
}
#reboot {
background-image: url("./icons/reboot.svg");
}

15
.config/wofi/config Normal file
View file

@ -0,0 +1,15 @@
hide_scroll=true
prompt=Launch
normal_window=true
no_actions=true
line_wrap=word
show-icons=true
width=550
height=335
allow_images=true
always_parse_args=true
show_all=false
term=kitty
insensitive=true
print_command=true
gtk_dark=false

5
.config/wofi/gruvbox.css Normal file
View file

@ -0,0 +1,5 @@
@define-color accent #BFAA80;
@define-color txt #DDD5C4;
@define-color bg #2C2A24;
@define-color bg2 #3A372F;
@define-color accent2 #8D7AAE;

76
.config/wofi/style.css Normal file
View file

@ -0,0 +1,76 @@
@import '/home/cloverta/.config/wofi/gruvbox.css';
* {
font-family: 'Iosevka Nerd Font mono';
font-size: 14px;
}
/* Window */
window {
margin: 0px;
padding: 10px;
border-radius: 0px;
background-color: @bg;
}
/* Inner Box */
#inner-box {
margin: 5px;
padding: 10px;
border: none;
border-radius: 5px;
background-color: @bg;
}
/* Outer Box */
#outer-box {
margin: 5px;
padding: 10px;
border: none;
background-color: @bg;
border-radius: 5px;
}
/* Scroll */
#scroll {
margin: 0px;
padding: 10px;
border: none;
}
/* Input */
#input {
margin: 10px 25px 10px 25px;
padding: 10px;
color: @accent;
background-color: @bg;
border: 2px solid @accent;
border-radius: 5px;
}
/* Text */
#text {
margin: 5px;
padding: 10px;
border: none;
color: @txt;
}
/* Selected Entry */
#entry:selected {
background-color: @bg;
border: 1px solid @accent2;
border-radius: 5px;
}
#entry:selected #text {
color: @txt;
}
image {
margin-left: 10px;
}
/*entfernen des anderen durch .left oder .right*/
#input > image.right {
-gtk-icon-transform:scaleX(0);
}

218
install.sh Executable file
View file

@ -0,0 +1,218 @@
#!/bin/bash
set -euo pipefail
echo "This Script is used to install this dot from a CLEAN INSTALLATION of ARCH LINUX."
echo "If you have set up Niri/sddm/other DE already, you can simply use the other Script replace.sh."
echo "Note that this Script is more about convience so it might break your own configuration."
echo "Do you wish to continue?[y/N]"
while true; do
read -r yn2 || yn2=""
yn2=${yn2:-n}
case "$yn2" in
[Yy])
echo "Lets GO!"
break
;;
[Nn])
echo "Aborting.."
exit 0
;;
*)
echo "Input y/n (default=n)"
;;
esac
done
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
echo "This Script contains installation, which needs your sudo permission:"
sudo -v
# Print key-value nicely: key: value
kv() {
local k="$1"
shift
local v="${1:-}"
printf "%-14s %s\n" "${k}:" "${v}"
}
# Defaults
ID=""
ID_LIKE=""
NAME=""
PRETTY_NAME=""
VERSION_ID=""
VERSION_CODENAME=""
# 1) Best source: /etc/os-release
if [[ -r /etc/os-release ]]; then
# shellcheck disable=SC1091
. /etc/os-release
ID="${ID:-}"
ID_LIKE="${ID_LIKE:-}"
NAME="${NAME:-}"
PRETTY_NAME="${PRETTY_NAME:-}"
VERSION_ID="${VERSION_ID:-}"
VERSION_CODENAME="${VERSION_CODENAME:-}"
# 2) Fallback: lsb_release
elif command -v lsb_release >/dev/null 2>&1; then
NAME="$(lsb_release -si 2>/dev/null || true)"
VERSION_ID="$(lsb_release -sr 2>/dev/null || true)"
VERSION_CODENAME="$(lsb_release -sc 2>/dev/null || true)"
PRETTY_NAME="${NAME} ${VERSION_ID}"
# 3) Fallback: hostnamectl (systemd)
elif command -v hostnamectl >/dev/null 2>&1; then
# Example line: "Operating System: Arch Linux"
PRETTY_NAME="$(hostnamectl 2>/dev/null | awk -F': ' '/Operating System/ {print $2; exit}' || true)"
# 4) Last resort: /etc/issue
elif [[ -r /etc/issue ]]; then
PRETTY_NAME="$(head -n1 /etc/issue | sed 's/\\.*$//' | xargs || true)"
fi
# Normalize: if PRETTY_NAME empty, try to build
if [[ -z "${PRETTY_NAME}" ]]; then
PRETTY_NAME="${NAME:-Unknown Linux}${VERSION_ID:+ ${VERSION_ID}}"
fi
echo "== Distro info =="
kv "PRETTY_NAME" "${PRETTY_NAME}"
kv "ID" "${ID}"
kv "ID_LIKE" "${ID_LIKE}"
kv "NAME" "${NAME}"
kv "VERSION_ID" "${VERSION_ID}"
kv "CODENAME" "${VERSION_CODENAME}"
# Optional: classify into a "family"
family="unknown"
case "${ID:-}" in
arch | manjaro | endeavouros) family="arch" ;;
ubuntu | debian | linuxmint | pop | elementary | kali) family="debian" ;;
fedora | rhel | centos | rocky | alma | ol) family="rhel" ;;
opensuse* | sles) family="suse" ;;
alpine) family="alpine" ;;
esac
kv "FAMILY" "${family}"
if [ "$family" = "arch" ]; then
echo "This dot recommends you to install the following packages:"
echo "cava fastfetch fuzzel kitty mako niri swaylock-fancy-git swaync waybar wlogout wofi sddm"
echo "Do you wish to install these needed packages? [Y/n]"
while true; do
read -r yn || yn=""
yn=${yn:-y} # 回车默认 y
case "$yn" in
[Yy])
echo "OK, installing..."
aur_helper=""
if command -v yay >/dev/null 2>&1; then
aur_helper="yay"
elif command -v paru >/dev/null 2>&1; then
aur_helper="paru"
fi
if [[ -n "$aur_helper" ]]; then
echo "Found AUR helper: $aur_helper"
else
echo "No AUR helper found (yay/paru). Do you wish to install yay/paru?[y(yay)/p(paru)/n(don't install)]"
while true; do
read -r yn1 || yn1=""
case "$yn1" in
[Yy])
echo "Installing Yay for you..."
sudo pacman -S --needed git base-devel
tmpdir="$(mktemp -d)"
(
trap 'rm -rf "$tmpdir"' EXIT
cd "$tmpdir"
git clone https://aur.archlinux.org/yay-bin.git
cd yay-bin
makepkg -si --noconfirm
)
command -v yay >/dev/null 2>&1 && aur_helper="yay" || aur_helper=""
break
;;
[Pp])
echo "Installing Paru for you..."
sudo pacman -S --needed git base-devel
tmpdir="$(mktemp -d)"
(
trap 'rm -rf "$tmpdir"' EXIT
cd "$tmpdir"
git clone https://aur.archlinux.org/paru.git
cd paru
makepkg -si --noconfirm
)
command -v paru >/dev/null 2>&1 && aur_helper="paru" || aur_helper=""
break
;;
[Nn])
echo "Skipped."
break
;;
*)
echo "Please enter y/p/n."
;;
esac
done
fi
if [[ -z "$aur_helper" ]]; then
echo "Continue installing using pacman, replacing swaylock-fancy with swaylock"
sudo pacman -S --needed cava fastfetch fuzzel kitty mako niri swaylock swaync waybar wlogout wofi sddm
else
echo "Installing packages..."
"$aur_helper" -S --needed cava fastfetch fuzzel kitty mako niri swaylock-fancy-git swaync waybar wlogout wofi sddm
fi
echo "Setting up SDDM..."
sudo systemctl enable --now sddm.service
sudo systemctl set-default graphical.target
bash -c "$(curl -fsSL https://raw.githubusercontent.com/keyitdev/sddm-astronaut-theme/master/setup.sh)" || echo "SDDM theme setup failed, skipped."
mkdir -p ~/.config
shopt -s nullglob dotglob
cp -r "$script_dir/.configs/"* ~/.config/
shopt -u nullglob dotglob
echo "Do you wish to use the grub theme from https://github.com/mateosss/matter?[Y/n]"
while true; do
read -r yn3 || yn3=""
yn3=${yn3:-y}
case "$yn3" in
[Yy])
echo "Setting up grub theme..."
python3 "$script_dir/matter/matter.py"
echo "./matter/matter.py -i(write your params here):"
read -r -a tokens || tokens=()
python3 "$script_dir/matter/matter.py" -i "${tokens[@]}"
break
;;
[Nn])
echo "Skipped."
break
;;
*)
echo "Please Input y/n (default=y)"
;;
esac
done
break
;;
[Nn])
echo "Skipped."
break
;;
*) echo "Please enter y or n (default: y): " ;;
esac
done
else
echo "Your Distro is not supported yet, Please install these needed packages manually:"
echo "cava fastfetch fuzzel kitty mako niri swaylock swaync waybar wlogout wofi sddm"
echo "You can use the replace.sh to replace all the configs after install. :)"
fi

23
matter/LICENSE Normal file
View file

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

3
matter/Matter/README.md Normal file
View file

@ -0,0 +1,3 @@
Based on Vimix by Vince Liuice
https://www.gnu.org/software/grub/manual/grub/html_node/Theme-file-format.html

BIN
matter/Matter/font.pf2 Normal file

Binary file not shown.

View file

@ -0,0 +1 @@
The converted png icons you choose for the --set-icons option will be saved to this folder.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

BIN
matter/Matter/icons/cog.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 980 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

BIN
matter/Matter/select_c.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 B

BIN
matter/Matter/select_e.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

BIN
matter/Matter/select_n.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

BIN
matter/Matter/select_ne.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 B

BIN
matter/Matter/select_nw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 955 B

BIN
matter/Matter/select_s.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

BIN
matter/Matter/select_se.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 955 B

BIN
matter/Matter/select_sw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 960 B

BIN
matter/Matter/select_w.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 952 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 963 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 963 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 952 B

48
matter/Matter/theme.txt Normal file
View file

@ -0,0 +1,48 @@
# If this file is called theme.txt then the theme template has been
# already parsed and the comments below could not make too much sense.
# theme.txt.template represents a python string that gets format()-ed
# Note: for escaping literal curly braces, double them like so: { or }
# Matter Theme File
# Designed for any resolution
# Global Property
title-text: ""
# desktop-image: "background.png"
desktop-color: "#263238"
terminal-font: "Unifont Regular 16" # A smaller font for the console
terminal-box: "terminal_box_*.png"
terminal-left: "0"
terminal-top: "0"
terminal-width: "100%"
terminal-height: "100%"
terminal-border: "0"
# Show the boot menu
+ boot_menu {
left = 36%
top = 29%
width = 28%
height = 60%
item_font = "Josefin Sans Regular 32"
item_color = "#ffffff"
selected_item_color = "#e91e63"
icon_width = 72
icon_height = 72
item_height = 72
item_spacing = 36
selected_item_pixmap_style = "select_*.png"
}
# Show a countdown message using the label component
+ label {
top = 82%
left = 35%
width = 30%
align = "center"
id = "__timeout__"
text = "Booting in %d seconds"
color = "#ffffff"
font = "Josefin Sans Regular 32"
}

397
matter/README.md Normal file
View file

@ -0,0 +1,397 @@
# Matter
Minimalist grub theme originally inspired by material design 2.
![Matter Gif](.docs/matter.gif)
Feel free to open issues for any problem or request you have and/or submit pull
requests.
# Index
- [Matter](#matter)
- **[Download](#download)**
- [Usage](#usage)
- [Help](#help)
- **[Quick Start](#quick-start)**
- [Uninstall](#uninstall)
- [Fonts](#fonts)
- [Colors](#colors)
- [Images](#images-unfinished)
- [Testing Without Rebooting](#testing-without-rebooting)
- [What does Matter do to my system
files?](#what-does-matter-do-to-my-system-files)
- [Gallery](#gallery): [1](#example-1), [2](#example-2), [3](#example-3),
[4](#example-4), [5](#example-5), [6](#example-6), [7](#example-7),
[8](#example-8), [9](#example-9), [10](#example-10), [11](#example-11)
- [Contributing](#contributing)
- [Thanks](#thanks)
# Download
[Click here to download Matter](https://github.com/mateosss/matter/releases/latest/download/matter.zip)
It is **strongly advised** to put the downloaded files in some folder that will
not get deleted, as the main script `matter.py` is needed for future grub
updates made by your system. Also if you want to uninstall matter you could do
it from there as well.
## Dependencies
Matter will inform you of any missing dependencies, but here is a list anyways:
- `inkscape` (**Main dependency**): This brings the `convert` command from
`imagemagick` with best svg to png conversion.
- `grub-mkconfig` and `grub-mkfont`: General grub utilities needed. If you
don't have these, please create an issue with more information about your
system as I've only worked with ones that have these commands.
- `PIL` (Optional): For image conversions with the `--downloadbackground/-dlbg`
option. Can be installed with either of: `pip install Pillow`, `sudo apt
install python3-pil` (Ubuntu), `pacman -S python-pillow` (Arch).
- [`grub2-theme-preview`](https://github.com/hartwork/grub2-theme-preview)
(Optional): For testing results (`--test/-t` argument) without rebooting.
# Usage
## Help
You always can see the command reference with `./matter.py -h`, next up are some
sections that may be useful, or may not be very well documented in the command's
help.
## Quick Start
<details>
<summary>Note for Fedora users (click to show)</summary>
*Matter does not yet support [The Boot Loader
Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION/) so before executing
`matter.py` you should set `GRUB_ENABLE_BLSCFG="false"` in `/etc/default/grub` and then
update your grub file with `sudo grub2-mkconfig -o /boot/grub2/grub.cfg`*
*See [this issue](https://github.com/mateosss/matter/issues/41) for updates
or if you want to help out with fedora support*
</details>
<details>
<summary>Note for users with screen resolution other than 1920x1080 (click to show)</summary>
*Matter layout might break in other resolutions with the default installation,
you might need to tweak the [`grub.template`](grub.template) `GRUB_GFXMODE`
property and [`theme.txt.template`](theme.txt.template) percentages*
*See [this issue](https://github.com/mateosss/matter/issues/4) for more
information.*
</details>
Following is a Matter installation with default values. Don't worry, it is very
easy to rollback or overwrite this installation later if you want to.
The script that does all the work is `matter.py`, so let's start by running it
```sh
./matter.py
```
It outputs almost everything you need to know for later, but for now let's focus
on the list it shows, those are your grub entries. It should look similar to
this one:
```sh
1. Ubuntu
2. Windows
3. More Options
4. Ubuntu, with Linux 5.3.0-61-generic
5. Ubuntu, with Linux 5.3.0-61-generic (recovery mode)
6. Ubuntu, with Linux 5.3.0-59-generic
7. Ubuntu, with Linux 5.3.0-59-generic (recovery mode)
8. System Setup
```
Now you should pick some icons from <https://materialdesignicons.com> for each entry
listed (you only need the icon's name, use the search panel and hover over any
icon you like to see its name). For this example I will pick `ubuntu` for entry
1, `microsoft-windows` for 2, `folder` for 3 (as it is a submenu in my
particular case), and `cog` for 8. I don't care about all the remaining entries
so I will just use "`_`" (underscore) for those.
```sh
# Installs matter with icons matching the corresponding entries
./matter.py -i ubuntu microsoft-windows folder _ _ _ _ cog
```
**And thats it!** If you reboot now, you should get something like this:
![Quick Start Result](.docs/quickstartresult.png)
*Tip: If you need to tidy up your grub entries hierarchy and names I recommend
using [grub-customizer](https://launchpad.net/grub-customizer)
([tutorial](https://vitux.com/how-to-install-grub-customizer-on-ubuntu/))*.
## Uninstall
You can completely remove Matter from your system with `./matter.py -u`
## Fonts
Matter uses `.ttf` fonts and only one, the default, comes prepackaged. You can
specify your own fonts by giving a `.ttf` file, the font name, and an optional
font size like so:
```sh
./matter.py -ff ~/fonts/Cinzel/Cinzel-Regular.ttf -fn Cinzel Regular -fs 40
```
- `--fontfile/-ff`: The `.ttf` path
- `--fontname/-fn`: The name of the font, in this case `Cinzel Regular` but
could be `Open Sans Bold` (*Tip: If you don't know the font name, you can
specify any name, go to the grub, press C to open console, and type `lsfonts`
to list the font names*)
- `--fontsize/-fs`: By default it is 32, recommended values are multiples of 4.
- `--font/-f`: This argument is not used in this example as it is used to select
prepackaged fonts. Note that after giving a ttf file to `-ff`, matter will
save it as a prepackaged font, so it could be referenced later on with this
flag. See prepackaged (available) fonts at the end of `--help/-h` output
*Tip: [Google Fonts](https://fonts.google.com/) is a good place to get fonts*
## Colors
You can specify the color of 4 elements: `--foreground/-fg`, `--background/-bg`,
`--iconcolor/-ic` and `--highlight/-hl` (selected text color), there are some
Material Design colors prepackaged that you can see at the end of the
`--help/-h` output, you can also specify custom colors. Here is an example of
the syntax:
```sh
./matter.py -hl FFC107 -fg white -bg 2196f3 -ic pink
```
## Images (unfinished)
You can specify a background image with `--image/-im`, the supported image
formats/extensions are PNG, JPG, JPEG, and TGA. This feature is considered
*unfinished* because it does not yet work as well as it could *(see
[#58](https://github.com/mateosss/matter/issues/58))*
Here is an example of the syntax:
```sh
./matter.py -im ~/Pictures/some-cool-image-that-has-good-contrast-with-my-text-color.png
```
You can also specify an URL with --downloadbackground/-dlbg to automatically download an image from the internet. The image will be converted to png so it doesn't need to be a grub-compatible 8-bit jpg. It should be in the `jpg` or `png` format though. This feature is also considered *unfinished*.
```sh
./matter.py -dlbg "https://source.unsplash.com/1920x1080/?nature"
```
## Testing Without Rebooting
If you install the `pip` package
[`grub2-theme-preview`](https://github.com/hartwork/grub2-theme-preview) you can
test combinations of fonts and colors with the `--buildonly/-b` and `--test/-t`
flags like so:
```sh
./matter.py -t -b -i ubuntu microsoft-windows folder _ _ _ _ _ _ cog \
-hl FFC107 -fg white -bg 2196F3 \
-ff ~/fonts/MuseoModerno/static/MuseoModerno-Regular.ttf \
-fn MuseoModerno Regular -fs 40
```
*Note: it will use your system's grub.cfg, so set your icons beforehand*.
# What does Matter do to my system files?
Besides the need for the extracted files to be in a persistent location, Matter
needs to edit three files:
1. `/etc/default/grub`: For setting theme and resolution.
2. `/boot/grub/grub.cfg`: For setting icons.
3. `/usr/sbin/grub-mkconfig`: For making icons persistent across grub updates.
4. `/etc/grub.d/99_matter`: For making icons persistent across grub upgrades.
Also it places the theme files in `/boot/grub/themes/Matter/`, this one is
standard to grub themes in general.
Both **(1)** and **(3)** are clearly distinguished with special `BEGIN`/`END`
comments at the end of each file. **(2)** Adds a `--class` flag to each entry,
but it can be restored as new with `update-grub`. And **(4)** is a custom file.
*All of these modifications are **completely** cleaned up by uninstalling*
# Gallery
Here are some examples with their respective commands that you can copy or get
inspired from.
## Example 1
![Example 1](.docs/gallery1.gif)
*Font: [Josefin Sans Regular
400](https://fonts.google.com/specimen/Josefin+Sans)*
```sh
# Light version, invert -fg and -bg for dark one
./matter.py -i ubuntu microsoft-windows folder _ _ _ _ _ _ cog \
-hl ef233c -fg 2b2d42 -bg edf2f4 \
-ff ~/fonts/Josefin_Sans/static/JosefinSans-Regular.ttf \
-fn Josefin Sans Regular -fs 32
```
## Example 2
![Example 2](.docs/gallery2.png)
*Font: [Comfortaa Medium 500](https://fonts.google.com/specimen/Comfortaa)*
```sh
./matter.py -i ubuntu microsoft-windows folder _ _ _ _ _ _ cog \
-hl white -fg f0f0f0 -bg ff0d7b \
-ff ~/fonts/Comfortaa/static/Comfortaa-Medium.ttf \
-fn Comfortaa Regular -fs 32
```
## Example 3
![Example 3](.docs/gallery3.png)
*Font: [Lobster Regular 400](https://fonts.google.com/specimen/Lobster)*
```sh
./matter.py -i ubuntu microsoft-windows folder _ _ _ _ _ _ cog \
-hl 118ab2 -fg ffd166 -bg 073b4c \
-ff ~/fonts/Lobster/Lobster-Regular.ttf \
-fn Lobster Regular -fs 32
```
## Example 4
![Example 4](.docs/gallery4.gif)
*Fonts: [Bebas Neue Regular 400](https://fonts.google.com/specimen/Bebas+Neue)
and [Russo One Regular 400](https://fonts.google.com/specimen/Russo+One)*
```sh
# Using Bebas Neue font (more compact), the other uses Russo One
./matter.py -i ubuntu microsoft-windows folder _ _ _ _ _ _ cog \
-hl 2c251b -fg 2c251b -bg ffe70b \
-ff ~/fonts/Bebas_Neue/BebasNeue-Regular.ttf \
-fn Bebas Neue Regular -fs 36
# -ff ~/fonts/Russo_One/RussoOne-Regular.ttf \
# -fn Russo One Regular -fs 36
```
## Example 5
![Example 5](.docs/gallery5.png)
*Font: [Poiret One Regular 400](https://fonts.google.com/specimen/Poiret+One)*
```sh
./matter.py -i ubuntu microsoft-windows folder _ _ _ _ _ _ cog \
-hl black -fg 101010 -bg fce1e0 \
-ff ~/fonts/Poiret_One/PoiretOne-Regular.ttf \
-fn Poiret One Regular -fs 48
```
## Example 6
![Example 6](.docs/gallery6.png)
*Font: [Josefin Sans Medium
500](https://fonts.google.com/specimen/Josefin+Sans)*
```sh
./matter.py -i ubuntu microsoft-windows folder _ _ _ _ _ _ cog \
-hl ffe78a -fg fdf7f9 -bg 582335 \
-ff ~/fonts/Josefin_Sans/static/JosefinSans-Medium.ttf \
-fn Josefin Sans Regular -fs 32
```
## Example 7
![Example 7](.docs/gallery7.png)
*Font: [Josefin Slab Bold 700](https://fonts.google.com/specimen/Josefin+Slab)*
```sh
./matter.py -i ubuntu microsoft-windows folder _ _ _ _ _ _ cog \
-hl A4E11E -fg white -bg 5b1ee1 \
-ff ~/fonts/Josefin_Slab/JosefinSlab-Bold.ttf \
-fn Josefin Slab Bold -fs 36
```
## Example 8
![Example 8](.docs/gallery8.png)
*Font: [MuseoModerno Regular
400](https://fonts.google.com/specimen/MuseoModerno)*
```sh
./matter.py -i ubuntu microsoft-windows folder _ _ _ _ _ _ cog \
-hl FFC107 -fg white -bg 2196F3 \
-ff ~/fonts/MuseoModerno/static/MuseoModerno-Regular.ttf \
-fn MuseoModerno Regular -fs 32
```
## Example 9
![Example 9](.docs/gallery9.png)
*Font: [Amatic SC Regular 400](https://fonts.google.com/specimen/Amatic+SC)*
```sh
./matter.py -i ubuntu microsoft-windows folder _ _ _ _ _ _ cog \
-bg FFF8E1 -fg 263238 -hl E91E63 \
-ff ~/fonts/Amatic_SC/AmaticSC-Regular.ttf \
-fn Amatic SC Regular -fs 64
```
## Example 10
![Example 10](.docs/gallery10.gif)
*Font: [Cinzel Regular 400](https://fonts.google.com/specimen/Cinzel)*
```sh
# This is the light version, the dark one uses -bg 1a1d21 -fg c9a45b instead
./matter.py -i ubuntu microsoft-windows folder _ _ _ _ _ _ cog \
-hl c28f2c -bg white -fg d0a85c \
-ff ~/fonts/Cinzel/Cinzel-Regular.ttf \
-fn Cinzel Regular -fs 40
# -hl c28f2c -bg 1a1d21 -fg c9a45b \
```
## Example 11
![Example 11](.docs/gallery11.png)
```sh
./matter.py -i ubuntu microsoft-windows folder _ _ _ _ _ _ cog \
-im ~/images/material-background.png
```
# Contributing
Feel free to submit any pull request that improves in any way the project.
Read the wiki <https://github.com/mateosss/matter/wiki>, that's where any useful
information for developers will reside.
If you think you got a nice result out of Matter and would like to share it,
please create an issue with it! I would love to see your results.
# Thanks
- Originally inspired by <https://github.com/vinceliuice/grub2-themes>
- Icons from <https://materialdesignicons.com/>
- Fonts mainly from <https://fonts.google.com>

Binary file not shown.

Binary file not shown.

1
matter/bg/README.md Normal file
View file

@ -0,0 +1 @@
The background images you choose for the --downloadbackground option will be saved to this folder.

1
matter/config.json Normal file
View file

@ -0,0 +1 @@
{"icons": {"Arch Linux, with Linux linux": "arch", "UEFI Firmware Settings": "cog", "Windows 11 (EFI)": "microsoft-windows"}}

Binary file not shown.

Binary file not shown.

1
matter/fonts/README.md Normal file
View file

@ -0,0 +1 @@
Preloaded Matter fonts, for adding a new one update FONTS dict in matter.py

6
matter/grub.template Normal file
View file

@ -0,0 +1,6 @@
GRUB_THEME={installation_dir}/theme.txt
GRUB_GFXMODE=1920x1080,auto
# Fedora specific fixes
GRUB_ENABLE_BLSCFG=false
GRUB_TERMINAL_OUTPUT=""

View file

@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""
This template file goes into /etc/grub.d/99_matter
It checks that the theme is still hooked to grub-mkconfig.
Hooks it back again if not.
This is a template file, be careful of your use of curly braces. Use {{ and }} instead.
# About /etc/grub.d
Files in /etc/grub.d run in the order of their prefix number.
We use the 99 number because the hook should be run after the entire grub.cfg file is generated.
Everything echoed to stdout is written to /boot/grub/grub.cfg after the previous files.
Everything echoed to stderr is shown to the user wherever there is a grub update.
This script does not write to stdout.
More @ https://www.gnu.org/software/grub/manual/grub/grub.html#Simple-configuration
# Why this script exists?
Files in /etc/grub.d are not erased after your distro upgrades the grub package.
As this theme relies on being hooked to the grub-mkconfig file (i.e. executed by it)
and grub package upgrades restore the mkconfig file, this file detects
when the grub-mkconfig file has been restored and then hook the theme back onto it.
"""
import re
from subprocess import run
from sys import stderr
GRUB_MKCONFIG_PATH = "{GRUB_MKCONFIG_PATH}"
THEME_NAME = "{THEME_NAME}"
THEME_OVERRIDES_TITLE = "{THEME_OVERRIDES_TITLE}"
BEGIN_THEME_OVERRIDES = "{BEGIN_THEME_OVERRIDES}"
END_THEME_OVERRIDES = "{END_THEME_OVERRIDES}"
SETICONS_CALL = "{SETICONS_CALL}"
cyan = "\033[36m"
pink = "\033[38;5;206m"
endcolor ="\033[0m"
def info(msg):
# info with cyan [I]
print(f"{{cyan}}[I]{{endcolor}} {{msg}}", file=stderr)
def sh(command):
"Executes command in shell and returns its exit status"
return run(command, shell=True).returncode
info(f"{{pink}}[Matter]{{endcolor}} Setting entry icons")
with open(GRUB_MKCONFIG_PATH, "r", newline="") as f:
grub_mkconfig = f.read()
theme_overrides = f"\n*{{BEGIN_THEME_OVERRIDES}}.*{{END_THEME_OVERRIDES}}\n*"
theme_hooked = re.search(theme_overrides, grub_mkconfig, flags=re.DOTALL) is not None
if theme_hooked:
info(f"Found {{GRUB_MKCONFIG_PATH}} hook")
exit(0)
info(f"Hook back onto {{GRUB_MKCONFIG_PATH}}")
grub_mkconfig += (
f"\n\n{{BEGIN_THEME_OVERRIDES}}\n{{SETICONS_CALL}}\n{{END_THEME_OVERRIDES}}\n\n"
)
with open(GRUB_MKCONFIG_PATH, "w") as f:
f.write(grub_mkconfig)
info(
f"{{GRUB_MKCONFIG_PATH}} successfully patched, icons will now persist between grub updates."
)
# Lastly call the hook as mkconfig is currently running and should be loaded in memory.
# NOTE: Some bash interpreters seem to not load the entire script in memory
# so appending commands to the end of the script at runtime will indeed make
# mkconfig run them. That's what's happening is you see the
# 'grub.cfg icons patched' message twice
sh(SETICONS_CALL)

1
matter/icons/README.md Normal file
View file

@ -0,0 +1 @@
The icons svg you choose for the --set-icons option will be saved to this folder.

1
matter/icons/arch.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" id="mdi-arch" viewBox="0 0 24 24"><path d="M12,2C11.11,4.18 10.57,5.61 9.58,7.73C10.19,8.37 10.93,9.12 12.14,9.97C10.84,9.43 9.95,8.9 9.29,8.34C8,11 6.03,14.75 2,22C5.17,20.17 7.63,19.04 9.92,18.61C9.82,18.19 9.76,17.73 9.77,17.25V17.15C9.82,15.12 10.88,13.56 12.13,13.67C13.38,13.77 14.35,15.5 14.3,17.54C14.29,17.92 14.25,18.29 14.18,18.63C16.44,19.07 18.87,20.19 22,22C21.38,20.86 20.83,19.84 20.31,18.87C19.5,18.23 18.61,17.39 16.85,16.5C18.06,16.8 18.93,17.16 19.61,17.57C14.26,7.62 13.83,6.3 12,2Z" /></svg>

After

Width:  |  Height:  |  Size: 553 B

1
matter/icons/cog.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" id="mdi-cog" viewBox="0 0 24 24"><path d="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z" /></svg>

After

Width:  |  Height:  |  Size: 999 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" id="mdi-microsoft-windows" viewBox="0 0 24 24"><path d="M3,12V6.75L9,5.43V11.91L3,12M20,3V11.75L10,11.9V5.21L20,3M3,13L9,13.09V19.9L3,18.75V13M20,13.25V22L10,20.09V13.1L20,13.25Z" /></svg>

After

Width:  |  Height:  |  Size: 228 B

882
matter/matter.py Executable file
View file

@ -0,0 +1,882 @@
#!/usr/bin/python3
# Standard library modules
import sys
import os
import re
import json
import argparse
import urllib.request as request
from urllib.error import HTTPError, URLError
from argparse import ArgumentParser, RawTextHelpFormatter
from os.path import dirname, basename, isdir, exists
from shutil import which, rmtree, copytree, copyfile
try:
from PIL import Image
except:
has_PIL = False
else:
has_PIL = True
# Local Matter modules
from utils import *
from svg2png import inkscape_convert_svg2png, magick_convert_svg2png
# Configuration constants
MIN_PYTHON_VERSION = (3, 6) # Mainly for f-strings
THEME_NAME = "Matter"
THEME_DESCRIPTION = (
"Matter is a minimalist grub theme originally inspired by material design 2.\n"
"Run this script without arguments for next steps on installing Matter."
)
if exists("/boot/grub"):
BOOT_GRUB_PATH = "/boot/grub"
elif exists("/boot/grub2"):
BOOT_GRUB_PATH = "/boot/grub2"
else:
error("Could not find your grub's boot path (tried /boot/grub and /boot/grub2)")
INSTALLER_ABSPATH = os.path.abspath(__file__)
INSTALLER_NAME = basename(INSTALLER_ABSPATH)
INSTALLER_DIR = dirname(INSTALLER_ABSPATH)
INSTALLATION_SOURCE_DIR = f"{INSTALLER_DIR}/{THEME_NAME}"
INSTALLATION_TARGET_DIR = f"{BOOT_GRUB_PATH}/themes/{THEME_NAME}"
THEME_DEFAULT_HIGHLIGHT = "pink"
THEME_DEFAULT_FOREGROUND = "white"
THEME_DEFAULT_BACKGROUND = "bluegrey-900"
THEME_DEFAULT_FONT_NAME = "Josefin Sans Regular"
THEME_DEFAULT_FONT = THEME_DEFAULT_FONT_NAME.replace(" ", "_")
THEME_DEFAULT_FONT_SIZE = 32
GRUB_DEFAULTS_PATH = "/etc/default/grub"
GRUB_SCRIPTS_PATH = "/etc/grub.d"
GRUB_CFG_PATH = f"{BOOT_GRUB_PATH}/grub.cfg"
GRUB_MKCONFIG_PATH = which("grub-mkconfig") or which("grub2-mkconfig")
if GRUB_MKCONFIG_PATH is None:
error("Could not find grub-mkconfig command file (grub2-mkconfig neither)")
THEME_TEMPLATE_PATH = f"{INSTALLER_DIR}/theme.txt.template"
GRUB_DEFAULTS_TEMPLATE_PATH = f"{INSTALLER_DIR}/grub.template"
HOOKCHECK_TEMPLATE_PATH = f"{INSTALLER_DIR}/hookcheck.py.template"
THEME_OVERRIDES_TITLE = f"{THEME_NAME} Theme Overrides"
BEGIN_THEME_OVERRIDES = f"### BEGIN {THEME_OVERRIDES_TITLE}"
END_THEME_OVERRIDES = f"### END {THEME_OVERRIDES_TITLE}"
ICON_SVG_PATHF = f"{INSTALLER_DIR}/icons/{{}}.svg"
ICON_PNG_PATHF = f"{INSTALLATION_SOURCE_DIR}/icons/{{}}.png"
BACKGROUND_TMP_PATHF = f"{INSTALLER_DIR}/bg/{{}}.tmp"
BACKGROUND_PNG_PATHF = f"{INSTALLER_DIR}/bg/{{}}.png"
CONFIG_FILE_PATH = f"{INSTALLER_DIR}/config.json"
PALETTE = {
"red": "f44336",
"pink": "e91e63",
"purple": "9c27b0",
"deeppurple": "673ab7",
"indigo": "3f51b5",
"blue": "2196f3",
"lightblue": "03a9f4",
"cyan": "00bcd4",
"teal": "009688",
"green": "4caf50",
"lightgreen": "8bc34a",
"lime": "cddc39",
"yellow": "ffeb3b",
"amber": "ffc107",
"orange": "ff9800",
"deeporange": "ff5722",
"brown": "795548",
"grey": "9e9e9e",
"bluegrey": "607d8b",
"white": "ffffff",
"black": "000000",
# Custom default colors
"white-350": "9E9E9E",
"bluegrey-900": "263238",
}
AVAILABLE_COLORS = list(PALETTE.keys())
MDI_CDN = "https://raw.githubusercontent.com/Templarian/MaterialDesign-SVG/master/svg/"
# Global user arguments set in main()
user_args: argparse.Namespace
# Utils
def check_python_version():
installed = (sys.version_info.major, sys.version_info.minor)
required = MIN_PYTHON_VERSION
if installed < required:
error(f"Python {required[0]}.{required[1]} or later required")
def check_root_or_prompt():
if os.geteuid() != 0:
info("Request root access")
exit_code = sh("sudo -v")
if exit_code != 0:
error("Could not verify root access, you could try with sudo")
# Relaunch the program with sudo
args = " ".join(sys.argv[1:])
child_exit_code = sh(f"sudo {INSTALLER_DIR}/{INSTALLER_NAME} {args}")
exit(child_exit_code) # Propagate exit code
def delete_dir(directory):
if isdir(directory):
rmtree(directory)
def read_cleaned_grub_defaults():
# Read previous defaults
with open(GRUB_DEFAULTS_PATH, "r", newline="") as f:
grub_defaults = f.read()
# Remove previous theme defaults
cleaned_grub_defaults = re.sub(
f"\n*{BEGIN_THEME_OVERRIDES}.*{END_THEME_OVERRIDES}\n*",
"",
grub_defaults,
flags=re.DOTALL,
)
return cleaned_grub_defaults
def read_cleaned_grub_mkconfig():
# Read previous defaults
with open(GRUB_MKCONFIG_PATH, "r", newline="") as f:
grub_mkconfig = f.read()
# Remove previous theme defaults
cleaned_grub_mkconfig = re.sub(
f"\n*{BEGIN_THEME_OVERRIDES}.*{END_THEME_OVERRIDES}\n*",
"",
grub_mkconfig,
flags=re.DOTALL,
)
return cleaned_grub_mkconfig
def download_icon(icon_name):
info(f"Download {icon_name}.svg")
url = f"{MDI_CDN}{icon_name}.svg"
try:
with request.urlopen(url) as f:
response = f.read()
except HTTPError as err: # A subclass of URLError
error(f"Couldn't get icon {icon_name} ({err.reason})", f"At URL {err.geturl()}")
except URLError as err:
error(f"Couldn't get icon {icon_name} ({err.reason})")
svg_path = ICON_SVG_PATHF.format(icon_name)
with open(svg_path, "wb") as f:
f.write(response)
return svg_path
def download_background(background_path):
if not has_PIL:
error("PIL not detected, cannot download background")
info(f"Downloading background image")
url = f"{background_path}"
try:
with request.urlopen(url) as f:
response = f.read()
except HTTPError as err: # A subclass of URLError
error(f"Couldn't get background image ({err.reason})", f"At URL {err.geturl()}")
except URLError as err:
error(f"Couldn't get background image ({err.reason})")
bg_path = BACKGROUND_TMP_PATHF.format('background_image')
conv_path = BACKGROUND_PNG_PATHF.format('background_image')
with open(bg_path, "wb") as f:
f.write(response)
im = Image.open(bg_path)
im.save(conv_path)
return conv_path
def get_converted_icons():
return [
filename[:-4] # Remove .png
for filename in os.listdir(f"{INSTALLATION_SOURCE_DIR}/icons/")
if filename.endswith(".png")
]
def is_icon_downloaded(icon_name):
svg_path = ICON_SVG_PATHF.format(icon_name)
return exists(svg_path)
def convert_icon_svg2png(icon_name, whisper=False):
if not has_command("inkscape"):
if not has_command("convert"):
error(
"Stop. Both `inkscape` and `convert` command from imagemagick were not found",
"Consider installing `inkscape` for the best results",
)
else:
command = "convert"
else:
command = "inkscape"
color = (
parse_color(user_args.iconcolor)
if user_args.iconcolor
else parse_color(user_args.foreground)
)
src_path = ICON_SVG_PATHF.format(icon_name)
dst_path = ICON_PNG_PATHF.format(icon_name)
if command == "convert":
warning("Resulting icons could look a bit off, consider installing inkscape")
converter = magick_convert_svg2png
elif command == "inkscape":
converter = inkscape_convert_svg2png
exit_code = converter(color, src_path, dst_path, whisper=whisper)
if exit_code != 0:
error(f"Stop. The `{command}` command returned an error")
def get_available_fonts():
"Returns the fonts present in /fonts"
return [
filename[:-4] # Remove .ttf
for filename in os.listdir(f"{INSTALLER_DIR}/fonts/")
if filename.endswith(".ttf")
]
def parse_color(color_string):
if color_string in AVAILABLE_COLORS:
color = PALETTE[color_string]
elif re.match(r"[0-9A-Fa-f]{6}", color_string) is not None:
color = color_string
else:
error(
f"Invalid color parsed from {color_string}",
f"Color must be an hex code like C00FFE or one of: {AVAILABLE_COLORS}",
)
return f"#{color}"
def check_icon_converted(icon):
available_icons = get_converted_icons()
if icon not in available_icons + ["_"]:
error(f"Invalid icon name: {icon}", f"Icons present are: {available_icons}")
return icon
def parse_font(font):
"""From a given --font check if available and return its font name
e.g. Open_Sans_Regular to Open Sans Regular"""
available_fonts = get_available_fonts()
if font not in available_fonts:
error(
f"Invalid font name: {font}", f"Available font names: {available_fonts}",
)
return font.replace("_", " ")
# Procedures
def clean_install_dir():
info("Clean install directory")
if isdir(INSTALLATION_TARGET_DIR):
rmtree(INSTALLATION_TARGET_DIR)
def prepare_source_dir():
info("Build theme from user preferences")
# Get user color preferences
highlight = parse_color(user_args.highlight)
foreground = parse_color(user_args.foreground)
background = parse_color(
THEME_DEFAULT_BACKGROUND
if user_args.background is None
else user_args.background
)
image = (
user_args.image
if user_args.downloadbackground is None
else download_background(user_args.downloadbackground)
)
fontkey = user_args.font
fontfile = user_args.fontfile
fontname = user_args.fontname
fontsize = user_args.fontsize
icons = user_args.icons
# Image checks
if image:
if not exists(image):
error(f"{image} does not exist")
if os.path.splitext(image)[1] not in (".png", ".jpg", ".jpeg", ".tga"):
error("Background image must be one of .png, .jpg, .jpeg or .tga formats.")
image_name = basename(image)
copyfile(image, f"{INSTALLATION_SOURCE_DIR}/{image_name}")
if user_args.background:
warning(
f"Both --background and --image arguments specified. Background color {background} will be ignored."
)
else:
image_name = "background.png"
# Icon checks
# Get entries from grub.cfg
entries = get_entry_names()
# Do icon count match grub entry count?
if len(icons) != len(entries):
error(
f"You must specify {len(entries)} icons ({len(icons)} provided) for entries:",
should_exit=False,
)
for i, m in enumerate(entries):
print(f"{i + 1}. {m['entryname']}")
exit(1)
# Font checks
# grub-mkfont present
grub_mkfont = which("grub-mkfont") or which("grub2-mkfont")
if grub_mkfont is None:
error(f"grub-mkfont command not found in your system (grub2-mkfont neither)")
# Valid font arguments
if fontfile is None: # User did not specify custom font file
fontfile = f"{INSTALLER_DIR}/fonts/{fontkey}.ttf"
fontname = f"{parse_font(fontkey)} {fontsize}"
elif not fontfile.endswith(".ttf"): # font file is not ttf
error(f"{fontfile} is not a .ttf file")
elif fontname is None: # User did, but did not gave its name
error(
"--fontname/-fn not specified for given font file.",
"e.g. 'Open Sans Regular'",
)
else: # User specified a custom fontfile
fontname = " ".join(fontname) # e.g. Open Sans Regular
dst_fontfile = f"{INSTALLER_DIR}/fonts/{fontname.replace(' ', '_')}.ttf" # e.g. .../Open_Sans_Regular.ttf
copyfile(fontfile, dst_fontfile)
fontfile = dst_fontfile
fontname = f"{fontname} {fontsize}" # e.g. Open Sans Regular 32
# Prepare Icons
# Download not-yet-downloaded icons
for icon in icons:
if not is_icon_downloaded(icon) and icon != "_":
download_icon(icon)
# Convert icons
info("Convert icons")
for i, icon in enumerate(icons):
if icon != "_":
if i == 0:
convert_icon_svg2png(icon)
else:
convert_icon_svg2png(icon, whisper=True)
# Prepare Font
# Generate font file
info("Build font")
stdout = shout(
f"{grub_mkfont} -o {INSTALLATION_SOURCE_DIR}/font.pf2 {fontfile} -s {fontsize}"
)
if stdout:
error(
f"{grub_mkfont} execution was not clean", f"for fontfile: {fontfile}",
)
# Prepare Theme.txt
# Parse theme template with user preferences
with open(THEME_TEMPLATE_PATH, "r", newline="") as f:
template = f.read()
context = {
"theme_name": THEME_NAME,
"highlight": highlight,
"foreground": foreground,
"background": background,
"image_name": image_name,
"fontname": fontname,
}
parsed_theme = template.format(**context)
if image:
parsed_theme = parsed_theme.replace("# desktop-image", "desktop-image")
theme_file_path = f"{INSTALLATION_SOURCE_DIR}/theme.txt"
with open(theme_file_path, "w") as f:
f.write(parsed_theme)
def prepare_target_dir():
info("Prepare installation directory")
clean_install_dir()
def copy_source_to_target():
info("Copy built theme to installation directory")
copytree(INSTALLATION_SOURCE_DIR, INSTALLATION_TARGET_DIR)
def update_grub_cfg():
info("Update grub.cfg")
update_command = (
which("update-grub") or which("grub-mkconfig") or which("grub2-mkconfig")
)
if update_command is None:
error(
f"Command for generating grub.cfg not found (tried update-grub, grub-mkconfig and grub2-mkconfig)"
)
command = f"{update_command} -o {GRUB_CFG_PATH}"
info(f"Remake grub.cfg with {command}")
sh(command)
def update_grub_defaults():
info(f"Patch {GRUB_DEFAULTS_PATH} with {THEME_OVERRIDES_TITLE}")
grub_configs = read_cleaned_grub_defaults()
# Parse grub defaults template, append parsed contents, and write back
with open(GRUB_DEFAULTS_TEMPLATE_PATH, "r", newline="") as f:
template = f.read()
context = {"installation_dir": INSTALLATION_TARGET_DIR}
parsed_extra_grub = template.format(**context)
grub_configs += (
f"\n\n{BEGIN_THEME_OVERRIDES}\n{parsed_extra_grub}\n{END_THEME_OVERRIDES}\n\n"
)
with open(GRUB_DEFAULTS_PATH, "w") as f:
f.write(grub_configs)
def clean_grub_defaults():
info(f"Clean {THEME_OVERRIDES_TITLE} from {GRUB_DEFAULTS_PATH}")
cleaned_grub_defaults = read_cleaned_grub_defaults()
with open(GRUB_DEFAULTS_PATH, "w") as f:
f.write(cleaned_grub_defaults)
def clean_grub_mkconfig():
info(f"Clean {THEME_OVERRIDES_TITLE} from {GRUB_MKCONFIG_PATH}")
cleaned_grub_mkconfig = read_cleaned_grub_mkconfig()
with open(GRUB_MKCONFIG_PATH, "w") as f:
f.write(cleaned_grub_mkconfig)
def clean_hookcheck():
info(f"Remove hookcheck script from {GRUB_SCRIPTS_PATH}")
hookcheck = f"{GRUB_SCRIPTS_PATH}/99_matter"
if exists(hookcheck):
os.remove(hookcheck)
def get_entry_names():
"Gets the entry names from grub.cfg contents"
with open(GRUB_CFG_PATH, "r", newline="") as f:
grub_cfg = f.read()
pattern = (
r"(?P<head>(?:submenu|menuentry) ?)" # menuentry or submenu
r"(?:\"|')" # " or '
r"(?P<entryname>[^\"']*)" # capture the entry name (without quotes)
r"(?:\"|')" # " or '
r"(?P<tail>[^\{]*\{)" # The rest of the entry header until a { is found
)
matchiter = re.finditer(pattern, grub_cfg)
matches = list(matchiter)
return matches
# Main procedures
def do_preinstall_hint():
info(
f"{color_string('[ ', fg='brightwhite')}"
f"{color_string(THEME_NAME, fg='brightmagenta')} "
f"{color_string('Grub Theme'.upper(), fg='lightcyan')}"
f"{color_string(' ]', fg='brightwhite')}"
)
info("Argument -i required. Which icons go to which grub entries?")
info("Your grub entries are:")
do_list_grub_cfg_entries()
info("Look for icons you like at https://materialdesignicons.com/")
info("Then install with:")
info("./matter.py -i icon-for-entry-1 icon-for-entry-2 ...")
info("Example (with 8 entries, _ means ignore):")
info("./matter.py -i ubuntu microsoft-windows folder _ _ _ _ cog")
def do_test():
info("Begin grub2-theme-preview")
warning(
"Argument --icons/-i does not have effect when testing",
"The icon names used are coming from your system's current grub.cfg",
"This is a feature that may work in the future",
)
if not has_command("grub2-theme-preview"):
error(
"You need grub2-theme-preview for testing",
"See https://github.com/hartwork/grub2-theme-preview",
)
sh(f"grub2-theme-preview {INSTALLATION_SOURCE_DIR}")
def do_install():
info(f"Begin {THEME_NAME} install")
prepare_source_dir()
check_root_or_prompt()
prepare_target_dir()
copy_source_to_target()
update_grub_defaults()
do_set_icons(patch_grubcfg=True)
install_hookcheck()
update_grub_cfg()
info(f"{THEME_NAME} successfully installed")
def do_uninstall():
info(f"Begin {THEME_NAME} uninstall")
check_root_or_prompt()
clean_grub_defaults()
clean_grub_mkconfig()
clean_hookcheck()
clean_install_dir()
update_grub_cfg()
info(f"{THEME_NAME} successfully uninstalled")
def do_list_grub_cfg_entries():
# Read current grub cfg
with open(GRUB_CFG_PATH, "r", newline="") as f:
grub_cfg = f.read()
entries = get_entry_names()
for i, m in enumerate(entries):
print(f"{i + 1}. {m['entryname']}")
def create_config_file():
icons = user_args.icons
# Read current grub cfg
entries = get_entry_names()
entries_to_icons = {}
for icon, entry in zip(icons, entries):
entryname = entry.group("entryname")
if entryname in entries_to_icons:
warning(f"Duplicate entry '{entryname}'. Unexpected behaviour may occur. Consider changing names using Grub Customizer.")
entries_to_icons[entryname] = icon
config = {"icons": entries_to_icons}
with open(CONFIG_FILE_PATH, 'w') as f:
f.write(json.dumps(config))
def patch_from_config_file():
# Read current grub cfg
current_entries = get_entry_names()
with open(CONFIG_FILE_PATH) as f:
config = json.loads(f.read())
entries_to_icons = config["icons"]
icons = []
for entry in current_entries:
entryname = entry.group("entryname")
if entryname in entries_to_icons:
icons.append(entries_to_icons[entryname])
else:
warning(
f"{entryname} is a new grub menu entry, no icon will be set for it. "
f"Rerun matter.py to set icons"
)
icons.append("_")
do_patch_grub_cfg_icons(icons)
def do_patch_grub_cfg_icons(icons):
info(f"Begin {GRUB_CFG_PATH} patch")
with open(GRUB_CFG_PATH, "r", newline="") as f:
grub_cfg = f.read()
# Read current grub cfg
entries = get_entry_names()
# Build new grub cfg with given icons
new_grub_cfg = ""
next_seek = 0
for m, i in zip(entries, icons):
mstart, mend = m.span()
new_grub_cfg += grub_cfg[next_seek:mstart]
icon_class = f" --class {i} " if i != "_" else ""
new_grub_cfg += f'{m["head"]}"{m["entryname"]}"{icon_class}{m["tail"]}'
next_seek = mend
new_grub_cfg += grub_cfg[mend:]
# Write new grub cfg back
check_root_or_prompt()
with open(GRUB_CFG_PATH, "w") as f:
f.write(new_grub_cfg)
info(f"{len(icons)} icons successfully patched onto {GRUB_CFG_PATH}")
def do_set_icons(patch_grubcfg):
icons = user_args.icons
if icons is None:
error("Stop. Unspecified icons (--icons/-i argument)")
icons = [check_icon_converted(i) for i in icons]
# Read current grub cfg
entries = get_entry_names()
if len(icons) != len(entries):
error(
f"You must specify {len(entries)} "
f"icons ({len(icons)} provided) for entries:",
should_exit=False,
)
for i, m in enumerate(entries):
print(f"{i + 1}. {m['entryname']}")
# NOTE: We exit with 0 here to not stop the apt upgrade process
# eventually it will be solved with an autoremove
exit(0)
do_patch_grub_cfg_icons(user_args.icons)
if patch_grubcfg:
create_config_file()
# Patch grub-mkconfig so everytime it executes, it patches grub.cfg
info(f"Begin {GRUB_MKCONFIG_PATH} patch")
info(f"Clean old {GRUB_MKCONFIG_PATH} patch if any")
# cmd_icons = " ".join(user_args.icons)
# seticons_call = f"{INSTALLER_DIR}/{INSTALLER_NAME} -so -i {cmd_icons} >&2"
seticons_call = f"{INSTALLER_DIR}/{INSTALLER_NAME} --configicons >&2"
new_grub_mkconfig = read_cleaned_grub_mkconfig()
# grub-mkconfig is called on upgrade, and on failure it halts.
# A failure on our part should not halt an upgrade, let's temporarily
# disable the stop-on-error functionality with set +e. See #67
new_grub_mkconfig += (
f"\n\n{BEGIN_THEME_OVERRIDES}\n"
f"set +e\n"
f"{seticons_call}\n"
f"set -e\n"
f"{END_THEME_OVERRIDES}\n\n"
)
check_root_or_prompt()
with open(GRUB_MKCONFIG_PATH, "w") as f:
f.write(new_grub_mkconfig)
info(
f"{GRUB_MKCONFIG_PATH} successfully patched, icons will now persist between grub updates."
)
def install_hookcheck():
info(f"Create hook check script")
with open(HOOKCHECK_TEMPLATE_PATH, "r", newline="") as f:
template = f.read()
cmd_icons = " ".join(user_args.icons)
# seticons_call = f"{INSTALLER_DIR}/{INSTALLER_NAME} -so -i {cmd_icons} >&2"
seticons_call = f"{INSTALLER_DIR}/{INSTALLER_NAME} --configicons"
context = {
"GRUB_MKCONFIG_PATH": GRUB_MKCONFIG_PATH,
"THEME_NAME": THEME_NAME,
"THEME_OVERRIDES_TITLE": THEME_OVERRIDES_TITLE,
"BEGIN_THEME_OVERRIDES": BEGIN_THEME_OVERRIDES,
"END_THEME_OVERRIDES": END_THEME_OVERRIDES,
"SETICONS_CALL": seticons_call,
}
parsed_script = template.format(**context)
script_file_path = f"{GRUB_SCRIPTS_PATH}/99_matter"
with open(script_file_path, "w") as f:
f.write(parsed_script)
# Make it executable by user, group and others
st = os.stat(script_file_path)
os.chmod(script_file_path, st.st_mode | 0o111)
# Script arguments
def parse_args():
parser = ArgumentParser(
description=THEME_DESCRIPTION,
epilog=f"[Available colors] are: {', '.join(AVAILABLE_COLORS)}.\n"
"You can specify your own hex colors as well (e.g. C0FFEE, FF00FF, etc).\n"
f"[Available fonts] are: {', '.join(get_available_fonts())}\n"
"You can always specify your own with the -ff argument\n"
f"[Available icons] can be found at https://materialdesignicons.com/\n"
"For requests open an issue on:\n"
"https://github.com/mateosss/matter/issues",
formatter_class=RawTextHelpFormatter,
)
parser.add_argument(
"--listentries", "-l", action="store_true", help=f"list grub entries",
)
parser.add_argument(
"--buildonly",
"-b",
action="store_true",
help=f"prepare the theme but do not install it",
)
parser.add_argument(
"--test",
"-t",
action="store_true",
help=f"test the generated theme with grub2-theme-preview",
)
parser.add_argument(
"--icons",
"-i",
type=str,
nargs="*",
help=f"specify icons for each grub entry listed with -l",
)
parser.add_argument(
"--seticons",
"-si",
action="store_true",
help=f"set grub entries icons given by -i and patch grub-mkconfig for persistence",
)
parser.add_argument(
"--seticons_once",
"-so",
action="store_true",
help=f"set grub entries icons given by -i, will be reverted on next grub update",
)
parser.add_argument(
"--uninstall", "-u", action="store_true", help=f"uninstall {THEME_NAME}",
)
parser.add_argument(
"--highlight",
"-hl",
type=str,
help=f"selected text color",
default=THEME_DEFAULT_HIGHLIGHT,
)
parser.add_argument(
"--foreground",
"-fg",
type=str,
help=f"main text color",
default=THEME_DEFAULT_FOREGROUND,
)
parser.add_argument(
"--background",
"-bg",
type=str,
help=f"solid background color",
default=None,
# default will be set to THEME_DEFAULT_BACKGROUND
)
parser.add_argument(
"--image",
"-im",
type=str,
help=f"image file to use as background, supported extensions: PNG, JPG, JPEG, TGA",
)
parser.add_argument(
"--iconcolor",
"-ic",
type=str,
help=f"icons fill color, by default same as foreground",
)
parser.add_argument(
"--font",
"-f",
type=str,
help=f"theme font from already downloaded fonts",
default=THEME_DEFAULT_FONT,
choices=get_available_fonts(),
)
parser.add_argument(
"--fontfile", "-ff", type=str, help=f"import theme font from custom .ttf file"
)
parser.add_argument(
"--fontname",
"-fn",
type=str,
nargs="*",
help=f"specify the font name for the given font file",
)
parser.add_argument(
"--fontsize",
"-fs",
type=int,
help=f"theme font size",
default=THEME_DEFAULT_FONT_SIZE,
)
parser.add_argument(
"--configicons",
"-ci",
action="store_true",
help="set grub entries icons using config file. "
)
parser.add_argument(
"--downloadbackground",
"-dlbg",
type=str,
help=f"download the background image from the given url",
)
return parser.parse_args()
if __name__ == "__main__":
try:
check_python_version()
user_args = parse_args()
if user_args.listentries:
do_list_grub_cfg_entries()
elif user_args.buildonly:
prepare_source_dir()
elif user_args.seticons_once:
do_set_icons(patch_grubcfg=False)
elif user_args.seticons:
do_set_icons(patch_grubcfg=True)
elif user_args.uninstall:
do_uninstall()
elif user_args.configicons:
patch_from_config_file()
elif user_args.icons is None:
do_preinstall_hint()
else:
do_install()
if user_args.test:
do_test()
except KeyboardInterrupt:
error("Stop. Script halted by user")
sys.exit(1)

117
matter/svg2png.py Executable file
View file

@ -0,0 +1,117 @@
#!/usr/bin/env python3
import os
import re
import xml.etree.ElementTree as ET
import xml.dom.minidom
# Local Matter modules
from utils import sh, shout, error
def inkscape_convert_svg2png(color, src_path, dst_path, whisper=False):
# SVG_URI = "http://www.w3.org/2000/svg"
FRAC = 0.6
TEMPFILE = "temp.svg"
def parse_with_map(source):
"""Parses file, returns a tuple containing the parsed ElementTree and a namespace map (dict).
The ElementTree object returned is the same as if parsed using xml.etree.ElementTree.parse. For
some reason, ElementTree objects by the xml package will not provide a namespace map, unlike the
lxml package.
"""
root = None
ns_map = []
for event, node in ET.iterparse(source, events=["start-ns", "start"]):
if event == "start-ns":
ns_map.append(node)
elif event == "start":
if root is None:
root = node
return (ET.ElementTree(root), dict(ns_map))
def int_ignore_units(s):
return int("".join(ch for ch in s if ch.isdigit()))
def prettify(xml_string):
return "\n".join(
line
for line in xml.dom.minidom.parseString(xml_string)
.toprettyxml(indent=" ")
.splitlines()
if not line.isspace() and line != ""
)
# Fixes undefined namespace tags in output xml (not a big issue)
dom, ns_map = parse_with_map(src_path)
for key, value in ns_map.items():
ET.register_namespace(key, value)
root = dom.getroot()
_, _, width, height = map(int, filter(bool, re.split(r'[ ,]', root.attrib["viewBox"])))
width_gap, height_gap = (1 - FRAC) * width / 2, (1 - FRAC) * height / 2
# Group all elements that are children of <svg> while changing their 'style' attributes
elements = list(root)
group = ET.SubElement(root, "g")
for element in elements:
# Don't group these special tags
if any(map(element.tag.endswith, ["defs", "metadata"])):
continue
root.remove(element)
# Changes all decendents (.iter will also include itself)
for child in element.iter():
if "style" in child.attrib:
child.attrib["style"] = re.sub(
r"(?<=fill:)\S+?(?=;)", color, child.attrib["style"]
)
else:
child.attrib["style"] = f"fill:{color};"
group.append(element)
# Shrink the svg by a factor of FRAC for padding around icon
group.attrib["transform"] = f"matrix({FRAC},0,0,{FRAC},{width_gap},{height_gap})"
xml_string = ET.tostring(root).decode()
xml_string = prettify(xml_string)
with open(TEMPFILE, "w") as f:
f.write(xml_string)
# Check inkscape version
version_string = shout("inkscape --version 2>/dev/null", silence=whisper)
inkscape_major = re.search(r"(\d+)\.\d+(\.\d+)?", version_string).group(1)
command = "inkscape "
if inkscape_major == "1":
command += f"--export-filename={dst_path} "
elif inkscape_major == "0":
command += f"--without-gui --export-png={dst_path} "
else:
error("Unsupported inkscape version")
command += f"-w 72 {TEMPFILE}"
if whisper:
command += " 2>&1 | tail -1"
exit_code = sh(command)
os.remove(TEMPFILE)
return exit_code
def magick_convert_svg2png(color, src_path, dst_path, whisper=None):
cmd = (
r"convert -trim -scale 36x36 -extent 72x72 -gravity center "
r"-define png:color-type=6 -background none -colorspace sRGB -channel RGB "
rf"-threshold -1 -density 300 -fill \{color} +opaque none "
rf"{src_path} {dst_path}"
)
return os.system(cmd)
# For demostration purposes
if __name__ == "__main__":
svg2png = inkscape_convert_svg2png
# svg2png = magick_convert_svg2png
for file in os.listdir("./icons"):
basename, ext = os.path.splitext(file)
if ext == ".svg":
svg2png("#FFFFFF", f"icons/{basename}.svg", f"icons/{basename}.png")

48
matter/theme.txt.template Normal file
View file

@ -0,0 +1,48 @@
# If this file is called theme.txt then the theme template has been
# already parsed and the comments below could not make too much sense.
# theme.txt.template represents a python string that gets format()-ed
# Note: for escaping literal curly braces, double them like so: {{ or }}
# {theme_name} Theme File
# Designed for any resolution
# Global Property
title-text: ""
# desktop-image: "{image_name}"
desktop-color: "{background}"
terminal-font: "Unifont Regular 16" # A smaller font for the console
terminal-box: "terminal_box_*.png"
terminal-left: "0"
terminal-top: "0"
terminal-width: "100%"
terminal-height: "100%"
terminal-border: "0"
# Show the boot menu
+ boot_menu {{
left = 36%
top = 29%
width = 28%
height = 60%
item_font = "{fontname}"
item_color = "{foreground}"
selected_item_color = "{highlight}"
icon_width = 72
icon_height = 72
item_height = 72
item_spacing = 36
selected_item_pixmap_style = "select_*.png"
}}
# Show a countdown message using the label component
+ label {{
top = 82%
left = 35%
width = 30%
align = "center"
id = "__timeout__"
text = "Booting in %d seconds"
color = "{foreground}"
font = "{fontname}"
}}

61
matter/utils.py Executable file
View file

@ -0,0 +1,61 @@
#!/usr/bin/env python3
from subprocess import run, PIPE
from shutil import which
# Logging utils
def color_string(string, fg=None):
COLORS = { # List some colors that may be needed
"red": "\033[31m",
"pink": "\033[38;5;206m",
"green": "\033[32m",
"orange": "\033[33m",
"blue": "\033[34m",
"cyan": "\033[36m",
"lightred": "\033[91m",
"lightgreen": "\033[92m",
"yellow": "\033[93m",
"lightblue": "\033[94m",
"lightcyan": "\033[96m",
"brightwhite": "\u001b[37;1m",
"brightmagenta": "\u001b[35;1m",
}
endcolor = "\033[0m"
return f"{COLORS.get(fg, '')}{string}{endcolor}"
def info(*lines):
for line in lines:
print(f"{color_string('[I] ', fg='cyan')}{line}")
def error(*lines, should_exit=True):
for line in lines:
print(f"{color_string('[E] ', fg='lightred')}{line}")
if should_exit:
exit(1)
def warning(*lines):
for line in lines:
print(f"{color_string('[W] ', fg='yellow')}{line}")
# Shell / external utils
def sh(command):
"Executes command in shell and returns its exit status"
return run(command, shell=True).returncode
def shout(command, silence=False):
"Executes command in shell and returns its stdout"
stdout = run(command, shell=True, stdout=PIPE).stdout.decode("utf-8")
if not silence:
print(stdout)
return stdout
def has_command(command):
return which(command) is not None