Compare commits

...

12 commits

2 changed files with 96 additions and 32 deletions

View file

@ -1,5 +1,7 @@
#+TITLE: exwm-modeline #+TITLE: exwm-modeline
[[https://melpa.org/#/exwm-modeline][file:https://melpa.org/packages/exwm-modeline-badge.svg]]
A modeline segment to display exwm workspaces. A modeline segment to display exwm workspaces.
Here's how it looks near the list of [[https://github.com/nex3/perspective-el][perspectives]] (the segment of the current package is to the left): Here's how it looks near the list of [[https://github.com/nex3/perspective-el][perspectives]] (the segment of the current package is to the left):
@ -13,15 +15,11 @@ Features:
- Numbers are clickable. - Numbers are clickable.
* Installation * Installation
As the package isn't yet available anywhere but in this repository, you can clone the repository, add it to the =load-path=, and =require= the package: The package is available on MELPA. Install it however you usually install packages, I use [[https://github.com/jwiegley/use-package][use-package]] and [[https://github.com/raxod502/straight.el][straight.el]]:
#+begin_src emacs-lisp
(require 'exwm-modeline)
#+end_src
My preferred way is to use =use-package= with =straight=:
#+begin_src emacs-lisp #+begin_src emacs-lisp
(use-package exwm-modeline (use-package exwm-modeline
:straight (:host github :repo "SqrtMinusOne/exwm-modeline") :straight t
:after (exwm)) :after (exwm))
#+end_src #+end_src
@ -35,6 +33,8 @@ Set =exwm-modeline-randr= to nil to turn off filtering of workspaces by monitor.
Set =exwm-modeline-short= to =t= display only the current workspace in the modeline. Set =exwm-modeline-short= to =t= display only the current workspace in the modeline.
Set =exwm-modeline-display-urgent= to nil to turn off displaying whether a workspace has an urgent window. This will significantly decrease the number of modeline updates, which may help with performance issues.
* Credits * Credits
[[https://github.com/nex3/perspective-el][perspective.el]] by [[https://github.com/nex3][@nex3]] was extremely instructive on how to make a modeline segment individual to a particular frame and avoid recalculating it too often. [[https://github.com/nex3/perspective-el][perspective.el]] by [[https://github.com/nex3][@nex3]] was extremely instructive on how to make a modeline segment individual to a particular frame and avoid recalculating it too often.

View file

@ -1,14 +1,15 @@
;;; exwm-modeline.el --- A modeline segment for EXWM workspaces -*- lexical-binding: t -*- ;;; exwm-modeline.el --- A modeline segment for EXWM workspaces -*- lexical-binding: t -*-
;; Copyright (C) 2021 Korytov Pavel ;; Copyright (C) 2021-2023 Korytov Pavel
;; Copyright (C) 2021 Ellis Kenyő ;; Copyright (C) 2021 Ellis Kenyő
;; Copyright (C) 2008-2020 Natalie Weizenbaum <nex342@gmail.com> ;; Copyright (C) 2008-2020 Natalie Weizenbaum <nex342@gmail.com>
;; Author: Korytov Pavel <thexcloud@gmail.com> ;; Author: Korytov Pavel <thexcloud@gmail.com>
;; Maintainer: Korytov Pavel <thexcloud@gmail.com> ;; Maintainer: Korytov Pavel <thexcloud@gmail.com>
;; Version: 0.1.2 ;; Version: 0.1.3
;; Package-Requires: ((emacs "27.1") (exwm "0.26")) ;; Package-Requires: ((emacs "27.1") (exwm "0.26"))
;; Homepage: https://github.com/SqrtMinusOne/pomm.el ;; Homepage: https://github.com/SqrtMinusOne/exwm-modeline
;; Published-At: 2021-12-22
;; This file is NOT part of GNU Emacs. ;; This file is NOT part of GNU Emacs.
@ -37,6 +38,8 @@
;; Take a look at `exwm-modeline-mode' for more info. ;; Take a look at `exwm-modeline-mode' for more info.
;;; Code: ;;; Code:
(eval-when-compile
(require 'cl-lib))
(require 'exwm) (require 'exwm)
(require 'exwm-randr) (require 'exwm-randr)
(require 'exwm-workspace) (require 'exwm-workspace)
@ -49,7 +52,7 @@
(defcustom exwm-modeline-dividers '("[" "]" "|") (defcustom exwm-modeline-dividers '("[" "]" "|")
"Plist of strings used to create the string shown in the modeline. "Plist of strings used to create the string shown in the modeline.
First string is the start of the modestring, second is the The first string is the start of the modestring, second is the
closing of the modestring, and the last is the divider between closing of the modestring, and the last is the divider between
workspaces." workspaces."
:group 'exwm-modeline :group 'exwm-modeline
@ -58,14 +61,54 @@ workspaces."
(string :tag "Divider"))) (string :tag "Divider")))
(defcustom exwm-modeline-short nil (defcustom exwm-modeline-short nil
"When t, display only the current workspace in the modeline." "When set, display only the current workspace in the modeline."
:group 'exwm-modeline :group 'exwm-modeline
:type 'boolean) :type 'boolean
:set (lambda (sym value)
(set-default sym value)
(when (bound-and-true-p exwm-modeline-mode)
(exwm-modeline-update))))
(defcustom exwm-modeline-randr t (defcustom exwm-modeline-randr t
"When t, show only workspaces on the current monitor." "When set, only show workspaces on the current monitor."
:group 'exwm-modeline :group 'exwm-modeline
:type 'boolean) :type 'boolean
:set (lambda (sym value)
(set-default sym value)
(when (bound-and-true-p exwm-modeline-mode)
(exwm-modeline-update))))
(defcustom exwm-modeline-display-urgent t
"When set, display the urgent status in the modeline.
With that set, the modeline will be updated on every workspace
switch, so the number of updates is increased significantly."
:group 'exwm-modeline
:type 'boolean
:set (lambda (sym value)
(set-default sym value)
(when (bound-and-true-p exwm-modeline-mode)
(exwm-modeline-update)
(if value
(progn
(advice-add #'exwm--update-hints :after #'exwm-modeline--urgency-advice)
(add-hook 'exwm-workspace-switch-hook #'exwm-modeline--urgency-advice))
(advice-remove #'exwm--update-hints #'exwm-modeline--urgency-advice)
(remove-hook 'exwm-workspace-switch-hook #'exwm-modeline--urgency-advice)))))
(defcustom exwm-modeline-workspace-index-map 'exwm-workspace-index-map
"Function for mapping a workspace index to a string for display.
If the value if 'exwm-workspace-index-map, dereference the
`exwm-workspace-index-map'variable. Otherwise, use a function."
:group 'exwm-workspace
:type '(choice
(symbol exwm-workspace-index-map :tag "Use EXWM default")
(function))
:set (lambda (sym value)
(set-default sym value)
(when (bound-and-true-p exwm-modeline-mode)
(exwm-modeline-update))))
(defface exwm-modeline-current-workspace (defface exwm-modeline-current-workspace
;; I'd rather :inherit and override warning there, but well ;; I'd rather :inherit and override warning there, but well
@ -89,8 +132,11 @@ workspaces."
:group 'exwm-modeline) :group 'exwm-modeline)
(defun exwm-modeline--urgent-p (frame) (defun exwm-modeline--urgent-p (frame)
"Determine if FRAME is tagged as urgent." "Determine if FRAME is tagged as urgent.
(frame-parameter frame 'exwm-urgency))
Always return nil if `exwm-modeline-display-urgent' is not set."
(when exwm-modeline-display-urgent
(frame-parameter frame 'exwm-urgency)))
(defun exwm-modeline--populated-p (frame) (defun exwm-modeline--populated-p (frame)
"Determine if FRAME has any X windows." "Determine if FRAME has any X windows."
@ -98,6 +144,14 @@ workspaces."
if (eq frame (buffer-local-value 'exwm--frame (cdr item))) if (eq frame (buffer-local-value 'exwm--frame (cdr item)))
return t)) return t))
(defun exwm-modeline--workspace-index-map (i)
"Map the workspace index I to a string for display.
See `exwm-modeline-workspace-index-map' for behaviour."
(if (eq exwm-modeline-workspace-index-map 'exwm-workspace-index-map)
(funcall exwm-workspace-index-map i)
(funcall exwm-modeline-workspace-index-map i)))
(defun exwm-modeline--click (event) (defun exwm-modeline--click (event)
"Process a click EVENT on the modeline segment." "Process a click EVENT on the modeline segment."
(interactive "e") (interactive "e")
@ -105,7 +159,7 @@ workspaces."
(target (target
(cl-loop with name = (format "%s" (car (posn-string (event-start event)))) (cl-loop with name = (format "%s" (car (posn-string (event-start event))))
for i from 0 to (1- (length exwm-workspace--list)) for i from 0 to (1- (length exwm-workspace--list))
if (string-equal (funcall exwm-workspace-index-map i) name) if (string-equal (exwm-modeline--workspace-index-map i) name)
return i)) return i))
(exwm-workspace-switch target))) (exwm-workspace-switch target)))
@ -121,8 +175,8 @@ workspaces."
WORKSPACE-LIST is the list of frames to display." WORKSPACE-LIST is the list of frames to display."
(cl-loop for frame in workspace-list (cl-loop for frame in workspace-list
for i from 0 to (length workspace-list) for i from 0 to (length workspace-list)
for workspace-name = (funcall exwm-workspace-index-map for workspace-name = (exwm-modeline--workspace-index-map
(exwm-workspace--position frame)) (exwm-workspace--position frame))
with current-frame = (selected-frame) with current-frame = (selected-frame)
if (= i 0) collect (nth 0 exwm-modeline-dividers) if (= i 0) collect (nth 0 exwm-modeline-dividers)
collect collect
@ -196,12 +250,10 @@ face in the segment has to change."
(exwm-modeline-update)) (exwm-modeline-update))
(defun exwm-modeline--urgency-advice (&rest _) (defun exwm-modeline--urgency-advice (&rest _)
"Update the modeline after change in the urgency status. "Update the modeline after a change in the urgency status.
This function is meant to be advised :after the following: This function is meant to be advised :after `exwm--update-hints' and
- `exwm--update-hints' be in the hook `exwm-workspace-switch-hook'.
- `exwm--on-ClientMessage'
Also to be put in the hook `exwm-workspace-switch-hook'.
The modeline is updated if `exwm-workspace--switch-history-outdated' The modeline is updated if `exwm-workspace--switch-history-outdated'
is set to t, because EXWM sets that variable whenever a window updates is set to t, because EXWM sets that variable whenever a window updates
@ -209,7 +261,14 @@ it urgency status. To avoid running the function too often,
`exwm-workspace--update-switch-history' is also called, which resets `exwm-workspace--update-switch-history' is also called, which resets
the variable. the variable.
Because the first two functions are very much critical for the normal However, one issue with the mentioned variable is that it is also
set whenever the workspace is switched, so using that advice also
increases the number of required updates. Optimizing that would
require more substantial modifications to EXWM code, so in this
package applying that advice is made optional with the
`exwm-modeline-display-urgent' variable.
Because the first function is very much critical for the normal
functioning of EXWM, the entire thing is wrapped in functioning of EXWM, the entire thing is wrapped in
`with-demoted-errors'." `with-demoted-errors'."
(with-demoted-errors "Error in exwm-modeline--urgency-advice: %S" (with-demoted-errors "Error in exwm-modeline--urgency-advice: %S"
@ -229,10 +288,16 @@ monitor. To display only the current workspace, enable
`exwm-modeline-short', and to disable the filtering by the `exwm-modeline-short', and to disable the filtering by the
monitor, disable `exwm-modeline-randr'. monitor, disable `exwm-modeline-randr'.
Also take a look at the `exwm-modeline' group for faces If `exwm-modeline-display-urgent' is set, the mode also displays
if the workspace has a window market as urgent. However, this
option forces the modeline to update after every workspace
switch, so it may be wise to disable that in case of performance
issues.
Also, take a look at the `exwm-modeline' group for faces
customization. customization.
This implementation indents to reduce the count of times of This implementation intends to reduce the count of times of
evaluating the modestring; the rendered modestring is saved as a evaluating the modestring; the rendered modestring is saved as a
frame parameter, and `exwm-modeline-segment' just returns it. frame parameter, and `exwm-modeline-segment' just returns it.
@ -250,9 +315,9 @@ cases when the workspace list changes."
(add-hook 'exwm-manage-finish-hook #'exwm-modeline-update) (add-hook 'exwm-manage-finish-hook #'exwm-modeline-update)
(advice-add #'exwm-manage--unmanage-window (advice-add #'exwm-manage--unmanage-window
:after #'exwm-modeline--unmanage-advice) :after #'exwm-modeline--unmanage-advice)
(add-hook 'exwm-workspace-switch-hook #'exwm-modeline--urgency-advice) (when exwm-modeline-display-urgent
(advice-add #'exwm--update-hints :after #'exwm-modeline--urgency-advice) (advice-add #'exwm--update-hints :after #'exwm-modeline--urgency-advice)
(advice-add #'exwm--on-ClientMessage :after #'exwm-modeline--urgency-advice)) (add-hook 'exwm-workspace-switch-hook #'exwm-modeline--urgency-advice)))
(setq global-mode-string (delete '(:eval (exwm-modeline-segment)) (setq global-mode-string (delete '(:eval (exwm-modeline-segment))
global-mode-string)) global-mode-string))
(remove-hook 'exwm-workspace-list-change-hook #'exwm-modeline-update) (remove-hook 'exwm-workspace-list-change-hook #'exwm-modeline-update)
@ -260,9 +325,8 @@ cases when the workspace list changes."
(remove-hook 'exwm-manage-finish-hook #'exwm-modeline-update) (remove-hook 'exwm-manage-finish-hook #'exwm-modeline-update)
(advice-remove #'exwm-manage--unmanage-window (advice-remove #'exwm-manage--unmanage-window
#'exwm-modeline--unmanage-advice) #'exwm-modeline--unmanage-advice)
(remove-hook 'exwm-workspace-switch-hook #'exwm-modeline--urgency-advice)
(advice-remove #'exwm--update-hints #'exwm-modeline--urgency-advice) (advice-remove #'exwm--update-hints #'exwm-modeline--urgency-advice)
(advice-remove #'exwm--on-ClientMessage #'exwm-modeline--urgency-advice))) (remove-hook 'exwm-workspace-switch-hook #'exwm-modeline--urgency-advice)))
(provide 'exwm-modeline) (provide 'exwm-modeline)
;;; exwm-modeline.el ends here ;;; exwm-modeline.el ends here