The Complete Computing Environment

Plasma Browser Integration

(provide 'cce/plasma-browser)

This stopped working recently, plasma 5.21 at least implements a new D-BUS interface specific to KDE's usecase without maintaining this old one with which I can provide all tabs to completing-read.

KDE Plasma has a Browser Integration now which exposes some common browser operations to the desktop and other applications. The desktop component is installed by default in Fedora KDE, and the browser side is managed through a Firefox plugin installed via Sync.

The desktop component communicates with the Firefox extension and exposes a D-BUS interface which Emacs can use to query for information about open tabs and control audio via mpris MediaPlayer2. The audio controls end up on my lock screen too, which is really great.

For Emacs, I have implemented a function called cce/switch-buffer-or-tab which is bound liberally to <SPC>B, <M-SPC><M-SPC>, and C-x B. This function queries the list of browser tabs from the plugin, and presents them along with the list of active Emacs buffers. Selecting an option will open the browser tab or buffer.

(require 'dbus)

(defun cce/browser-list-tabs (table)
  "Fetch the tabs from the active browser integration and put
   them in table, mutating it."
  (let ((res (dbus-call-method :session "org.kde.plasma.browser_integration" "/TabsRunner"
                               "org.kde.plasma.browser_integration.TabsRunner" "GetTabs")))
     (lambda (obj)
       (let ((id (number-to-string (car (car (alist-get "id" (car obj) nil nil #'equal))))) ; christ alive
             (title (car (car (alist-get "title" (car obj) nil nil #'equal)))))
         (puthash title id table)))

(defun cce/populate-switcher-hash (only-tabs)
  (let ((coll (make-hash-table :test #'equal)))
    (cce/browser-list-tabs coll)
    (unless only-tabs
      (dolist (name (ivy--buffer-list ""))
        (puthash name (list name 'buffer) coll)))

(defun cce/make-exwm-buffer-name (window-title &optional exwm-class)
  (let ((exwm-name (concat (or exwm-class "Firefox") ":" window-title)))
    (if (<= (length exwm-name) 58) exwm-name
      (concat (substring exwm-name 0 57) "..."))))

(defun cce/switcher-callback (collection choice)
  (let ((val (gethash choice collection)))
    (case (type-of val)
      ('string (let ((exwm-title (cce/make-exwm-buffer-name (concat choice " - Mozilla Firefox"))))
                 (dbus-call-method :session "org.kde.plasma.browser_integration" "/TabsRunner"
                                   "org.kde.plasma.browser_integration.TabsRunner" "Activate"
                                   :int32 (truncate (string-to-number val)))
                 (sit-for 0.1)
                 (with-current-buffer (or (get-buffer exwm-title)
                                          (get-buffer (concat " " exwm-title)))
                   (exwm-workspace-switch exwm--desktop)
                   (exwm-layout--show exwm--id))))
      ('cons   (switch-to-buffer (car val))))))

(defun cce/switch-buffer-or-tab (&optional only-tabs)
  (interactive "P")
  (let ((coll (cce/populate-switcher-hash only-tabs)))
    (cce/switcher-callback coll (ivy-read "Switch to: " coll))))

(evil-leader/set-key "B" #'cce/switch-buffer-or-tab)
(global-set-key (kbd "C-x B") #'cce/switch-buffer-or-tab)

(global-set-key (kbd "M-SPC") nil)
(global-set-key (kbd "M-SPC M-SPC") #'cce/switch-buffer-or-tab)