A complete guide to our personalized desktop widgets on Kali Linux XFCE
Created: December 11, 2025
This guide documents the setup of two custom desktop widgets:
Required packages for the widgets to function:
# Install required packages sudo apt update sudo apt install conky-all playerctl python3-gi python3-gi-cairo gir1.2-gtk-3.0 # Verify installations which conky playerctl python3
| Package | Purpose |
|---|---|
conky-all |
System monitor with all features enabled |
playerctl |
Command-line media player controller (MPRIS) |
python3-gi |
Python GTK bindings for the Spotify widget |
lm-sensors |
Hardware monitoring (CPU temp) |
conky.config = {
alignment = 'top_right',
gap_x = 20,
gap_y = 50,
minimum_width = 280,
maximum_width = 280,
background = true,
double_buffer = true,
own_window = true,
own_window_type = 'desktop',
own_window_argb_visual = true,
own_window_argb_value = 180,
own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager',
font = 'DejaVu Sans Mono:size=10',
use_xft = true,
update_interval = 2.0,
default_color = 'white',
color1 = 'cyan',
color2 = 'orange',
color3 = 'lime',
}
conky.text = [[
${color1}HOST INFO ${hr 2}${color}
Hostname: ${nodename}
User: ${user_names}
Kernel: ${kernel}
Arch: ${machine}
Uptime: ${uptime_short}
${color2}SYSTEM ${hr 2}${color}
CPU: ${cpu}% ${alignr}${freq_g}GHz
${cpubar 6}
CPU Temp: ${hwmon coretemp temp 1}°C
RAM: $mem / $memmax ($memperc%)
${membar 6}
${color3}GPU ${hr 2}${color}
${execi 5 nvidia-smi --query-gpu=name --format=csv,noheader,nounits}
Temp: ${execi 5 nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits}°C ${alignr}Fan: ${execi 5 nvidia-smi --query-gpu=fan.speed --format=csv,noheader,nounits}%
GPU: ${execi 5 nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits}% ${alignr}Mem: ${execi 5 nvidia-smi --query-gpu=utilization.memory --format=csv,noheader,nounits}%
VRAM: ${execi 5 nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits}MiB / ${execi 5 nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits}MiB
${color1}DISK USAGE ${hr 2}${color}
Root: ${fs_used /} / ${fs_size /} (${fs_used_perc /}%)
${fs_bar 6 /}
${color2}NETWORK ${hr 2}${color}
WLAN: ${if_up wlan0}${addr wlan0}${else}Disconnected${endif}
Down: ${downspeed wlan0} ${alignr}Up: ${upspeed wlan0}
Total: ${totaldown wlan0} ${alignr}Total: ${totalup wlan0}
ETH: ${if_up eth0}${addr eth0}${else}Disconnected${endif}
Down: ${downspeed eth0} ${alignr}Up: ${upspeed eth0}
]]
| Option | Description |
|---|---|
alignment |
Position on screen (top_right, bottom_left, etc.) |
gap_x / gap_y |
Pixel offset from screen edge |
own_window_argb_value |
Transparency (0=invisible, 255=opaque) |
update_interval |
Refresh rate in seconds |
${hwmon coretemp temp 1} |
CPU package temperature from lm-sensors |
${execi N cmd} |
Execute command every N seconds |
${if_up interface} |
Conditional display based on network interface status |
Tip: Finding Your Temperature Sensor
Run sensors command to see available sensors. Look for coretemp-isa-0000 for Intel CPUs or k10temp for AMD CPUs.
Conky doesn't support mouse click events natively. To create a clickable Spotify controller, we built a custom GTK3 widget in Python that:
#!/usr/bin/env python3
"""
Spotify Control Widget - A simple GTK widget for controlling Spotify
"""
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, Gdk
import subprocess
class SpotifyWidget(Gtk.Window):
def __init__(self):
super().__init__(title="Spotify")
# Window setup - borderless, sticky, bottom-right
self.set_decorated(False)
self.set_keep_below(True)
self.set_skip_taskbar_hint(True)
self.set_skip_pager_hint(True)
self.stick()
# Semi-transparent background
screen = self.get_screen()
visual = screen.get_rgba_visual()
if visual:
self.set_visual(visual)
self.set_app_paintable(True)
self.connect("draw", self.on_draw)
# Size and position
self.set_default_size(280, 130)
self.set_resizable(False)
# Position bottom-right with gap
display = Gdk.Display.get_default()
monitor = display.get_primary_monitor()
geometry = monitor.get_geometry()
x = geometry.width - 280 - 20
y = geometry.height - 130 - 20
self.move(x, y)
# Main container
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
vbox.set_margin_start(10)
vbox.set_margin_end(10)
vbox.set_margin_top(10)
vbox.set_margin_bottom(10)
self.add(vbox)
# Header
header = Gtk.Label()
header.set_markup('<span color="#1DB954" font_weight="bold">SPOTIFY</span>')
header.set_halign(Gtk.Align.START)
vbox.pack_start(header, False, False, 0)
# Separator
sep = Gtk.Separator()
vbox.pack_start(sep, False, False, 2)
# Track info labels
self.title_label = Gtk.Label(label="Not playing")
self.title_label.set_halign(Gtk.Align.START)
self.title_label.set_ellipsize(3)
self.title_label.set_max_width_chars(35)
vbox.pack_start(self.title_label, False, False, 0)
self.artist_label = Gtk.Label()
self.artist_label.set_markup('<span color="gray">-</span>')
self.artist_label.set_halign(Gtk.Align.START)
vbox.pack_start(self.artist_label, False, False, 0)
self.status_label = Gtk.Label(label="[Stopped]")
self.status_label.set_halign(Gtk.Align.CENTER)
vbox.pack_start(self.status_label, False, False, 5)
# Control buttons
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
hbox.set_halign(Gtk.Align.CENTER)
vbox.pack_start(hbox, False, False, 0)
for label, callback in [("⏮", self.on_prev),
("⏯", self.on_play),
("⏭", self.on_next)]:
btn = Gtk.Button(label=label)
btn.connect("clicked", callback)
btn.set_size_request(60, 35)
hbox.pack_start(btn, False, False, 0)
# Update every second
GLib.timeout_add_seconds(1, self.update_info)
self.apply_css()
def apply_css(self):
css = b"""
window { background-color: transparent; }
button {
background: rgba(50, 50, 50, 0.8);
color: white;
border: 1px solid #555;
border-radius: 5px;
font-size: 16px;
}
button:hover { background: rgba(29, 185, 84, 0.8); }
label { color: white; }
separator { background-color: #1DB954; min-height: 2px; }
"""
style_provider = Gtk.CssProvider()
style_provider.load_from_data(css)
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(), style_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
def on_draw(self, widget, cr):
cr.set_source_rgba(0.1, 0.1, 0.1, 0.85)
cr.set_operator(1)
cr.paint()
cr.set_operator(2)
return False
def run_cmd(self, cmd):
try:
result = subprocess.run(cmd, shell=True,
capture_output=True, text=True, timeout=2)
return result.stdout.strip()
except:
return ""
def update_info(self):
if self.run_cmd("pgrep -x spotify"):
title = self.run_cmd("playerctl -p spotify metadata --format '{{title}}'")
artist = self.run_cmd("playerctl -p spotify metadata --format '{{artist}}'")
status = self.run_cmd("playerctl -p spotify status")
self.title_label.set_text(title[:35] if title else "Unknown")
self.artist_label.set_markup(
f'<span color="gray">{artist[:35] if artist else "Unknown"}</span>')
self.status_label.set_text(f"[{status}]" if status else "[Unknown]")
else:
self.title_label.set_text("Spotify not running")
self.artist_label.set_markup('<span color="gray">-</span>')
self.status_label.set_text("[Stopped]")
return True
def on_prev(self, button):
self.run_cmd("playerctl -p spotify previous")
GLib.timeout_add(300, self.update_info)
def on_play(self, button):
self.run_cmd("playerctl -p spotify play-pause")
GLib.timeout_add(300, self.update_info)
def on_next(self, button):
self.run_cmd("playerctl -p spotify next")
GLib.timeout_add(300, self.update_info)
if __name__ == "__main__":
win = SpotifyWidget()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
Note: Spotify Must Be Running
The widget requires Spotify to be open to display track info and respond to controls. When Spotify is closed, it will show "Spotify not running".
To have both widgets start automatically when you log in:
[Desktop Entry] Type=Application Name=Conky Comment=System Monitor Widget Exec=conky -c /home/ringmast4r/.config/conky/conky-unified.conf Hidden=false NoDisplay=false X-GNOME-Autostart-enabled=true X-GNOME-Autostart-Delay=3
[Desktop Entry] Type=Application Name=Spotify Widget Comment=Spotify Control Widget Exec=python3 /home/ringmast4r/.config/conky/spotify-widget.py Hidden=false NoDisplay=false X-GNOME-Autostart-enabled=true X-GNOME-Autostart-Delay=5
Tip: Manual Launch Commands
You can also start the widgets manually:
# Start Conky conky -c ~/.config/conky/conky-unified.conf & # Start Spotify Widget python3 ~/.config/conky/spotify-widget.py & # Kill and restart Conky killall conky && conky -c ~/.config/conky/conky-unified.conf &
In the Conky config, modify the color definitions:
color1 = 'cyan', -- Section headers (HOST INFO, DISK) color2 = 'orange', -- Section headers (SYSTEM, NETWORK) color3 = 'lime', -- Section headers (GPU)
| Position | Alignment Value |
|---|---|
| Top Left | 'top_left' |
| Top Right | 'top_right' |
| Bottom Left | 'bottom_left' |
| Bottom Right | 'bottom_right' |
| Middle | 'middle_middle' |
-- Conky: 0 (invisible) to 255 (opaque) own_window_argb_value = 180, -- Spotify Widget (in on_draw method): cr.set_source_rgba(0.1, 0.1, 0.1, 0.85) -- Last value is alpha (0-1)
Useful Conky variables to add:
-- Battery (for laptops)
Battery: ${battery_percent BAT0}% ${battery_bar 6 BAT0}
-- Load average
Load: ${loadavg 1} ${loadavg 2} ${loadavg 3}
-- Top processes
${top name 1} ${top cpu 1}%
${top name 2} ${top cpu 2}%
-- Swap usage
Swap: $swap / $swapmax
${swapbar 6}
# Check if running pgrep -a conky # Check for errors conky -c ~/.config/conky/conky-unified.conf # Common fix: compositor issues # Try changing own_window_type to 'normal' or 'override'
# Install and configure lm-sensors sudo apt install lm-sensors sudo sensors-detect sensors # View available sensors # Find correct sensor name in output and update config
# Test playerctl directly playerctl -p spotify status playerctl -p spotify play-pause # If "No players found", Spotify might need restart # or playerctl service needs to detect it
# Requires NVIDIA drivers nvidia-smi # For AMD GPUs, use radeontop or remove GPU section
| File | Purpose |
|---|---|
~/.config/conky/conky-unified.conf |
Main Conky system monitor configuration |
~/.config/conky/spotify-widget.py |
Python GTK Spotify control widget |
~/.config/autostart/conky.desktop |
Conky autostart entry |
~/.config/autostart/spotify-widget.desktop |
Spotify widget autostart entry |
~/.local/bin/spotify-control |
CLI helper script for Spotify control |