home-service-dwl-guile icon indicating copy to clipboard operation
home-service-dwl-guile copied to clipboard

GNU Guix home service for the dwl window manager with dynamic configuration in Guile Scheme

  • A =dwl-guile= home service for GNU Guix This repository contains a [[https://guix.gnu.org/manual/devel/en/html_node/Home-Configuration.html][Guix Home]] service which installs and configures [[https://github.com/engstrand-config/dwl-guile][ =dwl-guile= ]], a patched version of [[https://github.com/djpohly/dwl][ =dwl= ]] that is configured in [[https://www.gnu.org/software/guile/][GNU Guile]]. You can install our home service with the help of the Guix channel below.

The main goal of =dwl-guile= is to serve as a minimal =dwm=-like Wayland compositor for those who use [[https://guix.gnu.org/][GNU Guix System]] --- a GNU/Linux distribution in which the user can customize and configure their entire system in GNU Guile.

With =dwl-guile=, we can integrate the window manager customization with that of the rest of the system, which allows for a dynamic, programmable and reproducible configuration for our entire computing environment --- all in Guile.

#+begin_quote This is a work in progress --- please report bugs to us and (if applicable) to upstream =dwl=! #+end_quote

** Features This Guix Home service can:

  • install =dwl-guile=
  • automatically start =dwl-guile= on the first TTY you log in to
  • set necessary environment variables for Wayland support in GTK, Java, etc
  • apply (some) other common =dwl= patches dynamically
  • configure all options in dwl (in an Emacs-esque way) with Guile

** Guix channel We provide =home-service-dwl-guile= in a Guix channel.

Add the channel to your =~/.config/guix/channels.scm=: #+begin_src scheme (channel (name 'home-service-dwl-guile) (url "https://github.com/engstrand-config/home-service-dwl-guile") (branch "main") (introduction (make-channel-introduction "314453a87634d67e914cfdf51d357638902dd9fe" (openpgp-fingerprint "C9BE B8A0 4458 FDDF 1268 1B39 029D 8EB7 7E18 D68C")))) #+end_src Afterwards, run =guix pull=.

** Usage *** Home service configuration =home-service-dwl-guile= is enabled by adding it to your list of home services. #+begin_src scheme ;; Import the service (use-modules (dwl-guile home-service) (dwl-guile patches)) ; import if you want to apply patches dynamically

;; Create and add the dwl-guile home service to your home configuration. (service home-dwl-guile-service-type ;; If you wish to configure the home service further, you can pass in ;; a configuration to the service. All options listed below are optional. (home-dwl-guile-configuration ;; Use a custom dwl-guile package. (package my-custom-dwl)

      ;; If you want to dynamically apply patches, you can create a new
      ;; modified package definition (multiple patches can be applied).
      ;; Note that some patches might have conflicts.
      ;;
      ;; (package
      ;;  (patch-dwl-guile-package dwl-guile
      ;;                           #:patches (list %patch-xwayland)))

      ;; Environment variables to set for Wayland compatibility with applications.
      ;; By default, native Wayland rendering will be enabled for most applications.
      ;; Native rendering of QT-applications is enabled using the @code{native-qt?}
      ;; option. This is because it requires the qtwayland package.
      ;;
      ;; Set it to an empty list to skip setting environment variables:
      ;; (environment-variables '())
      ;;
      ;; Or extend the default environment variables:
      ;; (environment-variables
      ;;  (append `(("var" . "value")) %dwl-guile-base-env-variables))

      ;; A string containing a command to execute after starting dwl-guile.
      ;; This is the equivalent of specifying a script to the '-s' flag of dwl.
      ;; The gexp's will be executed in the same order as in the list.
      ;;
      ;; The preferred way of running commands/applications (that does not need
      ;; to access the stdout of dwl) on startup is by using the
      ;; dwl:startup-hook in your Guile config.
      ;;
      ;; By default, this option is not used.
      (startup-command "foot --server <&-")

      ;; If QT-applications should be rendered natively. Enabled by default.
      ;; This will set QT_QPA_PLATFORM="wayland-egl" and install
      ;; the "qtwayland" package to enable support for Wayland.
      (native-qt? #t)

      ;; If dwl-guile should auto-start on first login. Enabled by default.
      (auto-start? #t)

      ;; If the dwl-guile config should be automatically reloaded on change.
      ;; This will allow you to see (most of) the effects of your config changes
      ;; dynamically, without restarting dwl-guile.
      (reload-config-on-change? #t)

      ;; Create a custom configuration for dwl.
      (config '())

#+end_src

*** Shepherd service After enabling the dwl-guile home service and reconfiguring, a new Shepherd service will be added. This allows you to control the dwl-guile executable using =herd=.

For example: #+BEGIN_SRC herd start dwl-guile herd stop dwl-guile herd restart dwl-guile #+END_SRC

Using these commands, dwl-guile will start with the correct options. Logs are available at =$XDG_LOG_HOME/dwl-guile.log=. If the XDG environment variable is not set, the log will be saved to your =HOME= directory.

*** Runtime evaluation of Guile expressions As of v2.0.0, you can execute arbitrary GNU Guile expressions in the context of dwl-guile during runtime. This allows for some scripting capabilities, as well as dynamic changes of the config.

Executing an expression is done using the =dwl-guile= executable, like so: #+BEGIN_SRC dwl-guile -e "(dwl:reload-config)" #+END_SRC

The result of the evaluation will be shown in stdout, or in stderr if an error occured. These types of evaluations will be executed in their thread, which means that it will not block dwl-guile. In other words, you can safely run commands that run for a longer time. Note that the expression is not evaluated in a shell context, which means that procedures such as =system*= will not work, but you can always use =dwl:shcmd= or =dwl:spawn= instead.

**** Using the Guile REPL for interacting with dwl-guile During runtime, it is possible to use the Guile REPL to interact with dwl-guile. In order to do this, you need to explicitly start the REPL server in your config by calling =(dwl:start-repl-server)=. You can then connect to the server in e.g. Emacs using Geiser.

For more information, see the man pages (=man dwl-guile=).

*** Configuring dwl-guile Using dwl-guile, all configuration is done in Guile by providing an alist of (Emacs-like) commands to the =config= field of the home service configuration.

A minimal set of keybindings will automatically be loaded, unless inhibitied using =(setq inhibit-defaults? #t)=. You can see the defaults in =/share/defaults.scm= of this repo. There are also some utilities that can be used in your config defined in =/share/init.scm=.

For more information, see the man pages (=man dwl-guile=).

*** C-bindings for dwl All functions that allow you to interact with dwl are exposed using the =libguile= API in dwl-guile. Each binding is prefixed with =dwl:= and uses kebab-case as naming scheme, e.g. =dwl:toggle-fullscreen=. There are currently no documentation for these bindings, other than the definitions and implementations [[https://github.com/engstrand-config/dwl-guile/blob/b780d0cded7a1040064d9066f5f41e274e3ffc64/dscm-bindings.h#L305-L373][here]].

*** Patches **** =%patch-attachabove= Puts newly spawned clients above the currently selected client. This is useful when you want to be able to spawn new clients without changing the master client.

**** =%patch-focusmonpointer= Move cursor with monitor focus. This will teleport your mouse to the center of focused monitor.

**** =%patch-monitor-config= Allows configuration of monitor resolution, refresh rate and adaptive sync, directly in your dwl config.

**** =%patch-movestack= Move clients up and down the stack. Exposes the =dwl:move-stack= binding that can be used to move clients up or down.

**** =%patch-swallow= Allows applications such as terminals to render launched applications in the same window. For example, opening a PDF using zathura will (if enabled) render zathura on top of the terminal, in the same client. Adds additional options to the =dwl-rule= record.

Note that swallowing does not work for XWayland clients.

**** =%patch-xwayland= Enable xwayland support.

*** Extending the home service You can extend the home service in order to extend the configuration. This is especially useful if you use something like [[https://github.com/abcdw/rde][ =rde= ]].

Consider the following example that adds two new keybindings that dismiss notifications from [[https://github.com/emersion/mako)][ =mako= ]]:

#+begin_src scheme (simple-service 'add-mako-dwl-keybindings home-dwl-guile-service-type `((set-keys ,dismiss-key (lambda () (dwl:shcmd ,(file-append mako "/bin/makoctl") "dismiss")) ,dismiss-all-key (lambda () (dwl:shcmd ,(file-append mako "/bin/makoctl") "dismiss" "--all"))))) #+end_src

You can find more examples of this in [[https://github.com/engstrand-config/guix-dotfiles][our GNU Guix configuration]], mainly in the =engstrand/features/wayland.scm= file.