kdos

kdos-shell

kdos-shell is the program behind almost everything a person clicks on the KDOS desktop: the panel along the bottom of the screen, the Start menu, the search palette, the desktop icons, the file chooser, the control centre, the device managers, the notification daemon and some forty smaller windows and popups. It is one C binary, built from src/desktop/kdos-shell/, that behaves as a different program depending on the name it is started under. This chapter describes every one of those programs: what it shows, which key opens it, which file configures it, and why it behaves as it does. It assumes you have read The desktop, which introduces the same windows from a user’s point of view, and The design language, which sets the colour, chrome and key rules every window here follows and which this chapter does not repeat.

The chapter serves three kinds of reader:

The order is from the general to the particular: how the binary dispatches, the conventions all its programs share, the panel, the large surfaces one by one, the small ones in tables, and last the complete option reference.

Invocation

kdos-shell is one binary that answers to 55 command names. It is installed once at /usr/bin/kdos-shell and symbolically linked under its other 54 names (/usr/bin/kdos-start, /usr/bin/kdos-settings, and so on). A surface on this page means one of those programs: one window, popup or daemon. The glossary keeps the word for a window or popup and calls the windowless programs helpers; this chapter counts every one of the 54, because they share one dispatch table, one build and one set of conventions.

When it starts, it takes the file name it was run under (the basename of argv[0]) and looks it up in the dispatch table in main.c. A name that matches runs that surface with the whole argument vector. Run as kdos-shell itself, it is the panel.

When the binary is run under a name that is not in the table (a freshly compiled ./a.out before any link exists, for example), it reads the first argument as the surface name instead, so ./a.out kdos-launcher opens the launcher. If neither the name nor the first argument matches, it prints kdos-shell: no tool named '<name>' and the list of every name to standard error, and exits 2.

The fallback never applies to kdos-shell itself. kdos-shell is in the table (it is the panel), so kdos-shell kdos-launcher starts the panel, which rejects the unknown argument with its usage line and exit status 2.

The 55 names reach 54 distinct surfaces: kdos-launcher and kdos-palette are the same search program, which reads the name it was started under and shows applications only when it is kdos-launcher. One program means one matcher and one idea of ranking.

A name in the table with no matching entry point fails to link, so it is caught at compile time. A name the build creates no link for is a program nothing can start; build.sh installs the binary and creates one link for each of the other 54 names in the table.

One binary instead of fifty-four has a reason beyond disk space. Every surface shares the font cache, the palette and the whole cell-grid toolkit, so two surfaces cannot drift into two different pictures of the same desktop. The binary links the libraries it uses (libkwl, libkdisp, libkcell, libktui, libkcolor, libkbase, libkxdg, libkicon, libkchrome, libkproc, libkimg, libkwm, libksig) statically from the source tree; see The C libraries.

The surfaces

Name Is Detail
kdos-shell The panel The panel
kdos-start The Start menu kdos-start
kdos-launcher The palette restricted to applications kdos-palette and kdos-launcher
kdos-palette One search over everything the desktop can reach kdos-palette and kdos-launcher
kdos-menu The Applications, Places, System and window menus kdos-menu
kdos-desk The desktop, its icons and its context menu kdos-desk
kdos-pick The file chooser, and the file browser kdos-pick
kdos-settings The control centre, nine categories kdos-settings
kdos-net Networking kdos-net
kdos-netagent The passphrase NetworkManager asks for kdos-netagent
kdos-bt Bluetooth kdos-bt
kdos-audio Audio outputs and Bluetooth audio devices kdos-audio
kdos-devices Cameras, microphones, scanners, removable media kdos-devices
kdos-disks Disks: mount, unlock, SMART, write an image, format kdos-disks
kdos-connect A folder on another machine, over SMB kdos-connect
kdos-print Printers: what is set up, what is on the network kdos-print
kdos-notifyd The notification daemon Notifications
kdos-notify The notification centre Notifications
kdos-osd Volume and brightness kdos-osd
kdos-store The application store kdos-store
kdos-trash What was deleted, and the way back kdos-trash
kdos-peek What is in a file, without its application kdos-peek
kdos-pix One picture, and the folder it is in kdos-pix
kdos-find Files by name or contents, applications, recents kdos-find
kdos-rec Record a microphone, and transcribe it kdos-rec
kdos-keys The keybinding card kdos-keys
kdos-style How the screen looks: the accent, and the font kdos-style
kdos-saver Attract mode, started by Super+Shift+L or by hand; idle does not start it kdos-saver
kdos-ime The input-method candidate window kdos-ime
kdos-status The overflow popup The overflow chevron
kdos-traymenu A tray item’s own menu, from its dbusmenu tree The tray
kdos-tip Tooltips Tooltips
kdos-clip Clipboard history: the daemon, and the picker The desk accessories
kdos-mediad The removable-media toast Background services
kdos-cal The calendar The desk accessories
kdos-calc The calculator The desk accessories
kdos-note The scratch pad The desk accessories
kdos-chars The character map The desk accessories
kdos-contacts The address book The desk accessories
kdos-about What this machine is The desk accessories
kdos-teams The window list The desk accessories
kdos-doc The documentation viewer kdos-doc
kdos-display Screen configuration The system surfaces
kdos-time The zone, the clock, and whether the clock is right The system surfaces
kdos-users The accounts, and which one tty1 logs in The system surfaces
kdos-update What is behind, what is vulnerable, which slot is live The system surfaces
kdos-firewall Which services answer the network The system surfaces
kdos-backup What is in the restic repository, adding to it, and restoring from it The system surfaces
kdos-burn A folder or an image onto an optical disc, and the disc checked The system surfaces
kdos-verify Files against a checksum list or a par2 set The system surfaces
kdos-openwith Choose a handler for one file The small dialogs
kdos-run The run box The small dialogs
kdos-prompt Yes/no by exit status; --input asks for a line The small dialogs
kdos-slit The dockapp column The small dialogs
kdos-ascii A picture, as characters The small dialogs

Chords

A chord is a key combination. These are the chords that open a surface of this binary, as shipped in /etc/skel/.config/kdos-comp/rc.xml (copied to ~/.config/kdos-comp/rc.xml when an account is created). Super is the Windows or Command key. Edit your own copy to rebind them; the compositor reads it at start and on reconfigure.

Chord Opens
Super+Space kdos-palette
Super+D, Super+F7 kdos-launcher
Super+A, Super+F10 kdos-start
Super+I kdos-settings
Super+F1 kdos-keys
Super+F2 kdos-teams
Super+F3 kdos-audio
Super+F4 kdos-net
Super+F5 kdos-bt
Super+F6 kdos-devices
Super+C kdos-cal
Super+P, the display key kdos-display
Super+/ kdos-doc
Super+Shift+F kdos-find
Super+Shift+L kdos-saver
Super+Shift+N kdos-notify
Super+Ctrl+Q kdos-calc
Super+Ctrl+N kdos-note
Super+Ctrl+E kdos-chars
Super+Ctrl+B kdos-contacts
Super+Ctrl+V kdos-clip (no argument: the history daemon, not the picker)
Super+Ctrl+Shift+Space kdos-style
Super+Ctrl+C kdos-palette --route capture
Super+Ctrl+H kdos-palette --route setup
Super+Shift+Space kdos panel toggle
Alt+F2 kdos-run
Volume up and down, mute kdos-osd volume +5, -5, toggle
Microphone mute kdos-osd mic toggle
Brightness up and down kdos-osd brightness +10, -10

The shipped Super+Ctrl+V binding opens no picker; the kdos-clip row under The desk accessories says why and how to rebind it.

Other chords in the same file start programs that are not this binary, among them foot, kdos-lock, kdos-power, kdos-session-save, kdos-res, kdos-energy, kdos-shot, kdos-record, kdos-mpctl and the kdos command. They are listed in the desktop guide.

Conventions every surface shares

The rules in this section hold for every surface, so a person who has learnt one window has learnt how the others behave. The key contract and places matter to anyone using the desktop; launching, opening a terminal, popup anchoring and offscreen rendering are mostly of interest to contributors, because they name the one function each surface must go through. The surface runner is the one loop ten of the surfaces share rather than write.

The key contract

Forty of these surfaces answer the same key contract, and the bottom row of each says what the rest of its keys do at that moment. Esc steps back one level and only then closes; F1 opens the surface’s page in kdos-doc where it has one. The layers Esc steps back through (a confirmation, a search, a selection) are declared by each surface and form its Esc ladder; each layer is a rung, named in the hint row by what Esc does there, such as Cancel or Deselect. The full rule, covering the ladder, the hint row and the &-marked accelerators, is in the design language.

Eight surfaces claim an F1 page: kdos-backup, kdos-bt, kdos-connect, kdos-devices, kdos-display, kdos-net, kdos-rec and kdos-settings, plus kdos-res, which is a separate binary. The pages live in /usr/share/kdos/doc and testing/preflight.sh refuses a claim with no file behind it, so a surface not in that list neither advertises F1 nor answers it.

Two menus take the ladder and not the hint row: kdos-menu’s cascade and its --windows list. Both are overlays sized to their content, so a hint row has to be bought out of the height they ask for, and the System menu is 22 items against a 24-row cap, exactly full, so reserving one scrolls Shut Down off the bottom of the menu somebody opened in order to shut down. The row would also be restating what a menu already says: Enter, the arrows and Escape are what a column of labels means.

Fourteen surfaces do not use the contract, and they split into two groups. Eight answer no keys at all: the panel itself, the daemons kdos-notifyd and kdos-mediad, the transient chrome kdos-tip, kdos-slit, kdos-saver and kdos-ime, and the filter kdos-ascii. The other six (kdos-start, kdos-traymenu, kdos-netagent, kdos-osd, kdos-run and kdos-prompt) answer keys from a switch of their own. kdos-run and kdos-prompt are fewer than eight rows tall, and at that height a hint row would take a third of the window; the rest are popups whose whole key vocabulary is the arrows, Enter and Escape.

Launching

Every launch surface in this binary calls sh_launch(): the Start menu, the palette, the desktop’s icons, kdos-find, Open With, the run box, the panel’s quick-launch row, its taskbar chips and kdos-menu’s rows. sh_launch() lives in apps.c, and launch.h carries the rule and is the header a new surface includes.

One shared path is what makes them agree about two things a surface splitting the line for itself gets wrong.

The shared path does A surface splitting its own line gets
Reads the line’s quoting through kxdg_exec_split() Exec=foot --title="Install KDOS" -- sudo kinstall reaches foot as --title="Install and a stray KDOS", so the installer’s own icon opens a terminal that exits
Spends field codes on the documents the launch carries, wherever they sit inside a word --open=%f is handed to the program with the %f still in it, and the program reports a missing file

The line that reaches sh_launch() is the line the entry wrote, field codes and all. The application index and the desktop’s icons keep Exec= verbatim, because the shared path reads those codes twice: it spends them on the documents the launch carries, and it treats a line with none as the kind that wants its documents appended, which is the only way Exec=xterm can be handed a file. A surface that deleted the codes as it read the entry would make every line look like that second kind, so --open=%f would run as --open= with the path as a word of its own and %u would take four documents where the entry asked for one.

Deleting them, which is sh_strip_field_codes()’s job, belongs to the one reader that cannot spend one: the haystack a search is matched against, where a %U is two more letters for a subsequence matcher to travel through, so fu would find every entry whose Exec ends in one. It is a copy that is stripped; the launch reads the same buffer. With nothing to open, kxdg_exec_split() drops every code and leaves no empty argument behind, so nothing has to be deleted first.

A verbatim launch is a typed command line: the run box, and Open With’s Other command… row. A % somebody typed is a character the program must see, so no field code is spent and a file travels as a trailing argument. The quoting is read either way, so mpv "my film.mkv" is two arguments from the run box as much as from an entry.

A surface that holds an identifier, not a line, calls sh_launch_id(), which resolves the entry through the XDG data directories and hands the whole of it over. The panel’s quick-launch row, its taskbar chips’ New window and kdos-menu’s window menu are the callers: none of the three builds the application index, which walks every directory on the machine, and a row that read only Exec would start a Terminal=true application with no terminal around it. The row is resolved twice on purpose, once when it is drawn (for the label) and again when it is clicked, because a row is drawn far more often than it is clicked and a package upgrade can rewrite the entry between the two.

Whatever the caller, the result is an argument vector passed to exec. No launch goes through a shell or system(), because an Exec= line comes from a file anything can write and a shell in the middle would turn it into an injection point.

All six launch keys are read once, by kxdg_launch_read(). The application index, the desktop’s icons, the Open With chooser and kdos-appbox open each reach the same reader, so a key added to it changes every surface at once. NoDisplay and Hidden are not among them: they say whether an entry belongs in a menu, which is a question for whoever is drawing one, and the MIME route opens a NoDisplay entry on purpose.

Opening a terminal

Nothing in this binary names a terminal emulator except sh_term(), which answers foot; every place that opens one asks it. Those places are the root menu’s rows, Places, Open Terminal Here, the manual-page link, the CPU tile, Open With, the run box’s In Terminal, the launcher, the desktop icons and the key card. A call site that spelled the program instead would be the one row that did not move when the answer did.

A terminal running somebody else’s program is given that program’s name. The compositor matches a window to its desktop entry by app-id, so a Terminal=true entry started as a bare foot -e btop becomes a taskbar row called foot, with foot’s icon, however many are open. sh_term_argv_in() writes the emulator, the identity and the -e together. The identity is the first word of the entry’s Exec without its directory: foot receives it as --app-id=NAME, and kdos-term receives it twice, as --title NAME (what a person reads until the program sets a title of its own) and --app-id NAME (what the desktop files the window under). The flags belong to the emulator rather than to the session: an entry may ask for either emulator with X-KDOS-Term, which accepts only foot or kdos-term and falls back to sh_term() for any other value, because an entry is a file anything can write and a key that named an arbitrary program would be a second Exec line.

The two rows that open a terminal as itself, the menu’s Terminal and Open Terminal Here, pass no identity, because there the emulator is the application.

X-KDOS-Float and X-KDOS-Size say how a terminal entry’s window should open, and they reach it through the same argv, as --float and --size COLSxROWS. foot has neither flag, and a window under the compositor is the compositor’s to place, so they are emitted only for kdos-term. A hint is dropped where there is no room in the caller’s argv. Each caller sizes its own, and a window that opens the ordinary way is better than a terminal that does not open because its wrapper would not fit. Both keys are read here and in kdos-desk, which reads the entry through the same call.

Places

Places are Home, the XDG user directories that exist, and then whatever ~/.config/kdos/places adds. There is one reader, kxdg_places(), so the desktop folder, the Places menu, the chooser and Add to Places cannot disagree. See libkxdg.

Surface How places appear
kdos-menu places The whole column, plus Trash (~/.local/share/Trash/files) and Computer (/)
kdos-desk Add to Places on a folder’s context menu, and on the wallpaper, where it adds ~/Desktop. Never on a file, because a file is not a place
kdos-pick Ctrl+P opens the column over the file list, with a Recent directories group under it

Recent directories come from zoxide, which bash.bashrc already initialises, so the group is the tree a person has actually walked rather than one the chooser invented. It is read when the column is opened (one process, not one per frame) and is silent when zoxide or its database is absent. It is a separate call from the places themselves: Add to Places decides whether a folder is already a place by asking for the column, and a frecency guess (zoxide’s ranking by how often and how recently a directory was visited) folded into that answer would make it refuse a folder somebody had merely visited.

Recent files are the same shape. kdos-appbox open is the one function every open passes through, so it is the only place recently-used.xbel is written. kdos-start’s right column draws a RECENT group from kxdg_recent_all(), and only when there is something in it, because a heading over nothing reads as a list that failed to load. A row opens through kdos-appbox open again, so a recent file opens with whatever its type is bound to rather than with whatever last touched it.

Routes

A route is a stable name for something the desktop can open. /etc/kdos/menu.conf defines them, and ~/.config/kdos/menu.conf (or $XDG_CONFIG_HOME/kdos/menu.conf) is read after it, so a route named in both takes the user’s value. Each line maps a name to an argument vector:

setup.network      = kdos-net
capture.region     = kdos-shot region
toggle.quiet       = kdos toggle dnd

The value is split on blanks and run without a shell. There is no quoting, so a route cannot run a pipeline or a shell expression, so a menu file the user owns cannot be made to run arbitrary shell code. A user file can add routes and override them, but cannot delete one, because a name a script holds has to keep resolving.

A route is a name a script can hold. A chord opens a surface and a person clicks a row; neither is something a shell script, a documentation page or another program can refer to. kdos menu summon setup.network is, and it keeps resolving when the chord is rebound or the row moves. kdos menu summon [<route>] opens the palette (kdos-palette), with the route typed in when one is given and empty when none is. The usage line prints the route as required, but it is optional. kdos menu toggle [<route>] does the same, except that when a palette is already open it closes that palette instead. It closes only the palette: an open kdos-launcher is left alone.

The shipped file defines 35 routes in eight groups:

Group Routes
setup 10
system 7
style 5
capture 5
toggle 3
learn 2
share 2
about 1

The table holds at most 64 routes (SH_ROUTE_MAX), system and user together. routes.c is the one reader; a second parse of menu.conf would be a second answer to what a name resolves to, differing only for the file a person edited. Loading is idempotent, so a surface that asks per keystroke costs no file open.

Routes have no column of their own. Their whole existence is a name to search for, so a search is where they appear: kdos-start and kdos-palette both search them, and --route NAME opens either with the name already in the field.

A @name = value line is a setting about the menu rather than a route. The one shipped is @toplevel = Network Sound Displays Terminal, which names the system rows that stay outside the Start menu’s fold (see kdos-start). @ cannot begin a route name, which is what keeps a setting from becoming a launchable row that runs its own value as a program.

Popups and anchoring

A menu opens under the word that was clicked. The panel, its popups and the menus are layer-shell surfaces (the Wayland protocol for panels, overlays and backgrounds, which places a surface against a screen edge rather than as a window). Such a surface has no coordinates, so “at x” is an anchor plus a margin in pixels, which the panel passes as --at X Y or --at-bottom X Y. Without it every menu opens in the centre of the screen and reads as a dialog. Which anchor depends on the bar’s own edge, because a popup belonging to a bar on the other edge has to grow the other way.

A popup’s margin is measured from the output, and the exclusive zone (the strip of screen a surface reserves so that windows do not cover it) decides that. A layer surface with a zone of zero is arranged inside the usable area, which already has the panel’s zone taken out of it, and the panel passes its own height as the margin, so the two apply one after the other and every popup floats exactly one bar height above the bar it belongs to. A zone of minus one means “do not move me out of anyone’s exclusive zone”, so the anchor is the output edge and the margin is the only offset, which is the arithmetic the caller already did. It is set only when a margin was actually given: a centred dialog, a notification with its default corner margin, and the volume bezel are all asking to be placed.

A keyboard overlay that loses focus closes itself, gated on having seen focus first. The compositor decides when an on-demand layer surface gets the keyboard, and a focus loss before any gain would close the surface during its own appearance. There is no “unfocused menu” state worth having.

The panel lights the word the pointer is over. Whether a menu is open is never known in the panel, because the menu is a separate process and does not report back. Hover is what the bar actually knows, and it is what makes three words read as three buttons.

The surface runner

Ten surfaces have no event loop of their own: kdos-about, kdos-chars, kdos-contacts, kdos-firewall, kdos-note, kdos-print, kdos-trash, kdos-update, kdos-users and kdos-verify each describe what they draw and how they answer, and sh_run() in shell.c runs them. It reads --font and --dump, applies the theme, draws the offscreen frame, opens the window, applies a resize, asks the key contract before the surface’s own keys, and follows an accent change while the window is open. Those steps are therefore the same on all ten, and a surface on the runner cannot leave one out. The other surfaces write the same loop out themselves. How to put a surface on it is in Writing desktop software.

Rendering one frame with no display

Any surface can be asked to draw one frame into memory and print it instead of opening a window. This is how the test suite checks layouts, and it is handy for a contributor: you can see what a surface draws from a plain terminal, with no compositor running.

Fifty of the 54 surfaces support it. The four that do not are kdos-ascii, which is a filter with no frame of its own, and kdos-mediad, kdos-netagent and kdos-ime, whose windows exist only in response to something arriving on a bus.

Flag Produces Offered by
--dump The cell buffer as plain text All 50
--dump-cells One line per painted, non-blank cell: row col U+XXXX fg bg attr kdos-start, kdos-menu, kdos-pick, kdos-settings, kdos-find, kdos-keys, kdos-doc, kdos-openwith, kdos-teams
--dump-size WxH Render at that size kdos-settings, kdos-keys, kdos-doc, kdos-openwith, kdos-teams; kdos-desk takes the two numbers as separate arguments, --dump-size W H
--dump-width N Render the panel at that width kdos-shell (the panel)
--dump-query TEXT Type a query before drawing kdos-palette, kdos-keys
--dump-view VIEW Draw one view of the menu — cats, cat:<Group> or search:<query> — and implies --dump kdos-start

For the surfaces with no size flag, the dump harness in testing/fixtures/shell/dumpmain.c overrides the geometry from KDOS_DUMP_SIZE=WxH. For example:

kdos-settings --page panel --dump --dump-size 100x30
kdos-keys --dump-query tile --dump

--dump proves the layout; --dump-cells is what makes a colour regression visible as well as a geometric one. Reference frames for both are committed and compared by the test suite.

Dumping at a small size catches layout faults that the shipped size hides: a surface forced to degrade shows whether, for example, the Start menu’s columns stay clear of its footer when they are long enough to reach it.

Those committed reference frames are called goldens: the test suite renders each surface offscreen and compares the result byte for byte against its golden (see Testing). Two sources cannot appear in a golden: the dump harness stubs the window list to zero, and the file source forks fd, which a dump stops before drawing. A golden of somebody’s home directory would depend on whoever ran the suite.

The panel

kdos-shell with no other name is the panel. It is two rows on the bottom edge by default, drawn as a layer-shell surface with an exclusive zone (see Popups and anchoring). One instance runs per output, started and supervised by the compositor, which passes it the options below from comp.conf. You do not normally run it by hand.

Option Does Set from comp.conf
--top, --bottom Which screen edge the bar sits on. Bottom is the default panel = top passes --top
--cells N Rows of cells, clamped to 1–4. The default 2 is a 40-pixel bar on the default 10×20 cell, which makes a four-cell window button a square panel_cells
--autohide Hide the bar until the pointer reaches the edge — see Autohide panel_autohide
--margin PX Gap to the screen edge, 0–64 pixels. 0 is edge to edge; anything else floats the bar and grows the exclusive zone to match panel_margin
--opacity PCT Bar opacity, clamped to 20–100. The floor exists because a bar at zero is still a row of controls that catch the pointer panel_opacity
--clock FMT The clock’s strftime format clock_format
--font NAME The cell font panel_font
--output NAME Which screen this panel belongs to. Without it layer-shell picks one output and the others get no bar Passed per output
--no-icons Draw no pictures icons = no
--dump [--dump-width N] Print one frame, 100 columns wide by default, and exit —
--version Print the program’s version and exit —

Any other argument prints the usage line and exits 2. panel = off (or none) in comp.conf starts no panel at all. The shipped comp.conf documents the defaults the compositor passes when a key is absent: panel_cells = 2, panel_margin = 0, panel_opacity = 80, panel_autohide = no, panel_font = Terminus:pixelsize=20; with no --clock the panel uses %H:%M. The compositor passes --font from panel_font, whose default gives a 10×20 cell and so a 40-pixel two-row bar; only an empty panel_font falls back to chrome_font. The two keys are separate so the bar can be drawn in a different face and size from the menus it opens.

From left to right the bar carries the Start button, the quick-launch row, the window list, the meters strip, the status wing (the notification-area widgets and the tray) and, in the last column, show desktop. The second row carries the clock’s date, a window button’s own title under its application name, and the meters strip.

Configuration

The panel reads ~/.config/kdos/panel.conf (or $XDG_CONFIG_HOME/kdos/panel.conf). The file is optional: without it, every key has the default below. The copy in /etc/skel that a new account receives is all comments, so it documents the keys without changing any of them. It has six keys, each key = value on its own line:

Key Takes Default
right The notification-area widget names, in order, left to right pager tray more media privacy mpris clipboard cpu stutter update restart net volume battery notify clock
overflow Which of them live behind the chevron stutter restart clipboard
meters Which meters, in order of importance cpu ram net
task_labels auto, yes or no (on and off are accepted too). See Layout and degradation no: icons only
tray_hide Tray item identifiers not drawn on the bar fcitx fcitx5 org.fcitx.fcitx5
start_label yes or no (or on, off): whether the Start button carries its word yes

For example, to put the stutter chip back on the bar and chart disk usage in place of the network:

overflow = restart clipboard
meters   = cpu ram disk

The meters share one tile of at most sixteen cells, and cpu ram net already fills it, so adding a meter to the default set draws nothing more: the last one in the list is dropped. See The meters strip for each meter’s width.

The sixteen widget names are clock, battery, volume, net, restart, privacy, tray, pager, mpris, cpu, clipboard, media, notify, stutter, update and more. more is the chevron itself and cannot be put in the overflow. An unknown name is reported on standard error and skipped, so a typo does not look like a widget that does nothing: kdos-shell: panel.conf: no widget named `x` — ignored for right, no meter named `x` — ignored for meters, and `x` cannot go in the overflow — ignored for an unknown name or more in overflow. A bad task_labels or start_label value is reported with the values the key takes. tray_hide holds at most eight identifiers, matched without regard to case. meters = or overflow = with nothing after it switches that feature off.

more sits immediately right of the tray, which is where every desktop that has one puts it, and here it is also arithmetic: the chevron is two columns with no gap of its own and sits against the tray. Hiding the input-method item gives three columns back, which more than pays for it.

The panel re-reads this file on SIGHUP, so changes take effect on the bar that is on screen. Settings sends that signal when you apply a change; after editing the file by hand, send it yourself:

pkill -x -HUP kdos-shell

-x sends it only to processes named exactly kdos-shell, which are the panels, one per screen. The other surfaces are the same binary running under their own names, and none of them re-reads panel.conf. The loader restores every default before parsing, because it runs again on that signal and a reload that only ever added would leave a widget hidden after the line hiding it was deleted. The file is the whole state, every time.

Whether pictures are drawn at all is icons in comp.conf, not a panel key: the compositor passes --no-icons to the children it spawns.

Layout and degradation

The bar is laid out in four passes, and the order is the priority:

Pass Gives up
0 Nothing — the whole bar
1 The meters strip
2 Also the Start button’s word, leaving its mark
3 Also the quick-launch row

No pass may drop a window button. Before pass 1 is reached, the window list degrades on its own. How it degrades depends on task_labels in panel.conf:

task_labels Window buttons
no (the default) Icon mode whenever the pixel layer is there: a square picture per window, like a dock. Labels appear only on a character grid, where icon mode cannot be drawn
auto Full labels, then labels squeezed to their floor, then icon mode — four cells per window, the picture centred over both rows with a state marker under it — and only after that an overflow cell
yes Always labels. When they do not fit, the extra windows go behind a +N overflow cell rather than into icon mode

Under auto, icon mode before overflow is the right trade. A picture that identifies the window is worth more than a word beside it, and dropping a window from view while the row still has room for a picture of it is not.

The right-hand wing reserves space against the window list’s icon floor, and the acceptance test measures against the same figure. If the two figures differed, a layout pass could satisfy the test and still discard the layout the test was written for.

The last column of the bar is show desktop, and it goes both ways: anything on screen and a click minimises it, nothing on screen and the same click puts back what that column hid.

The way back is the remembered set rather than the live list. kdos-comp re-reports a window on another workspace as minimised (the pager’s occupancy is derived from exactly that), so a column that un-minimised everything the bar calls minimised would haul every other workspace’s windows onto the one in front of you, and would undo a minimise somebody made on purpose before the press. The memory is window ids, matched against the live list on the way back, so a window that has closed or that something else restored is not found. It is the panel’s own: a panel started against an already-cleared desk has hidden nothing, and the column then does nothing until there is something on screen to hide. The compositor’s own ToggleShowDesktop is a separate control over the same windows, so pressing one and then the other can leave this one’s direction inverted for a press.

The Start button

The Start button shows the KDOS mark and the word Start. The mark is the kdos-launcher icon, or the theme’s start-here icon, or with no artwork a glyph on a UTF-8 terminal and the literal word KDOS elsewhere. The button is quiet at rest, lit in the accent colour under the pointer, and drawn in the warning colour while its menu is open. The rules below keep it that way; the constant and colour slot names in them are for contributors.

The button has three states, and one function draws both the pixel tile and the character fallback. A control whose two renderings disagree about its own state is worse than either.

Quick launch

Pinned applications, read from ~/.config/kdos/favorites, the file the Start menu’s pinned column also reads. The row looks up each whole trimmed line as a desktop-entry identifier and skips a line that names no entry, so a line carrying code= or any other word after the identifier is not shown. Every shipped line carries a code, so on a new account the row is empty until an application is pinned from a menu, which appends a bare identifier. The file’s format is in Configuration.

Hover is a fill behind the icon, never a tint of the icon, because tinting changes what the application looks like. A launch lays the accent over the button and pulses it twice in 1.1 seconds, drawn at the display’s rate by the backend’s frame clock; the panel wakes that often only while it runs and goes back to its once-a-second wait after it. With motion = no in comp.conf the accent is held still for the same time instead. It is pixels only, so tty1 and --dump show the button at rest.

Dragging reorders, and the order is written back. The button is what is remembered across events, because plain and dragged motion are indistinguishable in the protocol; the launch therefore happens on release, since a launch on press fires before a drag can begin. Released off the row it does nothing.

The window list

Button Does
Left Toggles: minimises the window you are in, restores one you are not, opens the member list for a group
Middle Opens a new instance, from the pinned entry’s command or the window’s own desktop entry
Right The window menu

Right opens the menu rather than minimising, because minimising is what left already does and the right button is the one every other desktop reserves for these verbs. Closing is the menu’s, not the middle button’s: middle is the button a hand hits by accident on a wheel, and on a row of icon-mode squares what it closed would have had no name on it and no confirmation.

A window button is a button. An inactive chip is filled, so the row reads as controls rather than as floating words; hover is a step brighter, and focused brighter still, so hover cannot be misread as “this is the window you are in”.

The shape is a pixel plate, or a cell fill where there is no pixel layer. Under a compositor the cells stay KT_SURFACE and the plate is the button, since a cell fill there would paint over it. A character grid has no plate, so the chip fills itself: KT_DIM at rest, KT_MID hovered, KT_ACCENT focused, each with its slots swapped. Without that the row is a line of floating words with no edges.

The state cue is a pixel underline, and a cell marker where there is no pixel layer. Running, focused and minimised are a two-pixel accent line under the plate: one fact in one place, so a chip carrying its application’s own picture is not also carrying a glyph saying the same thing. Where there is no pixel layer that line is never replayed, so a group that is entirely minimised marks the column between the picture and the label instead: the chip’s one spare cell, and exactly wide enough for the mark.

Icon mode needs the pixel layer, whatever task_labels says. A dock button is a 40×40 square whose shape is a plate and whose state is an underline, and both are pixels. On a character grid the same button is a 2×2 picture on the bar’s own background with nothing saying it is a button, is running, or is minimised, and an application the atlas has no artwork for arrives as one lowercase letter in four cells. The labelled chip is what the cell layer can draw, so a dump gets it.

The label is the desktop entry’s name, resolved once when the identifier arrives rather than per frame: name, then title, then the raw identifier. An application identifier is chosen so that it cannot collide, so the reverse-DNS form is not what belongs where a human name goes. The entry is found by its id first and then by the window-class field, which is what an X11 client under Xwayland needs.

The box appears in a label only when it disambiguates, which is where the same application runs in two boxes. A box name on every button would lengthen each label without helping anyone pick a window.

Applications can put a count badge or a progress bar on their button through the launcher-entry interface, which unity.c implements.

The meters strip

The meters strip is a row of live graphs drawn as one pixel tile on the second row. meters = in panel.conf selects which, and the order is the order of importance, because a narrow bar drops them from the right. Six kinds exist; the default selection is cpu ram net, which is 16 cells wide. Sixteen cells is also the most the strip can ever be (MET_TILE_MAX). That is the strip’s share of the bar, not a limit of the tile, which can be any size up to the grid: the window list and the status wing need the rest of the row. A selection wider than sixteen cells loses meters from the right even on a wide screen.

Meter Cells Source
cpu 5 Aggregate processor time
ram 5 Available memory, never total-minus-free — Linux spends every spare page on cache, and that arithmetic reports a healthy machine at 95%
disk 5 Filesystem usage on /, sampled every ten seconds
net 6 Received and sent, mirrored about a midline on one shared scale
diskio 6 Bytes read and written, whole disks only — the kernel lists partitions beside their disk and summing everything counts each byte twice
temp 5 Package temperature, on a fixed axis: an auto-scaled temperature would redraw itself every time the fan came on, which is the one moment somebody is looking at it

Seven rules make the charts readable, not merely present.

The trace spans the whole band from the first sample, the oldest value held out to the left edge, so the picture moves from the first sample onward rather than staying pinned to the right edge until the ring fills.

A left click on the strip opens btop in a terminal. Middle opens the stutter attribution and right opens the energy report, each in a kdos-status popup.

The status wing

Two rows, and every applet uses both: a readout with thirty-two pixels of nothing under it looks unfinished beside a clock that uses both.

Two shapes, and the split is what each carries: a number goes under its picture, a name goes beside it. The compact applet is three cells, an icon over a reading; three rather than two, so the icon is centred in the tile rather than in its left half, and so a one-character value is not pushed under the icon’s left edge. The wide applet is an icon with a headline and a detail line, for the two readouts carrying somebody’s name: the media title and the application holding the microphone.

An applet tile is a fixed width whatever it says. The wing is laid out right to left and everything to its left starts where that walk stopped, so a readout going from three characters to two would narrow the wing and slide every chart on the panel sideways.

The pager is little screens: one filled cell per workspace in its own state colour with the number under it, and the second cell of the stride left as background. Its hover is the width of what was drawn rather than of the stride, or the highlight lights two cells under a square that is one.

The window list stops at the wing. The wing’s left edge is whichever of its two rows reaches further left, and the meters strip is handed the room the window list needs as its floor, so on a narrow bar the strip degrades rather than the window list overwriting the charts.

The media title comes from whichever source can answer. MPRIS (the Media Player Remote Interfacing Specification) over the session bus is the protocol a desktop player speaks, and a player that speaks it also answers the transport keys beside the cell. mpd speaks none, so kdos-mpctl watch writes the same answer to $XDG_RUNTIME_DIR/kdos/nowplaying and the widget falls back to that file, reading it at most once a second because the draw is not on a tick. The session starts the watcher, and mpd itself with --no-daemon, once an mpd configuration file exists in one of the places mpd looks (~/.config/mpd/mpd.conf, ~/.mpdconf, ~/.mpd/mpd.conf or /etc/mpd.conf). The image ships none, and mpd exits at once without one. The watcher outlives mpd: when mpd goes away it empties the file, looks again every five seconds, and exits when the login’s runtime directory is removed. The file’s leading > or || is the play state and is stripped before the title is drawn. One widget reads both, because two cells disagreeing about what is playing is worse than one that is sometimes empty.

The disk warning

The disk meter charts /, because a chart has room for one number. The warning reads every writable filesystem, so it reaches a separate /home or the stick somebody is copying onto.

It walks /proc/mounts and calls statvfs on the meter’s ten-second cadence rather than per frame, because it makes one statvfs per mount and one on a network mount can block. At most sixteen mounts are tracked. kdos-mountd cannot answer this: it is gated to seat and wheel, its reply carries no free-space field, and it lists the media that are not mounted, which is the complement of the set that can be full.

At 90% a mark appears one column inside the clock’s own segment: an exclamation mark, warning coloured, error coloured from 95%. The column is spent whether or not there is a warning, or crossing the threshold would move every item left of the clock by one. It is a plain !, not a glyph slot, because nothing in the glyph tiers is a warning sign and a missing glyph draws a box. The clock is the one landmark on the bar that never moves, and the warning is about the machine rather than about whichever applet is next to it.

One notification is raised per five-point step from 90, so 90, 95 and 100 speak and nothing between them does. The step each mountpoint has already been warned about is latched in $XDG_STATE_HOME/kdos/diskwarn (~/.local/state/kdos/diskwarn by default). It is on disk rather than in memory, or a disk that stays full warns again at every login. A step that falls is recorded too, so emptying the disk and filling it again warns again.

The chevron’s popup carries the row whatever overflow = says, since the mark has no widget of its own and cannot say which filesystem or by how much. Opening it runs ncdu -x on that mountpoint: the question a full disk asks is where the space went, which is a recursive sum no listing shows, and -x keeps the scan off every other filesystem.

The overflow chevron

Widgets that only sometimes have something to say (the stutter chip, the restart mark, the clipboard depth) appear and vanish, changing the wing’s width by several columns each time and sliding every chart. So they live behind one chevron of fixed width, drawn whether or not anything is in it at the moment. overflow = in panel.conf names them, using the same names right = uses. The chevron is omitted only when the overflow is switched off entirely, with no widget assigned to it and no tray identifier hidden, because it would then be a control for nothing.

The chevron is two columns with no gap of its own. A full applet tile would be four columns, and four columns is the whole network chart on an eighty-column bar. It carries no count: a digit is one cell wide in a two-cell box and would sit half a cell off. Colour says whether anything is there and whether it wants attention; the tooltip names the first items; the popup has the list.

It returns one column short of its own left edge, because the widget to its left draws a separator at that column. Returning otherwise puts the rule through the chevron’s left cell and the two-cell picture comes up as its own right half.

kdos-status is the popup. The hidden-widget list is published by the panel into a file rather than re-derived, because re-deriving “three restarts” in the popup would be a second implementation of the same reading, asking the system again at the moment somebody clicked.

Its other half is a live pane: it runs the stutter report, the restart list or the energy report onto a pipe and drains it without blocking, so the two most KDOS-specific tools on the machine are read in a scrollable popup instead of in a terminal that covers the desktop and scrolls a fresh paragraph per dropped frame. Long lines are soft-wrapped on the way in, because a stutter report is a hundred columns wide and the half that gets clipped is the half naming the process.

The tray

The panel is a full StatusNotifierItem host (the D-Bus protocol through which applications publish tray icons), and also the watcher, because nothing else here is. That matters more here than elsewhere: a boxed application that minimises to a tray which does not exist has minimised to nowhere.

An item is three cells wide, centred on the wing’s full height. It is drawn as a picture when one resolves: first a themed name, then the item’s IconName, then its identifier lowercased and asked for as an icon name. When none resolves, the item is the first letter of its identifier, centred in the three cells and coloured by its status: KT_MID for passive, the text colour for active, the accent and reversed for needs-attention. KT_MID, not KT_DIM, because the fallback is a letter and dim is the palette’s fill: at 1.17:1 against the bar, the cell reads as empty. The identifier rather than the icon name, because a letter from a name a human chose beats a letter from a theme lookup that will never happen on a character grid.

Left activates, middle secondary-activates, right opens the menu, and which menu depends on what the item published. An item with a com.canonical.dbusmenu path gets its tree drawn by kdos-traymenu; one with none gets ContextMenu, which is what an application with a menu window of its own answers. An item that also sets ItemIsMenu has no useful Activate at all (the specification’s answer to a click on one is “show the menu”), so for those the left button opens it too.

Three rules keep the tray from taking the bar down with it.

When something else owns the watcher, the list is adopted, not contested, which is what makes a dump taken beside a running panel show the same items rather than an empty tray.

An item may resolve to a themed name first, and exactly one class does. The input-method item every session has publishes no usable icon name, so the identifier fallback finds its own full-colour artwork sitting between a phosphor network card and a phosphor speaker. Input-method identifiers map to a keyboard icon before the application-artwork lookup runs. That exception covers only an item that is a system function; every other item keeps its own artwork.

Because items can be hidden, the drawn order is recorded and the click reads that, rather than deriving an index from the pointer’s column. tray_hide = in panel.conf is the list of identifiers not drawn on the bar; they are listed in the overflow popup instead. The input-method item is on that list by default, which gives its three columns to the chevron; tray_hide = with nothing after it draws every item on the bar.

kdos-traymenu is the item’s own menu, and it is its own process, which is the rule every popup on this bar keeps: the panel’s event loop owns one surface and one cell buffer, and an application slow to answer GetLayout must not take the bar with it. The panel hands it the item’s bus name, the Menu object path and the item’s Id for the title, because the menu object publishes no name of its own.

A row carries its picture and its chord. icon-name goes through the theme lookup every other surface in this binary makes, and icon-data is a PNG decoded straight off the bus. A row that publishes its own bytes usually publishes no name, so declining the bytes would be declining the icon. shortcut is drawn on the right, dimmed even on the selected row: this desktop cannot press it, and a menu that names the key is how somebody stops opening the menu. A toggle is drawn, because a row that says “Pause” with no mark is a row whose state is a guess.

The icon column is spent by the level, not by the row. Labels starting in different columns depending on whether the row above resolved a picture read as ragged, which is the rule the toggle mark already keeps. The chord field is as wide as the widest chord on screen, so scrolling a long menu can move it rather than every level paying for the one row with four modifiers in it. --no-icons turns the pictures off, and a --dump turns them off for itself, because a golden frame is the character grid.

The privacy indicator

Which application is using your microphone or camera, by name.

  Found by Why not the other way
Microphone An audio-server capture stream, counted only while running The process list says the audio server holds the device, which is a non-answer
Camera A process holding a descriptor on a video device Almost nothing takes the camera through the portal, so the audio server would report nothing at all

A stream that exists is not a stream that is recording: an application that opened the microphone and went idle must not light the lamp. The camera is counted the other way round on purpose, since an open descriptor on a camera is use, there being no other reason to hold one.

The name is the application’s own, then its node name, then the process name. One application is named and the rest are counted, because the panel is one row and three truncated names say less than one name and a number.

It is drawn in the secondary colour, with the camera additionally reversed: the one thing on the panel that is a warning rather than a fact. The tooltip names the box; the bar does not. On a machine where every large graphical application runs in a container, “firefox is recording” leaves out the half that says which firefox, and there can legitimately be two.

The microphone lamp is a control: clicking mutes. An indicator that names the application recording you and cannot stop it is one people learn to ignore.

The stutter chip

The compositor reports every late frame on $XDG_RUNTIME_DIR/kdos-frames.sock. The panel holds that socket open, counts the drops of the last ten seconds, and shows a cell when there have been at least three. Clicking opens the attribution. The chip lives behind the overflow chevron unless overflow = is changed.

The cell goes away when the desktop stops missing frames, because an indicator that is always up says nothing.

The read is non-blocking, with a reconnect no oftener than every ten seconds, because the frame loop is what this must never slow. There is no JSON parser in this binary: the "dropped": field is scanned for literally, and the line it scans is written by the compositor’s kdos-frames.c in the same repository.

Tooltips

Half the panel is pictures with no words. Hovering one thing for 700 ms raises kdos-tip, which says what it is and what its three buttons do.

It is a separate process, because the toolkit has one cell buffer per process, and it takes no input at all, or it would eat the click aimed at the thing it describes. The panel shortens its own poll to the dwell deadline, exactly as it does for the meters and for the end of a launch pulse.

A window button’s tooltip carries a live picture of the window, which is the only way to tell three terminals apart.

A click spends the dwell. Killing the tip on motion clears the dwell because the pointer has moved to something else; a click moves nothing, so without spending it the tooltip goes straight back up over whatever the click just opened.

Autohide

panel_autohide in comp.conf is the pointer’s switch. Hidden, the panel drops its exclusive zone, so the strip it was holding goes back to the windows; what stays on screen is one row of accent shade, because a panel that vanished completely is a panel nobody finds again. An enter over that strip shows it at once, and a leave arms a deadline rather than hiding under the hand.

Super+Shift+Space is a person’s switch, and it outranks the pointer. kdos panel toggle signals the running panel with SIGUSR1, by name, which reaches the panel and not the desktop icons or the notification daemon (the other argv[0]s of the same binary). While the bar is put away the pointer will not bring it back. Without that it would return the first time the mouse crossed the bottom row, which reads as a chord that did not work. The name is matched against comm and matched whole, because SIGUSR1’s default disposition is death and an unanchored match would reach a future name carrying this one.

What the bar is drawn with

The bar looks like drawn chrome, with plates, edges and gradients, while every word and number on it sits on the same character grid as the rest of the desktop. The rest of this subsection is for contributors, and names the code that keeps the two layers aligned.

Every affordance on the bar is pixel chrome, drawn by libkchrome into the backdrop one layer under the cell grid: the body, the edge against the desktop, a button’s plate, the hairline between two segments and a meter’s gradient. It costs no columns and no rows, which is why the bar can be two rows and still look like chrome.

The body is laid at panel_opacity, and at full opacity while any window is maximized or fullscreen and not minimized: over a bright window a translucent bar loses the difference between its rest, hover and focused plates. At full opacity the bar tells the compositor it is opaque, so the compositor copies it instead of blending it. The backdrop goes in through kch_px_custom(), so the bar’s picture is cached like a popup’s: a clock tick or a meter sample repaints the cells that changed, a plate that moves repaints the cells it left and reached, and only a change of opacity or a retint repaints the bar.

Every picture on the bar is registered for, and drawn into, the content rows, never the surface’s full height. The edge row is stamped across every column after the layout, so a sprite that claimed the whole surface would lose its top row of tiles to the double horizontal, and what was left of the picture would sit high in the rows that remain.

applet_row, bar_y0 and bar_h decide where the content lives inside those rows, once per frame, and everything the frame calls reads them. A function that worked the row out from the surface height instead would draw into the edge row.

sh_pic_backend() installs the sprite table’s evictor and budget and registers a function with kdisp_on_scale() that moves both to the cell in force (kicon_recell() and the budget), so the icons are rebuilt when the output’s scale arrives or changes when the window moves to another screen; on a fractional scale it is the cell that grows, to the font’s at the device size. sh_pic_cell_w() is the nominal cell the budget is computed in, and it and kdisp_scale() are what a surface passes to kicon_init(). Every popup anchor the panel hands out (--at and --at-bottom for a menu, a tray menu, a tooltip, an applet) and a tray item’s click position are converted with kdisp_px_logical(), because another surface’s margin is logical pixels and the panel’s cells are device pixels on a fractional scale. kdisp_cell_w() is 1 on a display with no pixel size of its own, and libkicon refuses a cell under four pixels, so a surface that hands it the backend’s cell gets no pictures at all. icons_drawable() is the one place that asks whether a picture can be drawn, so no control spends cells on one that cannot.

Fixed-width fields

Every number on the bar has a fixed-width slot, so nothing on the bar shifts sideways as a value changes. No field is the width of the value currently in it. The right wing is laid out right to left, so an item that grew by a column would carry the separator, the meters strip and the right edge of the window list sideways with it, and the bar would shift whenever a percentage crossed 10 or 100, or a rate gained a unit. The table names the constants in panel.c that reserve each slot.

Field Reserved The widest legitimate value
A meter’s reading MET_VAL_W, 4, plus the arrow for a mirrored band 100%, and fmt_rate never writes more than four
The clock’s segment clock_field(), measured once over a synthetic year Whatever the person’s own strftime format can produce
The full-disk mark One column, always It is blank when the disk is not full
An applet tile AP_TILE_W, 3 — AP_NET_W, 4 for a rate A rate is a number and a unit
The one-row CPU applet AP_CPU_W, 8 CPU 100%

applet() is applet_w() sized to its own label, for a readout whose width cannot change; anything whose reading grows a column passes the field it reserves.

fmt_rate is capped at four cells with the unit always present. It could otherwise write 1023k, and a five-character value in a four-cell field loses its letter from the right, so 1.2M reaches the screen as 1.2, one and a fifth bytes a second. The decimal is what gives way, and only below ten of a unit.

A sparkline draws a zero as a baseline, so an idle meter still shows its track. ramp_index(0) is exact empty, which is right for a gauge and wrong for a chart: an idle meter would draw ten spaces between its label and its reading and the track would vanish, leaving the wing reading as words with gaps rather than as charts.

kdos-start

kdos-start is the Start menu, the entry point for somebody using a pointer.

The left column is what you use: pinned entries above the rule, most-frequently-launched below it, which needs a usage count, kept in the state directory and written atomically. All Programs opens the category list in place, because a cascade needs a surface per level and buys nothing on a grid.

A pinned row can carry a launch code: a line id code=XX in ~/.config/kdos/favorites draws the two letters at the right of that row, and typing both letters (in either case) into an empty search field opens the row at once, with no arrow keys and no Enter. The check runs when the second letter lands, so a third letter is an ordinary search. The menu reads the first word of a line as the identifier and ignores anything after it. All seven shipped favourites carry a code; the file and the palette’s use of the same codes are in Configuration.

The menu is three columns from a hundred columns wide and two below it: favourites and applications, then places and files, then the system group. Under a hundred columns the system group folds behind one Settings ▸ row and becomes a page of its own with a Back at the top, because fourteen system rows scrolling inside a sixteen-row body is a list whose end nobody finds.

The menu asks for the wide size and lays out from the width it was given. A surface size is a request that a server clamps to the work area, so asking for the narrow size and branching on the request would produce a three-column menu that exists only in a dump. The fold moves the right column and nothing else: the two application submenus rebuild the left one, and the search walks the whole array whether a row is folded away or not, so typing bluetooth finds it from either page. Which system rows stay outside the fold is @toplevel in menu.conf and nowhere in start.c, so promoting Bluetooth is a line in a file.

The way back is a row, because Escape and the right button are not discoverable and a pointer-only user is exactly the first-time user of a Start menu. It is the first row of the left column and it never closes the menu; a search gets the same row as “clear search”.

The field looks like a field: placeholder text, a block caret, sunken, lighting under the pointer, and going to the accent the moment it is active. A control that looks identical before and after being clicked is one people click again to find out whether it worked. The clear mark is drawn only while there is something to clear. With nothing typed, the row under the selection shows the selected entry’s comment, which every desktop entry carries.

The selected row is a plate, and an accent fill where there is no pixel layer. The plate with its accent left edge is the desktop’s one selection, from the tone table the taskbar and the cascading menu share; kch_px_live() is what says whether a recorded pixel op can reach a screen at all, which needs a backdrop installed, and an offscreen dump never has one. Where it cannot, the row draws the accent fill with its slots swapped instead. Without that a dumped menu has no visible cursor, and the cursor is the only thing saying what Enter will do. On a display the plate slides to the row the selection moves to, across into the other column and stretched to its width when the selection changes column, in 90 ms; the cascading menu, the window menu and the tray menu slide theirs the same way. Only the plate moves, so Enter and a click act on the new row at once, and a scroll that carries the same selection elsewhere puts the plate there without a slide: the scroll itself glides, each column on its own, the plate travelling with its row (see Motion). With motion = no the plate lands and the scroll jumps in the same frame.

Search reaches the fixed rows too. Every one carries synonyms, so typing wifi finds the network manager, and the hits are appended under a rule. A search over the application index alone would answer wifi with an empty list on a machine whose network tool is three rows up the same menu. The routes are searched beside them.

Applications on the installation medium are listed under their category with a medium icon, and under INSTALL FROM THE MEDIUM in a search. A row means open this: the pack is installed if it is not, and the application opens. The list is read from the medium’s own index rather than over a socket, because the search runs on every keystroke.

[box] marks a boxed application, because the first launch of one costs a container start and that is something a person is entitled to know before clicking. The mark is the shared application index’s alien flag, set from the entry’s Exec line by the same test kdos-openwith applies to its candidates, so the Start menu and the Open With dialog cannot disagree about it.

Pinning is done here, at the right edge of a row; the mark is checked before the row’s own action, or clicking it would launch the application and close the menu under the hand.

The five power and session verbs sit together in one row of icon buttons in the footer: Lock Screen, Suspend, Restart, Log Off and Shut Down, so every one is reachable with a pointer without knowing a key or a right-click. The buttons are three cells each and carry no label; with nothing typed, the search field describes the selection, so hovering a button turns the field into its name and purpose. They are also reachable from the keyboard, because a search walks the power row beside the others: typing sleep or reboot finds the verb and Enter runs it. Suspend asks nothing, because it is the one power action that undoes itself; Restart, Log Off and Shut Down ask for confirmation first. Lock Screen runs kdos-lock, Suspend, Restart and Shut Down are kdos-power verbs, and Log Off sends SIGTERM to kdos-comp by exact name, because kdos-powerd knows nothing about a session.

The category you were last in is preselected. It does not open, which costs nobody a keystroke and saves one for somebody who lives in Graphics.

kdos-start

kdos-palette and kdos-launcher

Super+Space opens kdos-palette: one input row, one result list, and six sources searched at once: windows, applications, routes, settings pages, files and chords. kdos-launcher is the same program showing applications only, on Super+D. Both are drawn as a centred overlay of 78 by 24 cells, and clicking outside it closes it.

The heading order is fixed and is not the ranking. Windows come first, because the cheapest thing to want is the window you already had; then applications, routes, settings, files, chords. The score orders the rows inside a heading, so a very good file match never climbs above the window you were just looking at. A heading with no hits is not drawn, and each kind is capped so one source cannot crowd out the rest.

Files are searched only after three characters. Every other source is a table already in memory; the file source forks fd, and doing that per keystroke is a directory walk per keystroke. Its lines arrive over several frames and are merged into the list as they come.

A chord row shows the chord and does not press it. Enter runs a chord’s program where the chord runs one, and otherwise the row says which keys to press. A client cannot fire the session’s own actions and must not be able to: synthesised input over the surface socket is a way to drive somebody’s desktop for anything that can reach it, which is the rule the protocol is shaped around. The row is still worth having: “what was the chord for tiling” is asked far more often than a chord is rebound.

A row is named by what it does, which is not the action field. The action names are those of labwc, the compositor kdos-comp is forked from, so nearly every row would be the word Execute; those rows are named by the command they run instead.

Super+Space opens this and not the root menu. A pointer wants rows and a keyboard wants a search, so the taskbar’s Start button and the right button on the desktop still open menus. The second step of kdos-keys’ first-run tour, “search for anything”, shows whichever chord binds the palette, and selftest.sh fails when the shipped rc.xml leaves any of the tour’s four steps unbound.

kdos-menu

kdos-menu draws the Applications, Places and System menus, and the taskbar’s window menu.

The compositor’s root-menu file, ~/.config/kdos-comp/menu.xml (copied from /etc/skel), lists no applications. The compositor’s built-in default is a compositor’s menu rather than a desktop’s, and an application menu built at compositor startup would be the one that went stale. Applications, Places and System are this program, reading the same entries the launcher and the panel do.

The taskbar’s window menu carries Restore, Minimize, Maximize and Fullscreen; then New Window when the window’s app_id resolves to a desktop entry; the application’s recent files when it has any; Pin to taskbar or Unpin from taskbar, again only for a resolved entry; and Close. For a group of more than one window it adds Minimize all (n), Restore all (n) and Close all (n) under Close, above the window titles. It reads the window’s own state, so Maximize says Restore Down when the window is maximised and Fullscreen says Leave Fullscreen when it is fullscreen, rather than being toggles whose direction nobody can see, and Restore all is drawn only when some of the group is minimised, counting those. A row that is always there and does nothing most of the time teaches people to stop reading the menu.

--windows APP_ID is the member list a left click on a grouped window button opens. It has one row per window, titled by the window’s title or, without one, its app_id. A minimised window is marked with a dot, not hidden, because the list is how you get it back, and the focused one is marked with a square. Under a rule come Close all, Minimize all, and Restore all (n) when some of the group is minimised.

This is a taskbar row’s menu, not a frame’s, and the two are different lists. This one is per application and carries the verbs for a whole group of windows; the frame’s own menu, on Alt+Space or a right press on a title row, is per window and carries every verb that frame has.

The window menu waits for the application’s own windows before deciding it has none. A compositor announces its toplevels inside the connect call, but a server that answers later leaves a list read at once empty whatever is on screen. The menu reads until a window carrying the app_id it was given arrives, or half a second passes; waiting for any window would end the wait on somebody else’s row and leave the menu with nothing to draw. After that it prints kdos-menu: no windows for <app_id> and exits without drawing. That message and this display server does not offer a window list are different answers on purpose: the second is asked of the display backend rather than of a count, because a count of zero is also an ordinary empty desktop.

Move and Size are deliberately absent: the protocol the panel uses has no request for either, and a menu row that did nothing would be worse than the title-bar drag that is the move.

The menu is titled with the box qualification when there is one, so it says the same words the taskbar button did.

kdos-desk

The desktop, its icons, and its own context menu. It is a background layer surface with on-demand keyboard, started per output by the compositor.

It claims the whole surface. Claiming only the cells its icons occupy lets a click on bare wallpaper reach the compositor’s root menu, at the cost of the desktop having no menu of its own, so creating a folder would be reachable only by right-clicking an existing icon, and on a fresh login the only icons are two pinned places.

So the desktop answers its own wallpaper: New Folder, New File, Sort Icons, Refresh, plus Applications, Change Wallpaper, Display Settings and Settings, and the verbs that act on the whole desktop: Show Desktop, Screenshot and Lock Screen. The desktop’s own right press is where a root menu has always been, and this is the surface that owns it, so a verb a chord runs is a row here too, carrying the same command the chord does. It is a management request, so this surface asks for manage: it is the session’s own chrome, started beside the panel by the compositor, which is the line that privilege is drawn on.

A verb reaches the table only where the machine can answer it, and desk_verb() is the one place that knows how each is answered. Show Desktop is ToggleShowDesktop on the compositor’s command socket, Lock Screen spawns kdos-lock, and Screenshot spawns kdos-shot screen, the command rc.xml binds to Print, so the row and the key take one screenshot in one way. A row for anything the compositor has no action and no program for would be a menu row that does nothing, which misstates what the machine can do.

Beside the wallpaper rows, an icon’s own menu adds Rename, and the Trash icon adds Empty Trash. Refresh re-reads ~/Desktop, and Sort Icons cycles the order the grid is sorted in.

The menu is two halves

The file verbs come from libkxdg’s table, the same one kdos-pick and mc’s F2 read, so a verb appears on all three at once. A rule follows, then the rows above, which only a desktop can answer. The rule is drawn only when both halves have something.

On bare wallpaper the shared half is masked to the verbs that mean here: Open Terminal Here, Find Here, Add to Places, Git Status Here. Open, Share and Move to Trash read as acting on the thing, and on the wallpaper there is none: a Move to Trash there would act on ~/Desktop itself.

Two shared rows keep a local action, and the row stays the table’s. Open goes through the desktop’s own opener, because a .desktop icon is an application to run rather than a file to open and the Trash icon is a place; handing either to the MIME chain would make the menu row mean something different from Enter on the same icon. An icon that is a .desktop file runs through sh_launch() with the Terminal, X-KDOS-Term, X-KDOS-Float and X-KDOS-Size keys this surface parsed, the same call the Start menu makes, so an icon and a menu row for one entry cannot open two different windows, and a boxed application dropped on the desktop reaches the session rather than being forked beside it. Everything that is not a .desktop icon goes to kdos-appbox open. Move to Trash asks first and refuses the two pinned places: `kdos trash

` confirms nothing, which is right for a prompt and for `mc` and wrong for the row sitting beside `Delete` on this surface. The context menu is drawn into its own grid, since this surface owns the screen and a popup here is not a second surface. Its local rows carry a scope (icon, wallpaper, or both) because two menus would be two places for New Folder to drift. ### Selection and the keyboard Nothing is selected until something selects it. The desktop opens with no highlight. `Escape` puts the highlight down from any icon (this is the `Deselect` rung on the Esc ladder), and a press on bare wallpaper does the same, because the menu that opens there acts on the folder and a highlight left on a file names something none of its rows touches. An arrow, `Tab` or `Shift+Tab` on a desktop with nothing selected selects the first icon and steps no further: a keystroke that reached for the grid and stepped inside it lands on an icon nobody aimed at. The ends of the grid stop rather than wrap, because a grid is a surface and a jump from the last icon to the first is the width of the screen for a key that means one cell. `Home` and `End` name a cell rather than a step, so `End` with nothing selected means the last icon. The hint row is drawn only while the desktop holds the keyboard. This is a background layer with on-demand keyboard: the compositor gives it the focus on a press on the icon layer and takes it back on the next press on a window, so between boot and the first click the arrows, `Enter`, `Del` and `Shift+F10` are not answered and are therefore not named. `Enter open` and `Del trash` are named only where there is a selection to act on, and `Arrows select` takes their place where there is not. The name editor is left by Escape and by a click, and its entrance is the pointer. New Folder, New File and Rename put a line editor on the status row, and the menu that opens them is reached with the right button, so a state a pointer can enter must be one a pointer can leave. A click abandons the half-typed name rather than applying it, and the press is then handled like any other, because a click that visibly did nothing is how a person concludes the desktop has stopped answering. Escape is the Cancel rung of the window contract's own ladder, so there is one cancel and not two. Without the click, an editor opened by the pointer swallows every pointer event over the whole desktop, and on a display that gives this surface no keyboard, that is a state with no exit at all. `~/Desktop` is created if it is missing. ### The wallpaper `kdos-desk` paints no ground of its own. The wallpaper is a PNG the compositor draws and this surface is transparent over it, so anything painted here would be a rectangle of opaque cells on somebody's photograph. What it draws is the icons, their labels and the selection, and nothing behind them. Transparency has to be asked for. `libkcell` paints every cell including its background slot, and the ordinary shm buffer is `XRGB8888`, which carries no alpha, so whatever `KT_BG` is, it would land opaque and the wallpaper would disappear the moment `kdos-desk` started. The background role therefore asks `libkwl` for an ARGB surface and turns on `libkcell`'s transparent-`KT_BG` mode: every cell the desktop does not write is cleared to zero and the compositor's wallpaper shows through, and a cell that does carry something (an icon glyph, a label, the selection bar) is painted normally. Which image, how it is cut and whether there is one at all are `wallpaper` in `comp.conf` and the compositor's business; see [kdos-comp](/docs/kdos/04-programs/kdos-comp.html#the-wallpaper). Icon labels stay readable over a photograph because they paint their own `KT_BG` rather than letting the ground show through. ## kdos-pick The file chooser, and with a browse flag the file manager. This is the dialog every boxed application reaches through the portal, and so does a native GTK application on the host, because `/etc/profile.d/10-wayland.sh` exports `GTK_USE_PORTAL=1`. That makes it the one surface on the system that other people's software puts in front of you. As a browser, opening a file hands it to the system's open resolution and the dialog stays up: a browser that closed after one file would be a chooser under another name. The preview pane parses P6 and nothing else, and everything else becomes one. `kdos thumb --ppm` owns the decoders, and the helper forks and hands back a small picture. That is what lets the chooser show a photograph, a PDF's first page or a frame of a video without linking an image library into a dialog that has to build on a bare host. The fork is synchronous and runs in the dialog's idle slot, so a slow helper stalls the dialog for as long as it runs. `Shift+F10` and the right button on a row open the file verbs: the same table `libkxdg` gives the desktop and `mc`'s `F2`, so the same file offers the same things wherever you meet it. A verb whose program is not on the machine is not offered, which is why the list is shorter than the table. On the empty space below the list the right button means *up* to the parent directory, the convention of the two-pane file managers `mc` follows. The hint row does not name `Shift+F10`, and that is the width: this dialog's row is thirty-eight cells and a fourth hint would take the `Esc` one with it. The menu is the one verb here with a pointer path, and the person in front of this dialog is usually holding a mouse. Places is a list on `Ctrl+P`, not a third column, and the width is the reason. The dialog is sixty-four columns and already spends its right-hand column on the preview; a third column leaves a file's name about thirty cells, and a chooser that cannot show a name is not a chooser. The list opens over the file list as the outer of the dialog's two Esc rungs, with the line editor over it. The portal's dialog is this dialog, at this width, and it is not widened for boxed applications: the smallest screen this desktop is drawn for is eighty columns, a sidebar that could be read is about sixteen more than sixty-four, and a chooser needing all eighty would have no frame, no ground and nowhere for the bar. Two dialogs at two widths would also be two layouts and two sets of reference frames. See [Decisions](/docs/kdos/01-philosophy/decisions.html#one-file-chooser-at-one-width). An archive opens two ways, and they are different questions. `mc` mounts a tar, a cpio, a zip or an `ar` as a directory through its own VFS, which is the right answer when you want one file out of one and a view no MIME type expresses. Everything its VFS cannot reach on this image goes to `kdos-openarchive`, which extracts beside the file. Only 7z and rar get a row: that is the intersection of what `ouch` reads and what shared-mime-info can name, and a row for a type the image cannot produce is one nobody can tell is dead. ![kdos-pick — the file dialog every boxed application reaches through the portal](/docs/screenshots/pick.png) ## kdos-settings The control centre. It opens on a grid of labelled pictures rather than a sidebar of words, because a sidebar is a fine way to move between pages once you know what is on them and a poor way to find anything the first time. Escape steps back to the grid rather than out of the program, so the unsaved-changes guard stays at the single exit. `--page NAME` lands directly on a page (`appearance`, `panel`, `desktop`, `hardware`, `session`, `input`, `apps`, `boxes` or `system`, in any case; any other name prints `kdos-settings: no page named ''` and exits 2), because an applet deep-linking into a page and then making you pick it again would be a link that does half its job. `kdos settings [page]` opens it from a prompt and passes the page word through unchecked: `kdos-settings` owns the list of page names, and a second copy in the command would be a second list to keep in step. Nine categories: | Category | Holds | |---|---| | Appearance | Accent, the [phosphor pass](/docs/kdos/04-programs/kdos-comp.html#the-phosphor-pass) (the compositor's CRT-style shader over the desktop), wallpaper, chrome font | | Panel | The taskbar and tray | | Desktop | Icons and workspaces | | Hardware | Screens, network, sound | | Session | Idle, lock and power | | Input | Keyboard and pointer | | Apps | Which application opens what: a fixed row per role (web browser, web page, mail, folder, text, PDF, document, image, video, music, archive), then every type you have set a default for. Each row names the handler `kdos-appbox open` would start and the layer that answered — *your default*, *your addition*, *KDOS default* (the shipped `/etc/xdg` tables) or *installed* (an entry's own MIME types) — through the same `kxdg_handlers()` lookup, so a fresh system shows its shipped defaults. Enter opens the Open With chooser for that row's type, which writes your choice; the page re-reads the tables whenever it is idle, so the change shows without reopening it | | Boxes | Environments and packs | | System | The machine itself | System is a list of ten rows that each open another program: *Resources…* (`kdos-res`), *Power…* (`kdos-energy`), *Disks…*, *Printers…*, *Accounts…*, *Date and time…* and *Sync…* under "The machine", then *Firewall…*, *Updates…* and *Backup…* under "Security and updates". Without them those programs are reachable only from their own chords and routes, and from nowhere a person looking for a control centre would go. *Sync…* is described as calendar and address-book synchronisation through vdirsyncer, but the program it opens is `kdos-connect`, the SMB share form. A closing note says what is not here, namely the service list and the root-owned files (`/etc/kdos/zram.conf`, `packd.conf`, the timers) that are edited directly, rather than implying that the machine is not reachable from here. The tile icon names are checked against the shipped icon set rather than taken from the naming specification, and the blurbs are cut to what a tile holds at eighty columns. ### Every value is a control A number is a `ktui_slider`: press the track, drag it, roll the wheel over it, or click an end cap for one step. A choice is a `ktui_dropdown`, which opens under the row and picks on a click. From the keyboard, Left and Right step a number by its step (held to its range) or move a choice to the previous or next option. Enter moves a choice on to its next option, and opens a number for typing; a second Enter commits what was typed. While a dropdown is open, Up, Down, Home and End move through it and Enter or Space picks. The control answers the first press, not the second. A row is selected and its slider is set by one gesture; a slider that needed the row selecting first would be two movements for one. The drag belongs to the press that began it, so the pointer may leave the column and go on setting the value, and the wheel turns the control it is over and scrolls the page everywhere else. An open dropdown owns the pointer and the keyboard while it is down, because it is drawn over the rows beneath it and a press tested against those rows would pick whatever the list is covering. A text value is the toolkit's own field, a `KtuiField` the loop hands its keys and presses to. A press inside the field places the caret, and a press anywhere else leaves the edit open with what was typed, because clicking away from a field is not how anybody means to discard it. The volume control is a slider. A progress bar drawn where a control belongs is a control the pointer cannot find, and this is the one surface a person opens because they want to change the number. A muted device draws at zero and says `mute` beside it; its real level is still there and comes back with the mute, because a track showing a level while the machine is silent would be the control disagreeing with the speaker. The wheel scrolls every table, through `ktui_table_event`: the wheel scrolls, a press moves the caret, a press on the row it is already on picks, and the right button is Back. That is the same rule `ktui_rows_event` gives a list, and one function's rule instead of a copy per surface. `kch_scrollbar` takes an id precisely so that the bar which was drawn is the thing a press is measured against. ### Reading a form Its two panes each carry a caret and only one carries a plate. The category column and the field list are drawn through `ktui_sel_row()`, which fills the row of the pane holding the keyboard and marks the other's with `►` alone. Two filled panes, the focused one in the accent and the cold one in `KT_DIM` (about 1.6:1 against the page), would give a form of eight numbered rows eight accent-coloured slider tracks and one lit full-width bar following the pointer, with no hierarchy in it to read. The scope tag and the value column lift off the muted colour when their row is selected. `live` and `login` are the answer to *did that do anything*, and a selected row is exactly when somebody is asking, but muted on the selection fill measures 2.44:1 to 3.38:1 depending on the accent (see [The design language](/docs/kdos/03-architecture/design-language.html#a-selected-row-is-kt_dim-under-kt_text-and-the-accent-is-one-cell)), so left alone the right-hand half of the row would disappear at the moment it matters. `ktui_sel_dim()` is that question asked in one place. `KT_DIM` survives in one role only, and off the fill: an `(unset)` value, where it carries the distinction and is not meant to be read. Every page is cut into sections. A page of forty knobs reads as one undifferentiated list, and the store a row writes (the compositor's file, the panel's, the monitor's) is exactly what a person needs to know before changing one. A section rule says it once for the rows under it, and the caret steps over a rule in whichever direction it was travelling: a heading that could be selected is a row where Enter, Left and Right all do nothing, which is the shape of a control that is broken. The pending counter and the quit guard count every store. A counter that asked `comp.conf` alone would show `0 pending` for a change to the panel's file or the monitor's, and a single Escape would discard it with the guard saying nothing. ### What the pages do not do The Appearance page does not list the accents. Its `Accent…` row opens `kdos-style`, which draws every scheme in its own colours and previews it live; a row of names beside it would be a second way to choose one, and the worse of the two. The Input page is notes and one way in. The pointer and the keyboard are both the compositor's, and both are `rc.xml`'s rather than this page's: the layout comes from `/etc/keymap` by way of `kdos-desktop`, and a row that wrote a file no program opens is exactly the "change a thing, see nothing" this window promises not to be. `Keyboard shortcuts…` opens the card. The Boxes page is the configuration half of box management; the runtime half is `kdos-box` and the monitor's Boxes page. It never writes a profile. It reads them, and writes by running `kdos-box`, which is the writer a person at a prompt reaches. That is what makes "a box created here is identical to one created by hand with the same answers" true rather than approximately true, and it keeps the three things `kdos-box` knows and this page must not re-derive: which keys the container engine can enforce, which it cannot, and that a namespace change needs the box recreating. Only the keys that changed are passed, because the writer rewrites the file from what it loaded and passing everything would also rewrite the keys this page does not show. A box a launch created is always a row, profile or not, because an application pack gets a box at first launch and nobody ever describes it. Such a row reads *no profile yet* rather than showing defaults as though somebody chose them. The name is editable only while creating, since renaming is an operation with no verb, and the network warning for a registry base is a row rather than a footnote. ### What it writes All files are in `~/.config/kdos/` (or `$XDG_CONFIG_HOME/kdos/`). Nothing is written until you apply; the footer then says how many changes took effect now and how many wait for the next login. If a file cannot be written, the footer says `could not write N file(s) — nothing else changed` and the changes stay pending. Every file is written through a temporary file, `fsync` and `rename`, so a crash mid-write leaves the old file intact instead of half of the new one. | File | Applied | |---|---| | `comp.conf` | `SIGHUP` to `kdos-comp`; each key applies on reload or at the next login per the [compositor's own split](/docs/kdos/04-programs/kdos-comp.html#configuration) | | `panel.conf` | `SIGHUP` to `kdos-shell`: the bar on screen re-reads it | | `res.conf` | `SIGHUP` to `kdos-res`, which re-reads it | | `term.conf` | `SIGHUP` to `kdos-term`. An open window applies the accent, `paste_guard`, `image_cells` and `images = no`; the other keys apply to windows opened afterwards, and a window whose font was stepped keeps its stepped size. See [kdos-term](/docs/kdos/04-programs/kdos-term.html#changing-it-while-windows-are-open) | | `launcher.conf` | No signal. Its one key, `files`, is written here and read by no program: see [known gaps](/docs/kdos/06-reference/known-gaps.html) | | The user's `menu.conf` | No signal: the Start menu reads it when it comes up | In `menu.conf` this page owns one `@` setting and no route at all. Every other line is copied through byte for byte, which is what makes writing into a route table safe. Each signal is sent with an exact name match (`pkill -x`), so it reaches only the process whose name is exactly the one given. A pattern match on `kdos-res` would also reach the setuid `kdos-resctl`, which handles no signals and would be killed by the `SIGHUP`. ![kdos-settings, which opens on a grid of labelled pictures rather than a sidebar of words](/docs/screenshots/settings.png) ## The device managers The surfaces in this section are windows over services that also work from a terminal: NetworkManager, BlueZ, ALSA and PipeWire, `kdos-mountd`, CUPS. `kdos-net`, `kdos-bt`, `kdos-audio`, `kdos-disks` and `kdos-print` share their chrome from `libkchrome`, and with it three things every classic control panel has: - A header band: accent-filled, two rows, an icon and a subject line saying what its subject is doing right now. "Am I connected" is the question the window is opened to answer. - Group headings where a list has groups, so eight rows read as two groups. - Real buttons, labelled with verbs, clickable, each enabled from the selection. A Connect that fails when pressed teaches people to stop trusting the row it was on. `kdos-devices` and `kdos-connect` use their own layout and draw no header band. `kdos-devices` has its own sections and a hint row rather than a header band, group headings and a button bar; `kdos-connect` draws the shared buttons under a layout of its own. Each of these opens either as a popup anchored to the panel or as a centred window, depending on what opened it. From the panel it is the bar's own popup, sized like one and dismissed by a click elsewhere; typed by name or opened from the Start menu it is the application, centred, full size and staying up, because somebody who went looking for the network tool will look at something else in the middle of using it, and a pairing confirmation must not vanish because the pointer went to the device on the desk. One flag decides both: `--at-bottom X Y`, which the panel passes with the pixel position of the applet that was clicked. `kdos-net`, `kdos-bt`, `kdos-audio` and `kdos-devices` all accept it, so a script can open any of them as a popup (for example `kdos-net --at-bottom 1600 1040`), and without it each opens as a centred window. ### kdos-net `kdos-net` talks to NetworkManager over the system bus. The list does not reorder under the pointer. Signal strength moves on its own, so the list is sorted once per refresh and the selection follows a kind-qualified key rather than the row index: an SSID for an access point and an object path for a saved profile, because a VPN may be named the same as a network. The passphrase typed here is written into the profile as it is created. Every later question belongs to `kdos-netagent`, because the service raises a secret request against its registered agents rather than against whichever program started the activation. Saved VPN and WireGuard profiles are rows too, at the left margin under the radios, because they belong to no radio. Enter is a toggle, not a join, since the service refuses to re-activate something already active. A profile's type is not a property of its connection object (only Unsaved, Flags and Filename are), so it comes from one `GetSettings` per path, cached, and a row appears the moment that answer lands. The window lists and toggles them; it does not create one. `h` shares the machine's network over its own radio, where the radio reports the access-point capability. That is `ipv4.method = shared`, which starts a DHCP and DNS server on the access point's interface and installs a NAT table of its own, so `/etc/nftables.conf` leaves forwarding and those two ports open for the shared subnet. A change made through `kdos-firewall` while a hotspot is up leaves its NAT in place: applying a rule change reloads the whole file, which replaces only the firewall's own `inet filter` table, so the hotspot's `nm-shared-*` table and rootful podman's `inet netavark` table survive the reload. ![kdos-net: the header band says what the subject is doing now, and the buttons are enabled from the selection](/docs/screenshots/net.png) ### kdos-netagent `kdos-netagent` is not a window that opens from the panel. It starts with the session, holds no display while idle, and raises one dialog when NetworkManager asks it for a secret. Without it the service fails such an activation in silence, because it never prompts on its own. The reply is deferred the way `kdos-bt`'s is. The box is a toplevel rather than a layer surface, because the compositor focuses a toplevel when it maps and focuses an on-demand layer surface only when it is pressed, and a passphrase field that swallowed the first keystrokes would be worse than none. It stores nothing, so a request without the interaction flag, which is the service polling its agents for saved secrets, is answered at once rather than with a dialog. ### kdos-bt `kdos-bt` registers a pairing agent, without which a keyboard cannot be paired at all: the service asks the agent to confirm a passkey and refuses the pairing when nobody answers. The confirmation is a deferred reply (the handler retains the message and returns without replying), because a handler that sat in its own loop would stop answering the service. What a headset actually sounds like is WirePlumber's decision, not this surface's. The profile a device negotiates (A2DP for music, HFP for a call) and the codec inside it belong to the session manager; `kdos-bt` pairs, trusts and connects, and the audio graph is [PipeWire's](/docs/kdos/03-architecture/session.html#audio). The `pipewire` port enables the AAC, aptX, LDAC, LC3, Opus and G.722 codecs in the bluez5 plugin beside the built-in SBC, FastStream and HFP codecs, so a device gets what it asks for rather than the lowest-quality codec the specification mandates. ### kdos-audio `kdos-audio`, on `Super+F3`, answers the questions the panel's volume applet cannot: which device the sound is coming out of, why it is the wrong one, and how to get the headphones connected. It has two panes, moved between with `Tab`. | Pane | Lists | Keys | |---|---|---| | Outputs | Every ALSA card, with its volume, and every PipeWire sink when a PipeWire graph is reachable | `Enter` makes the row the default, `Left`/`Right` change its volume by 5%, `m` mutes it | | Bluetooth | Paired and discovered devices, with their state | `Enter` connects or disconnects, `p` pairs, `s` starts or stops a scan | ALSA is the base layer and PipeWire is optional. The card list is always there, so the window can still switch outputs when PipeWire is what has gone wrong. Making a row the default means two different things: - **A PipeWire sink row** switches the session's default sink through `wpctl`, which moves what is playing now. Without `wpctl` on `$PATH` the window says so rather than pretending. - **A card row** writes `defaults.pcm.card` and `defaults.ctl.card` into `~/.asoundrc`. That steers programs that open the card directly. It does not move this session's sound, because the ALSA `default` device on KDOS is the sound server (unless `$KDOS_ALSA_DEFAULT` names another PCM from `/etc/alsa/conf.d/99-kdos-pipewire.conf`), and the status line says exactly that. Every Bluetooth call is asynchronous. Pairing a headset can take ten to thirty seconds, and the window keeps drawing throughout; the result arrives as the next refresh's device state. Pairing a keyboard, and the passkey confirmation it needs, is [`kdos-bt`](#kdos-bt)'s job. ### kdos-devices `kdos-devices` enumerates cameras by device call rather than through a library, finds who is holding one by walking the process table, and previews a grabbed frame through the shape-matching character renderer. Opening a camera to preview it is using it, so the privacy lamp lights for this program too and the descriptor is closed with the frame. Its scanner section is `scanimage -L` and not `libsane`. Linking the library would pull every backend's shared object and its configuration into the process to ask a question `scanimage` already answers, and the scanning is `scanimage` too. The probe walks a USB bus and the network, so it runs once per refresh and never on a keystroke. Its microphone list is `kpr_sound_pcms()` filtered to the PCMs that carry a capture stream. `kdos-rec` reads the same function, because two surfaces must not give two answers to what a microphone is, and a list built from `/proc/asound/cards` offers an HDMI codec as an input. It also fronts removable media, and it notices an application set on a stick: a `.ktar` on a mounted removable filesystem gets a row under `APPLICATION SETS ON A STICK`, and Enter imports it offline, with every pack verified where it mounts. That is the only route to software on a machine with no network. It reads one directory per already-mounted filesystem rather than scanning the machine, because the surface is waiting for a keystroke and a scan would not fit on a panel tick. The list scrolls through `ktui_table`, because the sections are as long as the machine's devices. The section captions are non-selectable headings: every verb here acts on a device, so the selection steps over them rather than landing where nothing would happen. ### kdos-disks `kdos-disks` is the surface for the `kdos-mountd` verbs: `mount`, `unmount`, `unlock`, `close`, `format`, `smart` and `write`. Ejecting is offered by the removable-media toast (`kdos-mediad`) rather than here. Every privileged operation is a daemon verb and this program runs as the user. It opens no block device, forks no `mkfs` and holds no capability; what it does is draw a list the daemon published and send back a row number, and for an image write, the image, opened as the user and handed to the daemon as an open descriptor. A disks window that needed root would be a setuid binary with a text editor's attack surface. Partitioning is `cfdisk` in a terminal and is not reimplemented. A partition editor is a program in its own right, `cfdisk` is on the image and is what somebody who partitions disks already knows, and it is pointed at the row's whole disk, because a partition editor aimed at `/dev/sdb1` opens the table inside a filesystem, which it reads as an empty disk and offers to write. There is no `fsck`. On a mounted volume it corrupts; on an unmounted one it takes minutes with no progress anybody can read; and a button that started one and could not be stopped would be the most dangerous control on this desktop. It belongs in a shell, run by somebody who has unmounted the volume first. Erase asks for the device's own name to be typed, which is the daemon's rule rather than this surface's decoration. **Write image** (`w`) puts a disk image over the whole disk the selected row is on. It asks for the image first (type a path, or drop the file on the window, which opens the prompt with the path filled in), checks that it is a file, and then asks for the disk's name: `sdb` for a row on `sdb1`, because the disk, partition table and all, is what is replaced. The daemon's worker reports as it goes, and the prompt row becomes a bar reading `writing of ` and then `verifying of `; the window keeps drawing and may be closed without stopping the write. The end is `written: sdb verified` or `not written:` and the daemon's reason. The verb is refused unless `/etc/kdos/mountd.conf` says `write = yes`; every refusal is in [the daemons](/docs/kdos/04-programs/daemons.html#writing-an-image). ### kdos-connect `kdos-connect` is the same shape for a folder on another machine: five fields (server, share, user, domain, password) handed to `kdos-mountd`'s `cifs` verb, with a list of what is mounted underneath. The password is a second frame and never a token, for the reason the passphrase in `kdos-disks` is: the request line is split on spaces. The four names are not checked here, they are checked in the daemon. `mount.cifs` builds its option string by concatenation and escapes nothing but the password, so the allowlist that refuses a comma has to be the daemon's, because a check in a surface is a check nothing else talking to the socket gets. A Kerberos share needs no password at all. *Sign in* chooses between a password and the ticket `kinit` has already put in the credential cache; with the ticket, the password row is not asked for, because nothing about a ticket crosses the daemon's socket: the kernel fetches it through `cifs.upcall`, which reads the cache directly. It is a separate daemon verb rather than an empty password, so a surface cannot ask for a Kerberos mount by accident. *Browse* (or `--browse` on the command line) lists the machines that answered an mDNS and a NetBIOS broadcast at the moment it was pressed. There is no browse master on the image to ask, so the list is a snapshot: machines come and go from it, and an empty list means nothing answered, not that the verb failed. The broadcast blocks, so the frame saying it is running is drawn before the call rather than after it. The list of mounted shares is re-read from `/proc/mounts` after every verb, because a row number is only true of the list it came with. While the form is open it takes every key before the window's shared key handling (the [key contract](#the-key-contract) every surface answers), and `Esc` returns from the form to the list, clearing the password. A text field that sat behind the shared handling would close the window the first time somebody typed a letter that handling gives a meaning to. ### kdos-print `kdos-print` drives `lpstat`, `lpinfo` and `lpadmin`, not libcups and not IPP directly. Those three are on the image, they are what the CUPS documentation tells a person to type, and they are the interface upstream keeps stable; linking libcups would put a second client library and its configuration parsing in the panel binary to re-derive answers three programs beside it already give. It runs as the user, because `/etc/group` grants the desktop user `lpadmin`, the authority CUPS itself defines. That is the difference between printing and mounting: CUPS shipped the privilege split and the kernel did not, so printing needs no daemon of ours in front of it. Discovery is `-m everywhere` and nothing else. IPP Everywhere is what a driverless printer advertises and what CUPS resolves without a PPD, and a driver picker would be a thousand `lpinfo -m` rows to choose from. A printer needing more than that needs its vendor's tooling, and the surface says so. The `file` and `serial` backends are dropped from the discovered list. `lpinfo -v` names every backend CUPS has, and offering one that prints to a file or one that names an empty serial port is offering a queue that will never produce a page. The queue name is derived from the URI rather than asked for, because CUPS refuses a name carrying a space, a slash or a `#`, and a device URI is full of all three. ## Notifications `kdos-notifyd` owns `org.freedesktop.Notifications` and the history; `kdos-notify` is the centre that draws it. The clipboard is split the same way. Sending one is `Notify` on the session bus, and the programs in this tree reach it by four routes. `kb_notify()` double-forks `gdbus` and serves programs with no bus connection of their own, among them the `kdos` command (`kdos notify`, its toggles and reminders), `kdos-shot`, `kdos-share`, `kdos-term` and the panel's toast for the taskbar chord. The panel's battery and full-disk warnings go out on the connection its tray already holds (`sh_tray_notify()`), because opening a second one to say one sentence is a second thing to keep alive. `kdos-mediad` calls `Notify` on its own connection, because it needs the returned identifier to match a click on the toast's buttons, and `kb_notify()` discards it. The compositor and `kdos-appbox` spawn `gdbus` directly, the compositor with a fixed `gdbus call` string from `kdos-idle.c` that has nothing interpolated into it. `kdos-notify` sends nothing at all: it is a viewer of what has already arrived. A notification that expired is not a notification that was read. Every toast joins the history on the way out, whatever took it out (expiry, a click, or the sending application closing it), because the ones nobody saw are exactly the ones the centre exists to answer for. That lands harder here than elsewhere: a boxed application's notification is often the only thing that says the work it was doing has finished. The daemon keeps a ring of recent entries and answers one short connection per request on `$XDG_RUNTIME_DIR/kdos-notify.sock` (see [Filesystem and IPC](../06-reference/filesystem-and-ipc.md#xdg_runtime_dirkdos-notifysock--notifications)): | Verb | Does | |---|---| | `count` | Unseen, total and the do-not-disturb flag, on one line — the panel asks once a second | | `list` | The history, newest first, tab separated | | `seen` | Clear the unseen count | | `open ` | Follow entry *n*'s link | | `forget ` | Drop entry *n* from the history | | `dismiss` | Take the newest toast off the screen, with the protocol's dismissed reason | | `dismiss all` | The same for every toast on screen | | `raise` | Put the last one dismissed back, out of the history rather than copied from it, and without its buttons | | `clear` | Empty the history | | `dnd [on\|off\|toggle]` | Set do not disturb, and answer with the state as it then reads | Unseen is what the badge counts, cleared by the centre being opened and by nothing else. A count that cleared itself on a timer is a count nobody trusts. Do not disturb depends on the history behind it. Silencing toasts without somewhere for them to go would mean losing them; with the ring in place the notification is kept, the badge still counts it, and the sending application cannot tell: the identifier is returned and the close signal is still emitted, so nothing hangs waiting. An urgent notification is shown anyway: a do not disturb that hid a battery-critical warning would be a switch nobody dares leave on. It is one flag, and it is the `dnd` toggle file. The daemon keeps no copy of its own, because a second flag OR'd with the toggle is a state the centre's own button cannot clear, so *Allow Toasts* would leave the toasts silenced and say it had not. The `dnd` verb writes the file and replies with what the file then reads rather than with what it was asked for, so a state directory that cannot be written leaves the button drawn the way things actually are. `kdos toggle dnd`, a chord and a script all set the same switch, and it outlives the daemon. Hovering a toast holds its countdown, because a toast that disappears while it is being read has to be read twice and it cannot be. The remaining time is banked and restored on leave with a floor, so a pointer merely crossing the corner never costs a notification. The border changes colour while it is held, because a countdown that quietly stops is one nobody can tell has stopped, and urgent keeps its own colour, since a warning that changed colour under the hand would be saying something it does not mean. The resume walks every held toast rather than one an index names, because the stack moves under the pointer whenever a toast is dropped. A toast dismisses on click, with the protocol's dismissed reason. The surface takes no keyboard, since a toast must never steal focus, so the pointer is the only way to make one go away early. ## kdos-osd `kdos-osd` draws two surfaces that share only the mixer helpers: the bezel, which takes no input, and the slider, which is interactive. The bezel is what the media keys raise. It takes no input at all, because a transient overlay that answers a click eats every click under it. The slider is what clicking the volume applet opens: anchored, interactive, clickable along its length (a control that can only be nudged in fixed steps is one people give up on), with mute as a labelled button beside it. It is dragged, too: the button is remembered across events, and the implicit grab means motion keeps arriving after the pointer leaves the popup, so a hand that runs past the end of the bar lands at the maximum rather than stopping wherever the surface did. The level carries the same speaker icon the panel applet resolves, so the readout and the popup it opens cannot show two different pictures of one number. The mixer is cached open and re-read through the library's event call. A panel asks once a second, and opening and loading the mixer per tick rebuilds it sixty times a minute. ## kdos-store `kdos-store` lists the boxed applications this machine can build and installs the ones you tick. ``` ┌ Applications ─────────────────────────────────────────┐ │ Groups │ All │ Graphics │ Office │ Installed │ ├────────────────────────────────────────────────────────┤ │ [x] GIMP rt-gtk Create images and edit photos │ │ [ ] Krita rt-qt Digital painting │ │ [·] Inkscape rt-gtk Vector graphics installed │ ├────────────────────────────────────────────────────────┤ │ 2 ticked · ~1.2 GB F5 install F6 import F7 export │ └────────────────────────────────────────────────────────┘ ``` The tab row is `Groups`, `All`, then a tab per category the catalogue actually uses, most-populated first, then `Installed`. The category tabs are built from the data rather than listed in C, so a new category in the catalogue is a tab with no code change. `Other` is not given a tab: it is where a row with no `meta` lands, and a tab named after the absence of information is not a place anybody looks. It computes nothing. `kdos-appbox catalogue` answers what exists and what is installed in one call, with the state as a field, so the three surfaces that ask cannot disagree. `kdos-appbox install` knows how to build one. `[·]` is an application already here and cannot be ticked, because installing it again rebuilds an image that exists and a tick that does nothing is a tick somebody will count. A group row shows `[x]`, `[-]` or `[ ]` for all, some or none of its members. The size is an estimate and the footer says so. What apt resolves on the day depends on the snapshot, and a runtime's layers are counted once on disk however many applications name them. A build runs in a terminal and this window stays alive. Installing is podman and apt, which take minutes for one application and the better part of an hour for a group, and a surface that ran it would be frozen for all of it, with one status line standing in for output that is worth reading when a package fails to resolve. The verb is handed to `kb_terminal()` and detached, so the build survives the window being closed and `r` picks up the result. On a bare virtual terminal there is no emulator to open, and the surface says so rather than naming a window nobody gets. Space ticks, Enter installs the row under the cursor (or ticks a group), `F5` installs the ticked set, `F6` imports, `F7` exports, and `r` refreshes. The button bar carries the same verbs as *Install*, *Import*, *Export* and *Close*. Export writes the ticked set to `~/apps.ktar` and Import reads the same file, so a set chosen on one machine can be carried to another. Any other path is `kdos app export ` or `kdos app import ` at a prompt, and [`kdos-devices`](#kdos-devices) imports a `.ktar` found on a removable stick. `--page NAME` opens on the tab of that name, matched without regard to case. ## kdos-trash What was deleted, when, and where it came from: one row per item, newest first, with `Enter` putting a row back where it was. The Trash icon on the desktop opens it. The list is a table with four titled columns: *Name*, *Size*, *Deleted* and *From*. A press on a title sorts by that column and a second press reverses it; `s` steps through the same orders from the keyboard, and the sorted title carries the direction arrow. Rows that tie keep newest-first, then name order. The name column's right edge, on the title or the rule under it, drags wider or narrower. Sizes end at the column's right edge so their digits line up. `PgUp`, `PgDn`, `Home` and `End` walk the list, and a trash longer than the window gets a scroll bar. Its main purpose is *Put back*: without it, moving a file to the trash would be a slower delete. The desktop's *Move to Trash* and `kdos trash ` put a file in, `kdos trash` lists what is there, and this window is the way back that needs no command line and no remembered name. It calls `kb_trash_*` and nothing else. The specification (the escaping, the `.trashinfo` record, the unique-name walk, the refusal to overwrite whatever is already at the origin) is one implementation in `libkbase`, and a surface reimplementing any of it would be a second answer to where a deleted file lives. The three failures the library distinguishes are said in its own words: a record that cannot be parsed and a file that is already back are different problems, and a person can act on the difference. The Trash icon does not open a file manager. Its path is a directory, and a browser on it shows the escaped names in `files/` with no origin and no deletion date; the record carrying both is in `info/` beside it. A destructive row asks first, and the question is a declared `Esc` rung rather than a flag, so `Escape` while it is up answers *no* and leaves the list exactly as it was. A folder's size is named, not measured. `bytes` in a record is the directory inode rather than a recursive total, so the column reads `folder`: a number that is wrong is worse than one that is missing. ## kdos-peek What is in a file, without starting the application that owns it. *Peek* in the file verbs, `k` on `mc`'s `F2`, and the default handler for the two page formats no desktop application is the default for: CBZ and XPS. It draws PDF, EPUB and FB2 too when asked, but their default handlers are Okular and Calibre's viewer ([Opening a file](/docs/kdos/02-user-guide/desktop.html#opening-a-file)). Four kinds and one decision, taken in this order because the cheap and certain tests come first: | Test | Kind | Shown as | |---|---|---| | The magic bytes of a PNG, JPEG or WebP | Picture | The picture, tiled into the cell grid | | A `.pdf`, `.epub`, `.cbz`, `.xps` or `.fb2` | Document | One page at a time, rendered by `mutool` | | `libarchive` agrees to open it | Archive | The entries, name and size | | No NUL in the first four kilobytes | Text | `less`, in a terminal — and this program exits | Anything left is refused by name, in the middle of the window, rather than shown as mojibake. A directory is refused: `mc` shows directories, and this program shows files. The refusal is taken from `stat` before any display is opened, so a directory fails on the argument rather than after a window has come up, and the message names where to open one. Nothing here decodes a picture. `libkimg` is the one place in KDOS that turns untrusted image bytes into pixels, under a budget checked before any allocation, and a page from `mutool` arrives as a PNG and goes through the same call. The scale and the cut into sprite tiles are `libkcell`'s one implementation, shared with the terminal's inline pictures. The magic is sniffed here as well as inside `libkimg`, because the decision is taken before the file is read: a two-gigabyte video must not be loaded into memory to discover that it is not a PNG. A document is chosen by extension and rendered at the pane's pixel size. `mutool draw -w -h` with no `-r` fits the page inside that box and keeps its aspect, so a resize is a re-render rather than a rescale of what was already drawn. The page count comes from one `mutool info` at open; when it says nothing usable the title shows a page number without a total, because a total this program guessed would be a number that is wrong rather than missing. `libarchive` opening the file is the test for whether it is an archive: the format probe is the same code that would read it, rather than a table of extensions that would disagree with it. The listing stops at 4096 entries, because the entry count is the archive's choice. Text is the pager's. A pager inside this window would be a second implementation of scrolling, searching and line wrapping, and the one on the machine is better than the one this file would grow. `--dump` never forks it: it draws what it would have done, the same split between measuring and acting the panel keeps. Peek is not the default handler for a book or a PDF, because a reader keeps a position, a table of contents and a search, which is the difference between reading one and glancing at a page of it. The Peek verb still shows any of the five, because it asks no handler table at all. On a bare virtual terminal, where the desktop's defaults have no compositor to open in, `kdos-peek` and `epy` are run by name. ## kdos-pix One picture, fit to the window, with the folder it is in as the album. `+` and `-` zoom, `0` is fit again, `Space` and `Backspace` step, and the arrows pan. The folder is the album. Opening one picture opens the sorted list of every picture beside it, so `Space` is the next photograph rather than an error. `kdos-peek` deliberately does not do this: a quick look is about the file somebody named. Zoom works on the source picture. The window keeps a factor and a centre, and what is registered is the crop those describe scaled to the pane, so zooming in reads more of the original's pixels rather than enlarging the ones already on the screen. That is the difference between a viewer and a magnifying glass over a thumbnail. It stops at 800%, where a cell is a colour, and at 5%, where the picture is smaller than the border around it. Fit never enlarges. A 32-pixel icon opened here is 32 pixels; `+` is how a person asks for it bigger, and a viewer that guessed would show every icon on the machine as a blur. A pan is a tenth of what is on the screen rather than a fixed number of pixels, so one press moves the same visible distance at every zoom. A new picture is shown whole, because carrying the previous one's zoom would put somebody at 400% in the corner of a photograph they have not seen yet. The folder is listed by extension, not by magic. It may hold thousands of files, and opening each one to sniff it would be the slowest part of starting a viewer. A named file that is not in the list (an extension nothing here reads) still shows, as a list of one. It handles the three types `libkimg` decodes: PNG, JPEG and WebP. `timg` keeps GIF, BMP and TIFF, which it cannot open, because a row for a type the viewer refuses would be a window that opens and says it cannot read the file. A boxed viewer keeps them all: it has real pixels at the screen's own resolution. The decode, the crop, the scale, the tiles and the draw are `picture.c`'s, shared with `kdos-peek`. ## kdos-find `kdos-find` searches applications, recent files, file names and file contents from one field. It opens on `Super+Shift+F`, *Find Here* in the file verbs, and `f` on `mc`'s `F2`. | Source | From | Searched | |---|---|---| | Applications | The launcher's own index, `sh_apps_match()` | Every keystroke | | Recent | `libkxdg`'s recently-used list | Every keystroke | | Names | `fd` | When the question stops changing | | Contents | `rga` | Only after `Ctrl+G` | Each source is somebody else's answer. A walker written here would disagree with `fd` about hidden files, ignore rules and symlinks, and a content search written here would be `rga` without the archive and document readers that are the point of it. The two forks stream. A search over a home directory is seconds, and a surface that waited for it would be a window that cannot be closed while doing the one thing it is for. The child writes into a pipe read in the poll loop, and `Escape` kills it, the same shape `kdos-status` uses. Contents are opt-in. Names come back in milliseconds and are what somebody usually means; a content search reads every file under the directory, and starting one per keystroke is a machine that never stops working. Once asked for, it follows every later question. Names are searched literally, not as a regex (`fd --fixed-strings`). Somebody typing `report.c` means the dot, and a pattern that swallowed it would match `reportxc`. Hidden files are included (`--hidden`), because somebody looking for `.bashrc` means it, and `.git` is excluded, because nobody means `.git/objects`. Contents are literal as well, and `rga` reports at most three matching lines per file. A result opens through `kdos-appbox open`, the one resolution the desktop, the chooser and `mc` all use. An application row is resolved to its entry again at the moment it is chosen, because a boxed application's `Exec` reads `kdos-appbox -b run ` (`kdos-appbox run ` for an application belonging to no pack), and its id is not that command: handing the id to `run` starts nothing at all. A consumer that recognised the line by a fixed prefix would see only the packless half; the binary and the verb are what identify it. The entry is then handed to `sh_apps_launch()`, the launcher's own call, so a row found here and the same row in the Start menu open the same window. The root is the directory the verb named, else home. A search with no root is a search of the filesystem, which is not what *Find Here* means and not what a chord with no context should start. ## kdos-rec Pick an input, record through `sox`, watch the level, keep the file. *Recorder* in the Start menu, *Recording…* under Settings' Hardware page, and `F1` opens `/usr/share/kdos/doc/rec.txt`. The input list holds PCMs rather than sound cards: `kpr_sound_pcms()` reads `/proc/asound/pcm`, one line per PCM, and the list keeps those carrying a capture stream. An HDMI codec is a card with four playback PCMs and no capture stream at all, so a picker built from `/proc/asound/cards` offers a monitor's audio output as an input, and the recorder that accepts it fails to open with a message about the device rather than about the choice. `Default` is the first row and always present. It is the one that works while the session's PipeWire holds the card, through `pipewire-alsa`; a `hw:C,D` row names one PCM directly, which is what answers when nothing else holds the card: a bare terminal, and the [QEMU test harness](/docs/kdos/05-developer/testing.html#the-qemu-rig). A live session can therefore refuse a `hw:` row with `EBUSY` while `Default` records. The list is the kernel's PCMs, not PipeWire's graph, and the surface shows `sox`'s own message rather than an empty file. The input is named on argv, `-t alsa hw:C,D`, and never left to `rec` or `-d`: sox's default-device probe opens a card for playback, so it skips a card that has no DAC. ``` sox -q --input-buffer 3200 -t alsa hw:1,0 -t raw -e signed -b 16 -c 1 -r 16000 - ``` `sox` does the part only `sox` can do: open the device at whatever rate, format and channel count it has, and resample. `whisper-cli` requires 16 kHz mono s16, so the file this writes is the file the transcriber reads with no second conversion. `kdos-rec` writes the 44-byte header itself and rewrites both length fields every tenth of a second, because a header written only at the end leaves a file no player will open whenever the machine goes away mid-recording. Files land in `~/Recordings`, made on demand. The level is the recorder's own peak over the bytes it wrote, not `sox --show-progress`, which is fourteen text steps two decibels apart on a throttled repaint. One entry per tick, never per read: a read's size is set by the pipe rather than by time, and with one entry per read the column spacing would follow the scheduler rather than the clock. The ring holds `max|s| / 32768`, a fraction, and `ktui_sparkline` is given `vmax = 1.0`, pinned rather than fitted, because an autoscaled meter paints a microphone's noise floor as a solid bar, which is the one reading a level meter must never give. dBFS is a table of integer peak thresholds at half-decibel midpoints, so the printed number is the nearest decibel and nothing in this tree gains a maths library for one logarithm. Clipping is the word `CLIP` and not a red chart: `ktui_sparkline` takes no foreground slot, and adding one to a function that the build orchestrator `kdosbuild` and `kdos-res` also call is a wider change than this surface earns. Recording refuses to start while the input is muted, and says so on the row. A recorder that quietly records a muted input is the worst thing this surface can do. One button carries two verbs, and the model decides which. With a model on the disk it says *Transcribe*; with none it says *Get model* and opens a terminal on `kdos speech get`. A control that is permanently greyed teaches that a feature does not work rather than that something is missing, and this window is the only place anybody finds out a model is missing at all. The download is not done here: a progress bar, a cancel, a disk-full and a network that went away all already exist in that command. Nothing is waited for either; the window keeps looking while there is no model, so the button becomes *Transcribe* on its own when the file lands. Whether `whisper-cli` is on `$PATH` is asked only when a child is started, because `$PATH` is not frozen in a reference frame and a button that changed shade with the host's packages could not have one. The three model locations, `$KDOS_WHISPER_MODEL`'s exclusive semantics and the four-byte magic test are in [configuration](/docs/kdos/06-reference/configuration.html#speech-to-text-models). KDOS ships the `base.en` model in `/usr/share/whisper.cpp/models`, which *Transcribe* uses unless you have one of your own. `kdos speech get` fetches another. This surface transcribes a file. `whisper-stream` on the image transcribes a live microphone and is a terminal program with no desktop verb in front of it: it opens SDL's audio device, which raises no window. Both want the same model. ## kdos-keys The keybinding card, on `Super+F1`, in six sections: launch, window, workspace, tools, media, system. It reads `rc.xml` as the compositor loaded it. One reader and one writer: a second copy of the table is a copy that goes stale, and a card that is confidently wrong is worse than no card. The card owns only the wording and the grouping; an action it has no row for is dropped, so a chord added to `rc.xml` and not worded in `keys.c` works and appears nowhere a person would look for it. The reading itself is `chords.c`'s, shared with the palette, and it skips commented-out bindings and a run-or-raise binding whose program is not installed; `selftest.sh` checks both against a fixture. The card is searchable. An input row filters the rows through `kb_fuzzy()`, the same matcher the palette and the launcher use, so three surfaces cannot rank one query three ways. Both columns are searched, because somebody after the tiling chord may type `tile` or may type `Super`. A section whose rows all fail the filter draws no heading. The field is always drawn rather than appearing once typing starts: the card is what people open when they do not know what to press, and the one thing it must say is that it can be asked. `Esc` clears a query before it closes the card, through the same layer contract every other surface uses, so the hint row says which of the two the next press does. `--print` writes the same rows to standard output, two columns at 132 characters, form-fed between pages, for a printer and for a wall. It runs before any display server is opened, so it works over `ssh`, from a script, and on a machine whose session is not up, which is most of the times somebody wants the card on paper. A print run has no screen to read the focused program off, so `--program NAME` is how it says which page it wants; without it, `--print` prints this desktop's chords. `--first-run` is the login spawn's flag. It puts a four-row tour above the list (open a terminal, search for anything, switch workspaces, reach another terminal), with each row's chord looked up in the same parse the list came from, so a rebound terminal moves the tour in the same edit and a step nothing binds is absent from the tour rather than wrong in it. The hint row names the chord that brings the card back, which is the one frame whose reader has not already used it. The tour is dropped below twelve rows, where what it pushes off the bottom is the card itself. This program also decides whether the welcome is due, from `~/.config/kdos/first-run`, so a session that asks at every login still shows it once. ### The focused program's own keys `Tab` shows the focused program's own keys, for the three that publish them, and is offered only where a reader exists: a Tab that leads to an empty screen teaches that the feature is broken rather than that the program does not publish its keys. The three are not alike, and the page says which it is by naming the program in its title. | Program | Read from | What it does not show | |---|---|---| | `tmux` | `tmux list-keys -N -T prefix`, live, plus `show-options -gv prefix` so a row reads `C-b c` and not `c` | The `root` and two `copy-mode` tables — 161 of the 267 default bindings, none reachable from the prompt the card is drawn over. `-N` lists only bindings carrying a note | | `mc` | `/usr/share/mc/mc.keymap`, then `/etc/mc/mc.keymap`, then the user's `~/.config/mc/mc.keymap`, each layered over the one before | The `Ctrl-x` second table, the editor and the viewer. It shows the keymap files, not mc's live bindings, so an action mc has a built-in key for that no file mentions is absent | | `micro` | `bindings.json` — the overrides only | Nearly everything: micro's defaults are compiled in, no flag prints them, and this image ships no micro configuration. On a fresh install the reader answers nothing and the page does not appear | ## kdos-style How the screen looks, on `Super+Ctrl+Shift+Space`, in two pages, the accent and the font, because they are one question and a second window would be a second thing to find. `kdos-style` is the picker. `kdos-theme` is a different program: the artwork generator that `kdos theme` runs as `kdos-theme gtk|icons|cursors`. ### The accent page One row per scheme, each drawn in its own colours, with the desktop repainting live as the highlight moves. `Enter` keeps and `Esc` puts back the one it opened on. There are eight schemes, from the table in `libkcolor`'s `kcolor.h`: phosphor, amber, ice, bone (the default), norton, borland, perfect and paper. The swatches are the only literal colours on this desktop. Everything else draws in named slots so one word repaints all of it, and a swatch that took the accent in force would show eight identical rows; `preflight.sh` names this file as the exception rather than dropping the check. The blocks come from the glyph table like every other picture-character here, so the swatch is still a swatch on a terminal with no UTF-8. A preview is half a theme. `kdos theme --preview` writes the accent state file and signals the session, and generates none of the GTK, icon, cursor or foreign-configuration artefacts, which take seconds and are read by programs that are not running, so the desktop moves and a GTK or Qt application, boxed or native, does not, and `Enter` runs the real switch. Leaving any other way, including a `SIGTERM`, puts the original back, because a picker killed halfway would otherwise leave the desktop in an accent no other artwork had been regenerated for. The window sizes itself from the scheme table, so an accent added to `kcolor.h` needs no edit here. ### The font page `--page font`, and Left/Right reach it. The page stays a page rather than becoming a tab that can disappear, because `--page font` is a route a script holds. The list is fontconfig's monospace families (`FC_SPACING == FC_MONO` and nothing else), deduplicated by family, sorted and scrolling under a fixed frame, because a proportional face does not fit a cell grid, and offering one is offering a broken screen. One row per family with a sample beside it, and the sample is drawn in the font currently in use: the arrows load the highlighted face live in this window, so the row under the highlight is shown in itself and so is every other cell here. The highlight opens on the family in force, or on the first row where the current font is one no row names, such as an alias like `monospace`, which fontconfig resolves and never lists. `Enter` is the only thing that writes. It puts the family into `chrome_font` and `panel_font` in `~/.config/kdos/comp.conf`, each key keeping its own size (the bar is glanced at and the menus are read), and copies every comment, blank line and unknown key through untouched. Every other surface is its own process and reads the file at its next start, so the rest of the desktop follows then and not before. Leaving any other way puts back the face this window opened on, which is what a person who was looking rather than choosing meant, and a family that will not load is never persisted: the old face stays on the screen and the file is left alone. Where the list is empty the page draws two lines: that there is nothing to pick, and that the font is set by `chrome_font` and `panel_font` in `~/.config/kdos/comp.conf`. An empty list with neither reads as a list still loading. That branch means this machine carries no monospace family at all, or a `--tty` run inside a terminal that owns the font. ## kdos-saver `kdos-saver` is attract mode: seven effects under eight names (`bounce` is `art`), `random` to pick one per start, and `off`, which draws nothing and connects to nothing, so a caller can start it unconditionally. | Effect | Draws | |---|---| | `art` | Drifts a loaded grid and bounces it. The default | | `bounce` | The same effect under the name it is known by | | `rain` | Falling columns of shade | | `matrix` | The same columns in characters | | `pipes` | Pipes that grow and turn, clearing when the screen fills | | `starfield` | Stars flying past | | `fire` | A climb through the palette, dim to urgent to text | | `clock` | The art, with the time in block digits as its grid | `--mode NAME` is the only way to choose an effect, because a name read out of a file under `/etc` would make a test frame draw whatever the developer's own machine says. Any other name prints the usage line with the list of modes and exits 2. `--fps N` sets the frame rate and `--output NAME` the screen. The compositor's idle policy does not start it: idle spawns `kdos-lock` and no saver. It runs when you press `Super+Shift+L` or type `kdos-saver`. `art` and `bounce` are a transform over one loaded grid, and the grid is a file, `~/.config/kdos/screensaver.txt` over `/usr/share/kdos/screensaver.txt`, so the picture belongs to whoever is looking at it. It is not `logo.txt`, which is the login banner's and must not change when a screensaver does. The other six need no file, which is what a machine with no art falls back to. The vocabulary is the virtual terminal font's 512 glyphs. The density effects take the same three-level ramp the rain does, `pipes` has its own ASCII tier, and `matrix` and `starfield` are ASCII outright: an effect drawn out of what `ter-kdos32n`, the virtual-terminal font, lacks is blank on `tty1` and correct in a terminal, a failure that no test in a terminal would catch. It never watches input and claims no pointer region. A screensaver that decided for itself when to go away could decide wrong, and one that took the keyboard would be a lock screen with no password. ## kdos-ime The input-method candidate window, drawn as cells. When you type Chinese, Japanese or Korean through fcitx5, the list of candidate characters you choose from is this window. It exists because the candidate window is otherwise the one thing on this desktop that is not cells: an input engine draws its own with its own renderer, which on a character grid is a rounded antialiased panel sitting on top of a text-mode desktop. It speaks kimpanel, the generic D-Bus panel protocol of the input-method framework and the same mechanism KDE's plasmoid and the GNOME extension use. So this is not an input method and knows nothing about any language: the engine decides what the candidates are and this draws them, with `libkchrome` chrome and `libkcolor` slots through `libkdisp`. Both halves of the protocol are implemented, because it is two. The preedit, the auxiliary string and the show and enable flags arrive as signals on `org.kde.kimpanel.inputmethod`; the candidate list arrives as a method call, `org.kde.impanel2.SetLookupTable`, on the panel's own object. A panel that only listened would show a preedit with nothing under it. The signal match names no object path, because fcitx5 exports `/kimpanel` while other kimpanel implementations use `/kimpanel/inputmethod`, and only the methods this can actually answer are declared, because the engine reads the introspection to decide what to send. Starting it is selecting it. fcitx5's kimpanel module has a UI priority above its own classic interface and becomes available the moment `org.kde.impanel` has an owner, so the session bring-up starting `kdos-ime` is the whole of choosing this panel over the engine's own. Only one process can own `org.kde.impanel`, and the protocol has no way to hand the name back. A second `kdos-ime` refuses to start and names the program that owns it, rather than taking the name and leaving whatever was drawing the candidates believing it is still the panel. A session bus outlives a session restart, so a panel left by an earlier session still holds the name and a second exits saying so, with the candidate window then drawn by a process nothing is feeding. `kdos-desktop-start` takes the name rather than asking for it, and kills its own panel again when the compositor exits. The engines a key can reach are a shipped file, `~/.config/fcitx5/profile` from `/etc/skel`, because the configuration tool upstream ships, `fcitx5-configtool`, is a separate project that is not a port. Its group names `keyboard-us` first (a login types Latin), then `pinyin`, `anthy` and `hangul`, which are the engines the image installs. fcitx5 started with no such file has a group holding nothing but the keyboard layout: `fcitx5-remote -s pinyin` then silently does nothing, every keystroke arrives as Latin, and no candidate window is ever asked for. There is no configuration surface for the engines themselves. See [known gaps](/docs/kdos/06-reference/known-gaps.html). ## kdos-doc `kdos-doc` is the documentation viewer, on `Super+/`, and what `F1` opens on the eight surfaces that claim a page. `F1` inside it does nothing: this surface is the help, and opening it on top of itself is worse than the key doing nothing. A page is a plain-text file, `NAME` or `NAME.txt`, in a `kdos/doc` directory under `$XDG_DATA_HOME` (`~/.local/share/kdos/doc` by default) or under each directory of `$XDG_DATA_DIRS` (`/usr/local/share` and `/usr/share` when that is unset). The image ships ten in `/usr/share/kdos/doc`: `index` and one page each for `backup`, `bt`, `connect`, `devices`, `display`, `net`, `rec`, `res` and `settings`. With no argument the viewer opens on its built-in index, which lists every page it finds and then the link kinds below, each with a live example; an empty set of pages is a working state, and the index says so. Inside a page a link is written `[[kind:target]]`, and every kind resolves through a program that already answers that question at a prompt: | Link | Opens | |---|---| | `[[doc:NAME]]` | Another page | | `[[port:zlib]]` | A recipe's own metadata, from `kpkg meta` | | `[[reason:NAME]]` | An entry from the reasons store `kdos why` reads: `$KDOS_REASONS`, else `/usr/share/kdos/reasons` | | `[[man:kpkg.1]]` | The manual page, through `man` in a terminal | | `[[key:W-F1]]` | The keybinding card, `kdos-keys` | `Tab` and `Shift+Tab` move between links, `Enter` or a click follows one, `Backspace` goes back, and `/` searches the page, with `n` and `N` stepping through the matches. The one positional argument opens a page directly: `kdos-doc net`, `kdos-doc port:zlib`, `kdos-doc reason:NAME`. A link that resolves to nothing says so on the note row (`no such port: zlub`) and the page does not change; an argument that names nothing opens the index with the same note. A viewer that ignored a dead link would make a stale set of pages look the same as a working one. ## The small surfaces The remaining surfaces are small enough to describe in a table row each. They fall into four groups: the desk accessories you open for a moment, the system surfaces that show and change how the machine is set up, the background services that run with the session and raise a window only when something happens, and the small dialogs other programs open. ### The desk accessories | Name | Notes | |---|---| | `kdos-cal` | The calendar, on `Super+C`. Two arrows and a Today button beside the wheel. A day with an event carries a mark in the column the grid already leaves spare, and today's events are listed under the month. `khal` is asked when the popup opens and when the month changes, never from the draw path — a fork there would run once a frame and would put `$PATH`, which nothing fixes for a dump, inside the picture. The strip costs rows only when there is something to put in them | | `kdos-calc` | The calculator, on `Super+Ctrl+Q`. It does not do the arithmetic: `qalc` does, and the tree already carries `libqalculate`, which parses what a person actually typed — units, hexadecimal, `to`, and precedence that matches a pocket calculator, not a programming language. It runs `qalc` as a child instead of linking `libqalculate`, because that library is C++ and linking it would put libstdc++ on the panel package for one accessory. It evaluates once per pause, not once per keystroke, when the poll loop goes idle with the input changed, which is a debounce that costs no timer. `Enter` copies the answer, because the answer to "what is three inches in millimetres" is nearly always going somewhere else | | `kdos-chars` | The character map, on `Super+Ctrl+E`. The name index is built at build time by a program that links ICU; this binary does not and must not, because ICU is thirty megabytes of library and data and every surface this binary is would carry it. The index is `mmap`ped and searched in place, not read into memory, so every surface shares one page-cache copy of the megabyte of names instead of each summon dirtying a megabyte of its own. The blob is stored upper case, so a keystroke folds the query and not forty thousand names. `Enter` copies the character — not its name and not its number, which is what a person who wanted `U+2192` would have typed | | `kdos-note` | The scratch pad, on `Super+Ctrl+N`: one buffer per user at `~/.local/share/kdos/scratch.txt`, saved on close and every thirty seconds. It is not an editor and must not grow into one — `micro` is the editor, `Ctrl+O` opens this same file in it, and every feature past "type a line and find it later" already exists there and is better done there | | `kdos-contacts` | The address book, on `Super+Ctrl+B`: type a name, `Enter` copies the address or the number. The store is `khard` and this window holds none, because a second vCard parser would be a second answer to what a contact is. Two forks per query, never from the draw. Two things khard does cost a line each: `email --parsable` prints `searching for '' ...` as its first row unless told not to, results or not; and an empty book exits non-zero while printing nothing, so what is read is the output and the status is not consulted. An empty book names the program that fills it rather than saying only "0" | | `kdos-clip` | Clipboard history. One binary, one name, two roles: the daemon the compositor supervises owns the list, and `kdos-clip --pick` is the picker that draws it — the panel's clipboard widget and the status centre open it that way. The shipped `Super+Ctrl+V` binding in `rc.xml` runs bare `kdos-clip`, which starts a second daemon rather than the picker; that daemon unlinks and rebinds the history socket, so the chord shows nothing and takes the history over from the supervised copy. Bind the chord to `kdos-clip --pick` in `~/.config/kdos-comp/rc.xml` to have it open the picker. It speaks `wlr-data-control`, which is the only protocol that can carry a clipboard history — `wl_data_device` delivers a selection event solely to the client with keyboard focus, so a manager built on it records nothing | | `kdos-about` | What this machine is, on three pages: Left and Right, Tab or a click on the strip turn them. **Machine** is the KDOS wordmark beside the version, kernel, CPU, thread count, uptime to the second, the boot slot and the session, then a memory map under them: one bar across the card split into the three applications holding the most, the rest in use, the page cache and free memory, with a legend naming each and its size, and the line `no systemd · no xorg · musl · GNU · signed` under it. The uptime and the map are read again every second while the page is open. The applications are grouped as `kdos-res`'s Apps page groups them — a boxed process by its box, a host process by its own name — and measured as resident memory; resident memory counts a shared page once per process mapping it, so the named segments are clamped to what is in use and "other" is the remainder. The slot row is the running slot from the command line's `kdos_slot=` and the trial, if any, from `/boot/efi/EFI/kdos/bootstate`: `b on trial, 1 boot left` over `a known good`, `a known good, b standby`, or `one root` with no state file. **Build** is `BUILD_ID` and `KDOS_BUILD_DATE` from `/etc/os-release` (or `unstamped` on an image built without them), the compiler and build date out of `/proc/version`, the C library, the userland, the package count, and each trusted key in `/etc/kdos/keys` and `/etc/kdos/keys/packs` by the key id `kdos-pack` and `kpkg` print. **Credits** is every package in `/var/lib/kpkg/db` with its version, sorted, under "KDOS stands on N projects"; it rolls a row at a time and starts again from the top, until any key stops it and the arrow keys, Page Up/Down, Home, End and the wheel scroll it by hand. The facts type themselves in over 400 ms when the window opens, and the Konami code (Up Up Down Down Left Right Left Right b a) degausses the wordmark; with `motion = no` in `comp.conf`, and under `--dump`, the facts are whole, the wordmark is still and the credits do not roll. Only the wordmark is drawn — the block of `logo.txt` before its first blank line — because the mascot under it would make the card taller than an 80×24 screen. No grid size — a surface knows the cells it was given and not the ones the screen has, so a figure printed here would be this window's own size under a name every reader takes for the desktop's. Every fact is read rather than forked — `uname`, `/proc`, `/etc/os-release`, the boot state, the keyrings and the package database are files this process can open, and a screenfetch spawned to render them would draw a second program's colours and ANSI onto a surface that paints in slots, and would leave this surface with no offscreen dump. `$KDOS_ABOUT_ROOT` prefixes every one of those paths and becomes `libkproc`'s root, which is how its goldens draw `testing/fixtures/about` | | `kdos-teams` | The window list, on `Super+F2`, and what the panel's overflow cell opens. The cell opens the list rather than stepping the chip row: a row that shifted by one per click costs a click and a reflow per hidden window, and the list reaches any of them in one | ### The system surfaces | Name | Notes | |---|---| | `kdos-display` | Screens, on `Super+P` and the display key. The layout it applies is saved in `~/.config/kdos/displays.conf`; `kdos-display --apply` re-applies that file with no window, and `--list` prints the outputs and exits. It carries a button bar, because a pointer could otherwise select a screen and then not switch it off or apply anything. `m` and the Mode button open a dropdown of the modes the monitor published: a screen that cannot show the mode being tried is a black screen and a wait for the revert, so the list is read before it is chosen from, never stepped blindly through. It speaks `wlr-output-management`, which is how every wlroots compositor takes its screen configuration | | `kdos-time` | The zone, the clock, and whether the clock is right. The zone list is `zone1970.tab`, read — tzdata ships here and carries the canonical list. Setting it is a `kdos-powerd` verb, because `/etc/localtime` and the profile's `TZ` are root's and the person setting a zone is the one administering the machine, which is what `wheel` already means; a setuid helper for one write would be a worse answer to a question that daemon already answers. `chronyc tracking` is read and never driven: whether to step the clock, how far and how fast is left to chrony, and a "sync now" button would be `chronyc makestep`, which steps a clock chrony is already disciplining. After a change the surface calls `tzset()` on itself, or its own clock keeps drawing the zone `TZ` named at the first call | | `kdos-users` | The accounts, split by privilege, with the surface saying which side each row is on. Reading `/etc/passwd` and `/etc/group` is anybody's; creating an account, changing a password and editing group membership are root's, and this program does none of them. An `Add user` button that answered "permission denied" would read as a fault in the machine rather than as the boundary it is. `passwd`, `adduser`, `deluser` and `usermod` are on the image and are what a person changing accounts uses. The one thing it does change is the autologin, through `kdos-powerd`, because `/etc/kdos/login.conf` is a KDOS file and "is this person administering the machine" is the question that daemon already answers. Off is a commented key rather than an empty one, because `kdos-login` asks for a password when it finds no key and `autologin =` with nothing after it would name an account called `""`. The list is `kb_users()`, and the daemon validates against the same call | | `kdos-update` | What is behind, what is vulnerable and which slot is live; `--security` opens on the vulnerability page. It computes nothing: `kdos update check --json`, `kdos cve --json` and `kdos-bootctl status` already answer these three, and the version comparison in particular is the packaging system's and is subtle. It applies nothing either: `kdos update apply` compiles packages, can take hours, and on an A/B machine writes the other slot, so a button behind a one-line status would be a progress bar over an unattended build with no way to see what it was doing. The surface says what to type and shows which slot it will land in. The footnote under the list names the tree's commit, or the last problem `kdos update` recorded (no network, a fetch or a build that failed) in the warning colour; with no ports tree the page says so in place of a list, which would otherwise read as everything current. The security table's age is on the screen beside the count, because a table three months stale reporting nothing to fix is worse than no answer. The JSON is read by a bounded key scan rather than a parser: both producers are in this tree and their shape is fixed | | `kdos-firewall` | Which of this machine's services answer the network. It carries no table of ports — `kdos-powerd` owns the names, because a client that could name a port could open any port. It edits `/etc/nftables.d/50-kdos-services.nft` and only that; the daemon rewrites that file whole, so anything hand-written belongs in another file beside it, said on the surface as well as in the file. It is not a firewall editor: the shipped policy is a workstation's, and the only question here is which of a short list may be reached from outside. The default is drawn on the screen under the list, because every row is an exception to it and a list of exceptions with the rule missing reads as the whole policy. `open` is drawn in the warning slot rather than the accent — a port answering the network is the state worth noticing. The `ssh` row is also the SSH server's switch: opening it starts `sshd` and enables it for later boots, and closing it stops and disables it | | `kdos-backup` | What is in the restic repository, one key to add to it, and the way back out of it. `--once` backs up and exits with no window, which is what a scheduled job runs. `Enter` (or a press on the selected row) opens a snapshot in the restore view: its folders one level at a time, `Enter` into a folder and `Backspace` back up, with each entry's size and modified time from `restic ls --json`. `t` restores the selected file or folder into `~/Restored//`, with its full path recreated beneath that, through `restic restore --target … --include `; the path is escaped because restic reads `--include` as a glob. A restore never writes over the original: what comes back sits beside what is there now. restic's last line is the status while it runs, and closing the window does not stop it. `F1` opens its page | | `kdos-burn` | A folder or an image onto a CD, DVD or Blu-ray, over `xorriso`. The drives are `/sys/block/sr*` with the vendor and model the drive reports; the source is the first argument, a typed path (`s`) or a file dropped on the window. A folder is burnt as one new ISO 9660 session named after the folder (`xorriso -outdev -blank as_needed -volid -map / -commit -compare_r /`), and the same run reads every file back and compares it. xorriso reports a difference as the line `Differences detected.` and still exits 0, so that line is what the window reads as a failed comparison. An image is written as it is (`xorriso -as cdrecord -v dev= blank=as_needed -eject padsize=300k `) and ejected, because the kernel does not see a disc libburn wrote until the tray has been opened; `v` then reads the reloaded disc and compares the image's length byte for byte (`kdos-burn --compare IMAGE DEVICE`, this program). `blank=as_needed` blanks a rewritable disc that holds data, so Burn asks for a second `Enter` naming the drive. No daemon stands in front of the drive: it belongs to `cdrom`, and the desktop user is in `cdrom`. A burn outlives its window | | `kdos-verify` | Files against a checksum list or a par2 set. The checker is chosen by the list's name, in any case: `.par2` is `par2 verify`; a name ending `.b3` or containing `b3sum` or `blake3` is `b3sum -c`; one containing `sha512` is `sha512sum -c`; anything else is `sha256sum -c`. It runs in the list's folder, since every such list names its files relative to itself. Each verdict line becomes a row (`OK`, or `FAILED` and the checker's reason; for par2, `found`, `damaged` or `missing`), and the status line counts them. After a par2 verify that says a repair is possible, `p` runs `par2 repair`, which rewrites the damaged files and keeps each old one as `.1`. The list is the first argument, a typed path (`l`) or a dropped file. `b3sum` is not a port on this image, so a BLAKE3 list answers that `b3sum` is not on this machine | ### Background services | Name | Notes | |---|---| | `kdos-mediad` | A stick goes in and the desktop says so. The daemon notices and the session speaks: `kdos-mountd` is root, starts before anybody logs in and has no session bus to raise a toast on, so it says only that something changed and this decides what that means. Subscribed, never polled — one connection that stays open and carries nothing until something happens. The index is re-read before it is used, twice: a row number is only true of the list it came with, so the toast is built from a fresh list and the button, clicked minutes later, finds its device by kernel name in a list read at the click | ### The small dialogs | Name | Notes | |---|---| | `kdos-openwith` | Choose a handler, and optionally always use it. The chosen entry is launched through `sh_launch()` with the file as its document — the difference between a chooser and a launcher is which entry is picked and nothing else, so `%f` lands where the entry put it and a quoted argument survives. The *Other command…* row is a `verbatim` launch: the typed words become an argument vector, so a `;` in the box is an argument rather than a second program, and the path follows as a trailing argument. A plain command cannot be made the default, because `mimeapps.list` records an entry and inventing one would leave a file in the user's applications directory nobody asked for | | `kdos-run` | The run box, on `Alt+F2`. A click places the caret, and the button bar is on the surface because *In Terminal* is a modifier and a modifier nothing draws is a feature nobody finds. It is a `verbatim` `sh_launch()`: quoting is read, so `mpv "my film.mkv"` is two arguments, and a `%` reaches the program as typed. *In Terminal* wraps it in this desktop's emulator; without it the program is spawned as it was typed | | `kdos-prompt` | Yes or no, answering by exit status, which is what the compositor reads. `--input` is a second shape: one row with a text box, the typed line on standard output, 0 for an answer and 254 for Escape or an empty box; `Enter` answers. A mode rather than a third button, because the yes/no shape's status is `kdos-comp`'s contract and must not gain a second meaning. Both shapes are the same hand-written loop: the text box is the toolkit's field, handed every key the dialog does not answer itself | | `kdos-slit` | The dockapp column, off by default: a slit nobody configured is a column of marks. `slit = yes` in `comp.conf` starts it, and it reads `~/.config/kdos/slit.conf` | | `kdos-ascii` | A picture, as characters. A filter with no display and no keyboard | ## Command lines Every surface's options. Each row is the usage line the surface prints; where its parser accepts an option the usage line leaves out, or rejects one it lists, the row says so. `--font NAME` sets the cell font, `--no-icons` turns pictures off, `--at X Y` and `--at-bottom X Y` anchor a popup at a pixel position (the panel passes these), and `--fixture DIR` reads a recorded machine instead of this one for tests. Any unknown argument prints the usage line and exits 2, except in `kdos-prompt`, which exits 254 as a cancel. | Command | Usage | |---|---| | `kdos-shell` | `[--top\|--bottom] [--cells N] [--autohide] [--no-icons] [--output NAME] [--font NAME] [--clock FMT] [--margin PX] [--opacity PCT]`; `--dump [--dump-width N]`. It also accepts `--version`, which the usage line leaves out | | `kdos-start` | `[--at-bottom X Y] [--font NAME] [--no-icons] [--route NAME] [--dump-view VIEW] [--dump]`. It also accepts `--dump-cells`, which the usage line leaves out | | `kdos-palette`, `kdos-launcher` | `[--apps] [--route NAME] [--font NAME] [--dump] [--dump-query TEXT]` | | `kdos-menu` | `[applications\|places\|system] [--windows APP_ID] [--winmenu APP_ID] [--box NAME] [--at X Y] [--at-bottom X Y] [--dump] [--font NAME]`. It also accepts `--dump-cells`, which the usage line leaves out | | `kdos-desk` | `[--output NAME] [--font NAME] [--no-icons]`; `--dump [--dump-size W H]` | | `kdos-pick` | `[--save] [--directory] [--multiple] [--browse [DIR]] [--title T] [--name N] [--dir D] [--filter 'Label:*.png *.jpg'] [--dump\|--dump-cells] [--font F] [--parent H]`. It also accepts `--places`, which with `--dump` draws the Places list over the file list and which the usage line leaves out | | `kdos-settings` | `[--dump\|--dump-cells] [--dump-size WxH] [--page NAME] [--no-icons] [--font NAME]` | | `kdos-net`, `kdos-bt` | `[--font NAME] [--no-icons] [--dump]`. Both also accept `--at-bottom X Y`, which the usage line leaves out | | `kdos-audio` | `[--font NAME] [--no-icons] [--dump]`. It accepts `--at-bottom X Y`, which the usage line leaves out, and rejects `--no-icons`, which it lists: `kdos-audio --no-icons` prints the usage line and exits 2 | | `kdos-disks`, `kdos-firewall`, `kdos-users` | `[--font NAME] [--no-icons] [--dump]` | | `kdos-netagent`, `kdos-ime` | `[--font NAME]` | | `kdos-devices` | `[--font NAME] [--fixture DIR] [--dump]`. It also accepts `--at-bottom X Y`, which the usage line leaves out | | `kdos-connect` | `[--font NAME] [--dump] [--browse]` | | `kdos-print` | `[--font NAME] [--no-icons] [--found] [--fixture DIR] [--dump]` — `--found` opens on the discovered printers | | `kdos-notifyd` | `[--font NAME] [--dump]` | | `kdos-notify` | `[--at X Y] [--at-bottom X Y] [--dnd] [--font NAME] [--no-icons] [--dump]` | | `kdos-osd` | `volume [+N\|-N\|mute\|toggle]`; `mic [toggle\|up\|down\|+N\|-N]`; `brightness [+N\|-N]`; `slider [--at-bottom X Y] [--font NAME]`; `--dump [volume\|mic\|brightness]` | | `kdos-store` | `[--font NAME] [--no-icons] [--fixture DIR] [--page NAME] [--dump]` | | `kdos-trash`, `kdos-note`, `kdos-run` | `[--font NAME] [--dump]` | | `kdos-about` | `[--font NAME] [--page machine\|build\|credits] [--dump]` | | `kdos-peek`, `kdos-pix` | `[--font NAME] [--dump] FILE` | | `kdos-find` | `[--font NAME] [--query TEXT] [--dump\|--dump-cells] [DIR]` | | `kdos-rec` | `[--font NAME] [--input hw:C,D] [--dump] [--fixture DIR] [--meter FILE] [--write OUT]` | | `kdos-keys` | `[--first-run] [--print] [--program NAME] [--dump\|--dump-cells] [--dump-size WxH] [--dump-query TEXT] [--font NAME]` | | `kdos-style` | `[--page accent\|font] [--font NAME] [--dump]` | | `kdos-saver` | `[--mode NAME] [--fps N] [--output NAME] [--font NAME] [--dump]` | | `kdos-status` | `[--at X Y] [--at-bottom X Y] [--open KEY] [--from FILE] [--font NAME] [--no-icons] [--dump]` | | `kdos-traymenu` | `SERVICE PATH [--name NAME] [--at X Y] [--at-bottom X Y] [--open ID] [--pick ID] [--dump] [--no-icons] [--font NAME]` | | `kdos-tip` | `[--at X Y] [--at-bottom X Y] [--ms N] [--font NAME] [--preview APP_ID] TEXT [DETAIL]`. `--ms` is how long the tip stays up, 6000 by default. It also accepts `--dump`, which the usage line leaves out; an unknown word that does not begin with `--` is taken as the text rather than refused | | `kdos-clip` | no argument: the daemon; `--pick [--at-bottom X Y]`: the picker; `--dump`. It also accepts `--font NAME`, which the usage line leaves out | | `kdos-cal` | `[--at X Y] [--at-bottom X Y] [--dump] [--font NAME]` | | `kdos-calc` | `[--font NAME] [--dump] [EXPRESSION]` | | `kdos-chars`, `kdos-contacts` | `[--font NAME] [--dump] [QUERY]` | | `kdos-teams` | `[--at X Y] [--at-bottom X Y] [--dump\|--dump-cells] [--dump-size WxH] [--font NAME]` | | `kdos-display` | `[--list] [--apply] [--font NAME] [--dump]` | | `kdos-doc` | `[\|port:\|reason:] [--dump\|--dump-cells] [--dump-size WxH] [--font NAME]` | | `kdos-time` | `[--font NAME] [--no-icons] [--dump] [QUERY]` | | `kdos-update` | `[--font NAME] [--no-icons] [--security] [--dump]` | | `kdos-backup` | `[--font NAME] [--fixture DIR] [--open SNAPSHOT [--dir PATH]] [--once] [--dump]` — `--open` starts in the restore view on that snapshot, and `--dir` in one folder of it | | `kdos-burn` | `[--font NAME] [--no-icons] [--fixture SYS] [--dump] [FOLDER\|IMAGE]`; `--compare IMAGE DEVICE` compares and exits, 0 on a match | | `kdos-verify` | `[--font NAME] [--no-icons] [--dump] [LIST]`. With `--dump` the check runs to the end before the frame is drawn | | `kdos-openwith` | ` \| --mime `, `[--print] [--dump\|--dump-cells] [--dump-size WxH] [--font NAME]` — `--print` lists the MIME type, the default and every candidate entry, tab separated, and launches nothing | | `kdos-prompt` | `--message TEXT [--yes LABEL] [--no LABEL] [--font NAME] [--dump]`; `--input --message TEXT [--placeholder TEXT]` | | `kdos-slit` | `[--output NAME] [--font NAME] [--config PATH] [--dump]` | | `kdos-ascii` | `[FILE.ppm] [--width N] [--plain] [--mono] [--font NAME]`; reads a PPM on standard input, as in `grim -t ppm - \| kdos-ascii` | `kdos-mediad` reads no arguments at all and ignores any it is given. `kdos-prompt` exits 0 for yes, 1 for no and 254 when cancelled; with `--input` it prints the typed line on standard output and exits 0, or exits 254 with nothing printed. An unknown argument also prints the usage line and exits 254, so a malformed call reads as a cancel and never as a yes. ## See also - [The desktop](/docs/kdos/02-user-guide/desktop.html) — using all of this - [The design language](/docs/kdos/03-architecture/design-language.html) — the rules every surface follows - [Writing desktop software](/docs/kdos/05-developer/writing-desktop-software.html) — adding a surface - [kdos-comp](/docs/kdos/04-programs/kdos-comp.html) — what supervises it, and the sockets it reads - [The C libraries](/docs/kdos/05-developer/c-libraries.html) — the toolkit these surfaces are drawn with - [Configuration](/docs/kdos/06-reference/configuration.html) — `panel.conf` and every other key file - [kdos-appbox](/docs/kdos/04-programs/kdos-appbox.html) — `kdos-appbox open`, the resolution every "open this file" row here ends in, and the boxes the `[box]` mark refers to - [The daemons](/docs/kdos/04-programs/daemons.html) — `kdos-mountd` and `kdos-powerd`, which do the privileged half of `kdos-disks`, `kdos-connect`, `kdos-time`, `kdos-users` and `kdos-firewall` - [Testing](/docs/kdos/05-developer/testing.html) — the dump harness and the goldens the surfaces are checked against - [How KDOS differs](/docs/kdos/01-philosophy/how-kdos-differs.html#toolkits) — why the desktop is one cell-grid toolkit of its own rather than GTK or Qt under its surfaces - [How KDOS is built](/docs/kdos/05-developer/how-kdos-is-built.html#the-desktop-50_desktop) — where the `kdos-shell` port is built in the desktop phase, and how its links reach the image --- *Part IV — Programs, chapter 22.* Previous: [21. kdos-comp](/docs/kdos/04-programs/kdos-comp.html) · [Contents](/docs/kdos/) · Next: [23. kdos-res](/docs/kdos/04-programs/kdos-res.html)