Hi Abbé,

shepherd doesn't really offer this functionality built-in.
Moreover, even if you do setenv in shepherd process, it won't
automatically be propagated to the started services,
you need to modify their environment parameter.

There are a couple of ways to achieve what you want, though.
One is to make a service that will set this variable based on
the file appearing at XDG_RUNTIME_DIR, ie. /run/user/1000/wayland-X,
this is how the home x display service works, so you can check
its source in guix channel. Then you can call setenv
and set the service's run value to the display name.
Then other services depend on this one and are started only
after this one starts. There is sort of a timeout of 10 seconds
to wait for a display.
But I personally don't like this approach of 'guessing' the wayland
display and relying on it starting in time.

The approach I take is that I make a service called wayland-display,
and I start this service with a run value I want, as an argument.
In turn it is `herd start wayland-display $WAYLAND_DISPLAY`. This
service will keep $WAYLAND_DISPLAY as an argument and I can then use
it in other services.

Here is the service, it is very simple.
```
(define (wayland-display-shepherd-service config)
  (list
   (shepherd-service
    (documentation "Sets WAYLAND_DISPLAY to argument passed in.
This should be called from a wayland compositor like this: `herd start 
wayland-display $WAYLAND_DISPLAY`")
    (provision '(wayland-display))
    (auto-start? #f)
    (start #~(lambda (wayland-display) wayland-display))
    (stop #~(lambda _ #f)))))

```

Then to use it in another services, here is an example with swayidle:
```
(define (home-swayidle-shepherd-services config)
  (list
   (shepherd-service
    (documentation "Run the swayidle daemon for managing commands executed when 
idle.")
    (provision '(swayidle))
    (requirement '(wayland-display))
    (modules '((srfi srfi-1)
               (srfi srfi-26)
               (shepherd service)
               (shepherd support)))
    (start #~(lambda _
               (fork+exec-command
                (list #$(file-append
                         (home-swayidle-configuration-swayidle config)
                         "/bin/swayidle")
                      "-wC"
                      #$(serialize-swayidle-configuration 
(home-swayidle-configuration-config config)))
                #:environment-variables #$wayland-display-environ)))
    (stop #~(make-kill-destructor)))))
```
BEWARE of make-fork-exec-command! Then environment will be picked up at
wrong time. Also the modules are important, since they are used in the
`wayland-display-environ`

And here is the symbol to obtain the run value, used in
#:environment-variables argument:
```
(define wayland-display-environ
  #~(begin
      (use-modules (shepherd service)
                   (shepherd support))
      (let* ((display-service (lookup-service 'wayland-display))
           (display (or (and display-service
                             (service-running-value display-service))
                        "FAILED_TO_OBTAIN")))
      (cons (string-append "WAYLAND_DISPLAY=" display)
            (remove (cut string-prefix? "WAYLAND_DISPLAY=" <>)
                    (default-environment-variables))))))
```

Then the idea is to start a batch of services that depend
on this.
One problem with this is that shepherd doesn't really
have targets, ie. ways to start a group of services, so I made
myself a service that will achieve that, it just calls all
services that are passed in its configuration
```
(define (wlr-services-shepherd-service services)
  (list
   (shepherd-service
    (documentation "Service for starting WLR services.")
    (provision '(wlr-services))
    (auto-start? #f)
    (start
     #~(lambda* (#:optional (wayland-display #f))
         (use-modules (shepherd service)
                      (shepherd support))

         (let ((display-service (lookup-service 'wayland-display)))
           (when (and wayland-display
                      (service-stopped? display-service))
             (start-service display-service
                            wayland-display))

           (if (service-running? display-service)
               (for-each (lambda (service)
                           (start-service (lookup-service service)))
                         '#$services)
               (begin
                 ((@ (shepherd support) local-output)
                  ((@ (shepherd support) l10n)
                   "Cannot start wlr-services, because wayland-display is not 
running and WAYLAND_DISPLAY argument has not been supplied."))
               #f)))))
    (stop #~(lambda* (running-value #:optional (stop-display "yes"))
              (use-modules (shepherd service)
                           (shepherd support))

              (for-each (lambda (service)
                          (stop-service (lookup-service service)))
                        '#$services)

              (when (equal? stop-display "yes")
                ((@ (shepherd support) local-output) ((@ (shepherd support) 
l10n) "Stopping wayland-display as well."))
                (stop-service (lookup-service 'wayland-display)))
              #f)))))
(define-public home-wlr-services-service-type
  (service-type
   (name 'home-wayland-display)
   (description "A service to start a list of services, meant for wlr.")
   (default-value '())
   (extensions
    (list (service-extension home-shepherd-service-type
                             wlr-services-shepherd-service)
          (service-extension home-wayland-display-service-type
                             (const #f))))))
```

Config example:
```
(service home-wlr-services-service-type
                  '(waybar kanshi emacs gammastep swayidle
                    blueman-applet network-manager-applet))
```

You can find more in my channel:
https://git.ditigal.xyz/~ruther/guix-exprs/tree/main/item/ruther/home/services/wayland.scm
If anyone had any ideas on improvements, I would appreciate that.

Regards,
Rutherther

Reply via email to