Skip to content

Custom app info functions for less common compositors

Sam Stites edited this page Dec 22, 2025 · 8 revisions

While Gnome and KDE should work OOTB on Wayland, less common compositors require emacs-everywhere-app-info-function to be set to a custom app-info function.

Hyprland

(setq emacs-everywhere-window-focus-command (list "hyprctl" "dispatch" "focuswindow" "address:%w"))
(setq emacs-everywhere-app-info-function #'emacs-everywhere--app-info-linux-hyprland)

(require 'json)
(defun emacs-everywhere--app-info-linux-hyprland ()
  "Return information on the current active window, on a Linux Hyprland session."
  (let* ((json-string (emacs-everywhere--call "hyprctl" "-j" "activewindow"))
         (json-object (json-read-from-string json-string))
         (window-id (cdr (assoc 'address json-object)))
         (app-name (cdr (assoc 'class json-object)))
         (window-title (cdr (assoc 'title json-object)))
         (window-geometry (list (aref (cdr (assoc 'at json-object)) 0)
                                (aref (cdr (assoc 'at json-object)) 1)
                                (aref (cdr (assoc 'size json-object)) 0)
                                (aref (cdr (assoc 'size json-object)) 1))))
    (make-emacs-everywhere-app
     :id window-id
     :class app-name
     :title window-title
     :geometry window-geometry)))

Niri

(setq emacs-everywhere-system-configs
        (append emacs-everywhere-system-configs
                '(((wayland . niri)
                   :focus-command ("niri" "msg" "action" "focus-window" "--id" "%w")
                   :info-function emacs-everywhere--app-info-linux-niri))))

(defun my-emacs-everywhere/update-niri-socket ()
  ;; NOTE: If you use doomemacs, you will likely need to update this
  ;; environment variable so that `niri msg' interfaces with the correct
  ;; socket. You only need this once per server initialization: place this in
  ;; (use-package! emacs-everywhere :config ...)  or an
  ;; (after! emacs-everywhere ...) block.
  (let* ((rundir (format "/run/user/%d/" (user-uid)))
         (sockets (when (file-directory-p rundir)
                     (directory-files rundir nil "^niri.*sock$" t)))
         ;; NOTE: we assume that there will only be one socket
         (socket-file (if (consp sockets)
                     (concat rundir (car sockets))
                     nil)))
    (if socket-file
        (setenv "NIRI_SOCKET" socket-file)
        ;; the nil case will fall back on whatever can be found by `getenv'
        (message "Could not find an active niri socket"))))

(defun emacs-everywhere--app-info-linux-niri ()
  "Return information on the current active window, on a Linux Niri session."
  (require 'json)
  (let*
    ((json-raw (emacs-everywhere--call "niri" "msg" "-j" "focused-window"))
     (is-err (string-prefix-p "Error" json-raw)))
  (if is-err
      (progn
        (message "[emacs-everywhere] %s" json-raw)
        (message "[emacs-everywhere] NIRI_SOCKET=%s" (getenv "NIRI_SOCKET"))
        (error "[emacs-everywhere] Error in `niri msg -j focused-window' (see *messages*)"))
      (let*
         ((json (json-read-from-string json-raw)) ;; -j for json
          (wid (cdr (assq 'id json)))
          (window-id (if (numberp wid) (number-to-string wid) wid))
          (window-title (cdr (assq 'title json)))
          (app-name (cdr (assq 'app_id json)))
          (window-geometry nil)) ;; no geometry in niri
         (make-emacs-everywhere-app
          :id window-id
          :class app-name
          :title window-title
          :geometry window-geometry))
       )))

Clone this wiki locally