No description
  • Kotlin 77.9%
  • C 21.6%
  • Makefile 0.4%
  • Shell 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-06-09 03:39:29 -07:00
contrib Initial commit 2026-06-09 03:39:29 -07:00
etc/pam.d Initial commit 2026-06-09 03:39:29 -07:00
lib Initial commit 2026-06-09 03:39:29 -07:00
media Initial commit 2026-06-09 03:39:29 -07:00
src Initial commit 2026-06-09 03:39:29 -07:00
.SRCINFO Initial commit 2026-06-09 03:39:29 -07:00
LICENSE Initial commit 2026-06-09 03:39:29 -07:00
Makefile Initial commit 2026-06-09 03:39:29 -07:00
PKGBUILD Initial commit 2026-06-09 03:39:29 -07:00
README.md Initial commit 2026-06-09 03:39:29 -07:00

klear

this is a hyprland status bar i made in kotlin/native. i started it because i tried a bunch of the existing bars and none of them felt right, so i figured how hard could it be (it was pretty hard). it draws everything with nanovg + opengl straight onto a wlr-layer-shell surface. no jvm, no gradle, no electron — just the binary.

i learned a ton building this, so if some of it looks over-engineered, that's me figuring things out in real time. it works on my machine and a few friends' machines, which i'm pretty proud of.

what it does

  • session lockscreen (klear -L) — it's an ext-session-lock-v1 client with real pam auth. it grabs a screenshot, blurs it, and uses that as the backdrop, with a themable card and a refraction halo around the box
  • workspace pill with a sliding indicator — works on hyprland, sway, niri, or mangowc, and it figures out which one you're running on its own
  • clock pill (uses a real font, you pick the strftime format)
  • click the clock and you get a calendar popup — month navigation, events pulled from .ics feeds (local files or urls), and alarms you can set
  • system tray — i had to write the whole StatusNotifierWatcher + StatusNotifierItem thing by hand over dbus. there's no library for it, so i spent a lot of time staring at the spec
  • right-click any tray icon → popup menu, parsed out of com.canonical.dbusmenu
  • battery pill on the far right with a tooltip + a power-profile picker. you can hide/show it at runtime over an ipc socket
  • the bar can sit on any edge — top, bottom, left, right. vertical bars rotate the widgets but keep the text upright (that took a few tries to get right)
  • "pills" mode where each widget gets its own glass chrome, or "unified" mode where one bar-wide pill sits behind everything
  • the glassy refractive look — rim refraction + chromatic aberration done through a hyprland screen_shader, plus a highlight that follows the sun angle so it tracks time of day
  • klearglass blur sets itself up — klear runs the layerrule = blur, klear-bar itself on startup, so you don't have to touch hyprland.conf
  • app launcher (Super opens it by default), fuzzy-matched against your .desktop files + flatpak apps
  • control-center sidebar — wi-fi (NetworkManager or iwd), bluetooth, audio (pipewire via wpctl), and notification history. saved networks and paired devices get inline forget chips, both in the sidebar and the settings panel
  • the wi-fi connect flow lives inline in the settings panel: click an unknown network and a password prompt appears under the row, click a saved one to reconnect, or hit Forget. WPA-Enterprise / 802.1X (the eduroam kind) also gets a username field
  • every popup animates open and closed (power profiles, tray menus, sidebar, launcher, settings, OSDs), and the screen-shader refraction follows the scale + slide every frame
  • multi-monitor: one bar per wl_output, each with its own workspace pill that animates independently. overlays (launcher/sidebar/settings/popups) follow whichever monitor is focused, and there's a keyboard-layout pill next to the clock
  • klear magnifier toggles a loupe above the cursor — convex-lens magnification with a chromatic rim and a bright outline, also done as a screen_shader pass
  • in-app settings panel (the gear button) for wallpapers, theme tweaks, network, etc.
  • desktop notifications via org.freedesktop.Notifications — a banner stack that pops up plus persistent history in the sidebar
  • bottom popup (klear bottom) with cpu/mem, an mpris now-playing disc, sun position, weather, and a cava-style audio spectrum
  • OSD popups for volume, brightness, mic mute, charger, and caps lock
  • the whole theme dumps to ~/.config/klear/theme.json on first run, and hot-reloads when you edit it

screenshots

top edge, pill style (default):

top, pills

top edge, unified style — one bar-wide pill behind every widget:

top, unified

bottom edge:

bottom

left and right edges — vertical bar, widgets rotated, text upright:

left right

app launcher (klear launch / Super):

launcher

control-center sidebar (klear sidebar) — wi-fi, bluetooth, audio, notifications:

sidebar

deps

system packages you'll need:

  • wayland-client, wayland-egl, EGL, GL
  • wayland-scanner, wayland-protocols (only needed to build)
  • a TTF font — it looks for /usr/share/fonts/Adwaita/AdwaitaSans-Regular.ttf by default, but you can point clockFontPath / menuFontPath at whatever you have

build tools:

  • kotlinc-native and cinterop (these come with the kotlin/native toolchain)
  • gcc

optional runtime stuff — klear runs fine without these, the matching feature just gets dimmed or hidden:

  • matugen — wallpaper-driven theming
  • nmcli or iwctl — wi-fi controls in the sidebar
  • bluetoothctl — bluetooth controls
  • wpctl (pipewire) — audio + the volume osd
  • powerprofilesctl — the profile picker in the battery popup
  • swww, hyprpaper, swaybg, wpaperd, mpvpaper, or feh — any one of these works as a wallpaper backend

build

make deps     # one-time: clones nanovg into lib/nanovg
make          # builds bin/klear

after that, rebuilds are just make. make clean wipes the artifacts if something gets weird.

the individual targets, if you need them:

  • make cinterop — regenerate lib/{nanovg,wayland}.klib
  • make c-objects — recompile the C side (nanovg + bridge + protocols)
  • make regen-xdg — rerun wayland-scanner against the system xdg-shell xml
  • make run — build and launch

install

sudo make install                    # → /usr/bin/klear
sudo make PREFIX=/usr/local install  # somewhere else
make DESTDIR=./pkg install           # staged install (for packaging)

arch / aur

klear is on the aur, so paru -S klear should do it (or yay / pikaur / whatever you use). the PKGBUILD lives in aur/klear and pulls nanovg in as a separate git source.

run

./bin/klear

if you're in a hyprland session it should just work. if HYPRLAND_INSTANCE_SIGNATURE happens to be unset, klear scans /run/user/$UID/hypr/ and picks the active one, and XDG_RUNTIME_DIR falls back to /run/user/$UID. sway works the same way through SWAYSOCK, niri through NIRI_SOCKET, and mangowc through XDG_CURRENT_DESKTOP=mango (the workspace pill talks dwl-ipc-v2 through the mmsg CLI, so you'll need the mmsg binary that ships with mangowc).

ipc / cli

the binary is also its own client, which i thought was a neat trick — any first arg other than start gets sent over /tmp/klear-bar.sock instead of starting a new bar.

klear launch          # toggle the app launcher
klear launch on       # force open
klear launch off      # force close
klear sidebar         # toggle the control-center panel
klear settings        # toggle the settings window
klear magnifier       # toggle a loupe-magnifier above the cursor (also: -m)
klear -L              # lock the session
klear unlock          # emergency unlock if the lock ui wedges
klear status          # current bar state
klear config          # path to theme.json
klear kill            # stop klear cleanly
klear help            # list commands

anything it doesn't recognize just prints the help text. you can bind these in hyprland with something like bind = SUPER, SPACE, exec, klear launch.

config

the theme lives at $XDG_CONFIG_HOME/klear/theme.json (or ~/.config/klear/theme.json). the first launch writes out every field with sensible defaults — edit it and klear hot-reloads, so most changes show up without a restart. (barPosition is the one exception, since it sets the wlr layer-shell anchor at startup.)

  • colors are "0xAARRGGBB" strings
  • numbers are plain numbers, bools are bools, strings are strings
  • // line comments and /* ... */ block comments are allowed
  • missing keys fall back to defaults, so you can keep a stripped-down config if you want
  • you can also drop a colors.json next to it as an overlay — keys in there override theme.json. matugen writes this file (more on that below)

some of the knobs i reach for most:

  • barPosition — "top" | "bottom" | "left" | "right"
  • barStyle — "pills" (separate widget chrome) or "unified" (one bar-wide background pill)
  • clockFormat — strftime, e.g. "%I:%M %p", "%H:%M:%S", "%a %H:%M"
  • clockUseFont — set it to false to switch the clock to my old vector "bubble digit" thing
  • useScreenShader, useKlearGlass, useCaustic, useChromaticFringe, useRefraction — turn the fancy glass effects on or off
  • glassRefractStrength, glassChromaStrength, glassEdgePx, glassRimGlow — refraction tuning
  • showShadow — drop shadow under every pill
  • batteryDefaultOn — the starting value of the runtime toggle
  • notifDefaultTimeoutMs — how long a notification banner sticks around before it fades
  • lockBackdropBlur, lockBackdropPasses, lockBackdropSaturation, lockBackdropDim — lockscreen background blur tuning
  • lockCardBg, lockCardCornerRadius, lockClockColor, lockDotColor, lockFieldBg — lockscreen card/field/text colors
  • wallpaperDir — where the settings panel looks for wallpapers (auto-detects if you leave it empty)
  • matugenAutoRun / matugenScheme / matugenMode — see below

matugen

if ~/.config/klear/colors.json exists, klear loads it on top of theme.json — which is handy for matugen generating a palette from your wallpaper. only the keys in colors.json get overridden, everything else keeps your theme.json values.

there are two ways to wire it up:

self-contained (no matugen config needed). flip matugenAutoRun to true in theme.json, or do it in the settings panel → wallpapers tab. then when you change wallpapers through klear, it runs matugen --json hex itself, parses the material palette, maps it onto klear's color keys, and writes colors.json directly. you don't need a klear template in your matugen config for this.

external matugen template. if you already use matugen and want klear to re-theme whenever your other targets do, drop contrib/matugen/klear.json into ~/.config/matugen/templates/ and add this to ~/.config/matugen/config.toml:

[templates.klear]
input_path  = "~/.config/matugen/templates/klear.json"
output_path = "~/.config/klear/colors.json"

then any matugen image /path/to/wallpaper.jpg rewrites colors.json and klear hot-reloads.

picking a different scheme (scheme-tonal-spot, scheme-vibrant, ...) or mode (dark/light) in the settings panel re-runs matugen on the current wallpaper right away, so the change actually shows up.

klearglass / blur

klear's surface is semi-transparent on purpose — the pills, popups, and notifications are drawn with low-alpha fills so the desktop shows through live. for the klearglass look to work, the compositor needs to blur whatever's behind klear.

klear installs the layer rules itself at startup (hyprctl keyword layerrule blur,klear-bar + ignorezero + ignorealpha 0.1), so you don't have to add anything to hyprland.conf for blur.

you can dial the blur strength in the global decoration block of hyprland.conf — blur { size = 4, passes = 2 } is a decent "less blurred" baseline, and bumping passes softens it more:

decoration {
    blur {
        enabled = yes
        size = 4
        passes = 2
        new_optimizations = yes
    }
}

on top of the compositor blur, hyprland's screen_shader runs klear's per-pill refraction shader, so each glass surface picks up the rim distortion + sun-angle highlight (the highlight angle ticks every minute to track time of day).

layout

src/
  main.kt              wayland setup, render loop, workers, action plumbing
  Shell.kt             popen + sysfs helpers, hasCmd cache
  Compositor.kt        picks Hyprland, Sway, Niri, or MangoWC based on env
  Hyprland.kt          socket1 + socket2 readers
  Sway.kt              i3-ipc binary protocol reader
  Niri.kt              niri ipc EventStream reader
  Mango.kt             mangowc — shells out to mmsg (dwl-ipc-v2 client) for tag, selmon, kb_layout
  Dbus.kt              StatusNotifierWatcher, dbusmenu, org.freedesktop.Notifications — hand-rolled wire protocol
  Battery.kt           /sys/class/power_supply reader
  Calendar.kt          ics feed parser + alarms for the clock-click popup
  PowerProfiles.kt     wraps powerprofilesctl
  Wallpaper.kt         finds the active wallpaper from hyprpaper / swww / swaybg
  AppLauncher.kt       scans .desktop files (+ flatpak) for the launcher
  SystemControls.kt    wi-fi (nmcli/iwctl), bluetooth, audio (wpctl), brightness + capslock device probes
  SystemUsage.kt       cpu/mem for the bottom popup
  Visualizer.kt        cava-driven audio spectrum
  Weather.kt           open-meteo + geoip
  Sun.kt               sunrise/sunset for the bottom popup gradient
  IdleInhibit.kt       systemd-inhibit wrapper for the caffeine toggle
  Settings.kt          wallpaper backends + matugen runner
  ThemeApply.kt        applies matugen output to theme.json
  Lockscreen.kt        ext-session-lock-v1 client + pam auth + blurred screen-capture backdrop
  ShaderInstaller.kt   builds + installs the hyprland screen_shader, manages layer rules
  Config.kt            json(c) theme loader/dumper
  IPC.kt               /tmp/klear-bar.sock daemon ↔ cli
  time.kt              strftime wrapper
  ui/
    renderer.kt        every pill, popup, refraction effect, the OSD popups
    theme.kt           every tweakable value, single data class
    state.kt           atomics shared between threads
    Wakeup.kt          eventfd helpers to wake the render loop and worker threads
    Anim.kt, Color.kt, Glyphs.kt   small shared helpers
lib/
  nanovg/, nanovg_bridge.c     C side
  protocols/                   wayland protocol XML → C
  *.klib *.o                   build artifacts
src/def/
  wayland.def, nanovg.def, pam.def   cinterop config
contrib/
  matugen/klear.json           ready-to-use matugen template

lockscreen

klear -L enters a session lock. it grabs the desktop with wlr-screencopy first, blurs that on the cpu, and renders it as the lock background. on top of that sits a card with the clock, your username, and a password field, and auth goes through pam. while it's locked, klear swaps in a lock-only screen_shader (at ~/.cache/klear/lock.frag) so the area around the card refracts the blurred backdrop full-screen — you can tune the rim/halo strength through the lock card theme settings.

install the pam service so klear can check against your normal password — otherwise it falls back to /etc/pam.d/login, which rejects in-session unlocks:

sudo install -Dm644 etc/pam.d/klear /etc/pam.d/klear

if the lock ever wedges (compositor stuck in lock-prevent state, or klear died), open a tty and run bash unlock-recovery.sh — it walks you through klear unlock, a sigterm, and the hyprctl misc:allow_session_lock_restore recipe. i added this after locking myself out once, so trust me, it helps.

bind it in hyprland with something like bind = SUPER, L, exec, klear -L.

license

mit. see LICENSE. do whatever you want with it, just don't blame me if your bar catches fire.