DARKMAN(1)

πŸ“¦
version 2.3.1 Β· 4 versions
, in alpine 3.24, arch rolling, nixpkgs unstable

πŸ’… darkman(1) β€” the group chat edition πŸŽ€

okay bestie, so this is from the General Commands Manual and it's called darkman β€” yes THE darkman. buckle up, she's iconic. πŸ’œ


✨ NAME ✨

darkman β€” controls the whole dark-mode / light-mode transition situation for you. she's the one flipping the lights. πŸ”₯


🌈 SYNOPSIS 🌈

darkman [options] command

πŸ¦‹ DESCRIPTION πŸ¦‹

okay so picture this: darkman just vibes in the background, and at sundown she flips dark mode ON, and then at sunrise she flips it off again. literally that simple. πŸŒ™β˜€οΈ

and ngl, she is not designed to be used interactively β€” like, she's not a chatty app you poke at. the whole point is you set her up once, and then she runs in the background forever. main character energy, honestly. πŸ‘‘

but wait, there's more: you can also trigger manual transitions if you want, and you can also straight-up disable automatic transitions entirely. your life, your rules. periodt. πŸ’…


πŸŽ€ COMMANDS πŸŽ€

  • run [--ready-fd fd] β€” runs the darkman service. this one is meant to be executed by a service manager, init script, or something like that. like, it's for the machinery, not for you personally.

the optional fd is a file descriptor where darkman will notify when she's ready to receive connections from clients. the protocol here is compatible with readiness notifications for s6, dinit and systemd. so like, it's a whole ecosystem-compatible queen. ✨

  • set ⟨light | dark⟩ β€” sets the current mode. that's it, that's the tweet.
  • get β€” prints the current mode. 🌸
  • toggle β€” toggles the current mode. flip it and reverse it. πŸ¦‹

πŸ’– OPTIONS πŸ’–

  • --verbose β€” print detailed logs. defaults to true for the run command and to false for all other commands. so yeah, she's loud by default only when she's out there running. 😭

🧚 CONFIGURATION 🧚

see darkman.conf(5) for the configuration file format, and also check the β€œFILES” section (down below πŸ‘‡) for where those config files actually live. iykyk.


⭐ INTEGRATIONS ⭐

okay so real talk: the open source desktop ecosystem is quite heterogeneous 😭 β€” like, making different applications switch between dark/light requires a whole bunch of different mechanisms. it's giving chaos.

so darkman seeks to implement the more widely adopted standards, while leaving room for users to hook in custom scripts for other applications. balance. 🫢

🌸 Custom executables 🌸

darkman can run custom executables β€” and yes, they can absolutely be simple shell scripts. we love a low-effort king.

scripts are searched in a directory named darkman inside paths defined in the XDG_DATA_DIRS as well as XDG_DATA_HOME. each script receives the current mode (β€œdark” or β€œlight”) as its first argument ($1). this allows a single script to handle both modes:

#!/bin/sh
case "$1" in
dark) THEME=dark-theme ;;
light) THEME=light-theme ;;
esac
gsettings set org.example.app theme "$THEME"

for backwards compatibility, darkman also searches dark-mode.d and light-mode.d directories (the legacy format, bless). scripts in these directories run without arguments. and β€” important β€” scripts in the darkman directory with the same name will override legacy scripts. newer script ate and left no crumbs. πŸ’€

both of these variables and their fallbacks are defined in the XDG Base Directory Specification: https://specifications.freedesktop.org/basedir-spec/latest/

these scripts or executables can perform any actions required β€” like re-write configuration files for a PDF reader, control a notification daemon to switch to another theme, or toggle a DE-specific setting. truly limitless. ✨

files must have the executable bit set in order to be considered an executable. and scripts must have the proper shebang for the OS to properly execute them. no shebang, no slay. πŸ™ˆ

typically, the XDG_DATA_* variables mentioned above will resolve to the following set of directories:

  • ~/.local/share/
  • /usr/local/share/
  • /usr/share/

example scripts (plus discussion on how to integrate different applications) are available in the project repository: https://gitlab.com/WhyNotHugo/darkman

packages may also drop-in their own scripts into any of these locations β€” although application developers are encouraged to use the socket API to determine the current mode and listen for changes (see below for details πŸ‘€).

🌈 XDG Settings portal 🌈

darkman implements the XDG desktop portal's dark mode interface. applications using this API should switch to dark/light mode based on darkman's current preference. this interface was originally pushed by the GNOME and Elementary teams, and is currently supported by KDE, Firefox and many other projects. so you should expect applications from those environments to support it, amongst others. 🌍

for more details on this protocol, see: https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html

as of xdg-desktop-portal(1) version 1.17.0, portals MUST be configured with per-user configuration portals.conf(5). to force the usage of darkman for dark/light mode setting, use something like the following:

[preferred]
org.freedesktop.impl.portal.Settings=darkman

when using a desktop-specific configuration (e.g.: swaywm-portals.conf), please keep in mind that the environment variable XDG_CURRENT_DESKTOP must be set for the xdg-desktop-portal(1). don't skip this step bestie, it will not work. 😭

the xdg-desktop-portal(1) should start after darkman has started and is ready. use --ready-fd for readiness notification. this is likely not relevant on systemd-based setups, where the service manager intermediates in taking the named bus. 🚌

for a more in-depth explanation, see this article: https://whynothugo.nl/journal/2024/04/09/darkman-portal-configuration/

πŸ’œ Socket API πŸ’œ

darkman listens on a Unix socket for query and control commands. the socket exposes a simple text-based API where commands are followed by a newline, and darkman writes a response also followed by a newline. clean, simple, iconic. πŸŽ€

the get, set and toggle commands use this API. it's also the recommended approach when writing custom tools (e.g.: switching the current mode based on the input from a light sensor). very DIY of you, love that. 🫢

the following commands are available:

  • get β€” print the current mode.
  • set mode β€” set the current mode. mode must be one of light or dark.
  • toggle β€” switch to the opposite mode. the new mode is printed as a response. πŸ”
  • watch β€” print mode changes as they occur. the current mode is printed immediately, followed by each subsequent change. she's keeping you posted. πŸ‘€

to interact with the socket manually:

socat UNIX-CONNECT:${XDG_RUNTIME_DIR}/darkman/control.sock STDIO

a libdarkman Go package is available to query this interface from other client applications. see its documentation for details: https://godocs.io/gitlab.com/WhyNotHugo/darkman/libdarkman

πŸ¦‹ Third party integrations πŸ¦‹

for Emacs users, a third party package exists to integrate darkman with Emacs: https://github.com/grtcdr/darkman.el

there also exists a plugin for neovim users: https://github.com/4e554c4c/darkman.nvim

(no cap, there's something for everyone. 🌈)


πŸ‘‘ LOCATION πŸ‘‘

the current location may be specified in the configuration file. the location is used to calculate what time sundown and sunrise happen. πŸŒ…

it's also possible for darkman to automatically determine the system's location using geoclue(1). heads up though: Geoclue's reliability varies depending on distribution and desktop environment, as an agent often needs to be configured for it to work properly. so like, it's giving "sometimes works, sometimes doesn't." 😭

and if no location is known, automatic transitions are disabled. no location, no sundown, no vibes. πŸ’€


🌸 ENVIRONMENT 🌸

the following environment variables are also read and will override the configuration file:

  • DARKMAN_LAT β€” overrides the latitude for the current location. 🌍
  • DARKMAN_LNG β€” overrides the longitude for the current location. 🌍
  • XDG_CURRENT_DESKTOP β€” darkman does not use this variable; it should be defined for the xdg-desktop-portal(1) instead. just so we're clear. πŸ’…

πŸŽ€ FILES πŸŽ€

darkman searches for a configuration file in the following locations, using the first one found:

  1. $XDG_CONFIG_HOME/darkman/config.yaml
  2. Each directory in XDG_CONFIG_DIRS, with darkman/config.yaml appended.

if XDG_CONFIG_HOME is not set, it defaults to ~/.config. if XDG_CONFIG_DIRS is not set, it defaults to /etc/xdg. πŸ—‚οΈ

see darkman.conf(5) for configuration file format.


πŸ™ˆ PRIVACY πŸ™ˆ

okay so this part is real, bestie β€” read it. πŸ‘€

darkman will trigger a darkmode/lightmode transition at sundown in the current location. any application that is running locally can record or transmit the time of these transitions and attempt to extrapolate information related to the current location. 😬

when a web browser applies this transition at the same time, open websites can record this information too. yeah. the websites. πŸ’€

a potential stalker or tracker can use the above information to infer that you are likely in a region of the world where sunset happened at a specific time. this region is usually a wide area spanning tens of thousands of kilometers, but can be smaller for certain geographical locations.

the author of this tool uses a manually configured location with an integer latitude and longitude to achieve a sensible balance between privacy and convenience. smart queen behavior. πŸ‘‘


⭐ DEVELOPMENT ⭐

for issues and general development inquiries, see the project home currently hosted at GitLab: https://gitlab.com/WhyNotHugo/darkman


πŸ”₯ DEBUGGING πŸ”₯

to confirm which value is relayed via the xdg-desktop-portal(1), use:

gdbus call --session \
    --dest org.freedesktop.portal.Desktop \
    --object-path /org/freedesktop/portal/desktop \
    --method org.freedesktop.portal.Settings.ReadOne \
    org.freedesktop.appearance color-scheme

🌈 SEE ALSO 🌈

portals.conf(5), gammastep(1), sd-ready-fd(1)

go read them, they're all friends of the show. 🫢


πŸ’– AUTHORS πŸ’–

developed by Hugo O. Barrera <hugo@whynothugo.nl>, with invaluable contributions from the community. 🫢

darkman is an open source project licensed under the ISC licence and developed for anyone to use freely. if you would like to sponsor this project, see: https://whynothugo.nl/sponsor


πŸ’Œ and that's a wrap bestie!! this lil page is the Debian build, dated 2025-12-25 β€” yep, a christmas day release, festive icon behavior πŸŽ„βœ¨. now go set up your transitions and live your dark-mode truth. mwah. πŸ’…πŸ’œ
πŸ“¦ where this page comes from