Compare commits

...

12 commits

2 changed files with 96 additions and 32 deletions

View file

@ -1,5 +1,7 @@
#+TITLE: exwm-modeline
[[https://melpa.org/#/exwm-modeline][file:https://melpa.org/packages/exwm-modeline-badge.svg]]
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):
@ -13,15 +15,11 @@ Features:
- Numbers are clickable.
* 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:
#+begin_src emacs-lisp
(require 'exwm-modeline)
#+end_src
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]]:
My preferred way is to use =use-package= with =straight=:
#+begin_src emacs-lisp
(use-package exwm-modeline
:straight (:host github :repo "SqrtMinusOne/exwm-modeline")
:straight t
:after (exwm))
#+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-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
[[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 -*-
;; Copyright (C) 2021 Korytov Pavel
;; Copyright (C) 2021-2023 Korytov Pavel
;; Copyright (C) 2021 Ellis Kenyő
;; Copyright (C) 2008-2020 Natalie Weizenbaum <nex342@gmail.com>
;; Author: 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"))
;; 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.
@ -37,6 +38,8 @@
;; Take a look at `exwm-modeline-mode' for more info.
;;; Code:
(eval-when-compile
(require 'cl-lib))
(require 'exwm)
(require 'exwm-randr)
(require 'exwm-workspace)
@ -49,7 +52,7 @@
(defcustom exwm-modeline-dividers '("[" "]" "|")
"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
workspaces."
:group 'exwm-modeline
@ -58,14 +61,54 @@ workspaces."
(string :tag "Divider")))
(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
: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
"When t, show only workspaces on the current monitor."
"When set, only show workspaces on the current monitor."
: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
;; I'd rather :inherit and override warning there, but well
@ -89,8 +132,11 @@ workspaces."
:group 'exwm-modeline)
(defun exwm-modeline--urgent-p (frame)
"Determine if FRAME is tagged as urgent."
(frame-parameter frame 'exwm-urgency))
"Determine if FRAME is tagged as urgent.
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)
"Determine if FRAME has any X windows."
@ -98,6 +144,14 @@ workspaces."
if (eq frame (buffer-local-value 'exwm--frame (cdr item)))
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)
"Process a click EVENT on the modeline segment."
(interactive "e")
@ -105,7 +159,7 @@ workspaces."
(target
(cl-loop with name = (format "%s" (car (posn-string (event-start event))))
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))
(exwm-workspace-switch target)))
@ -121,8 +175,8 @@ workspaces."
WORKSPACE-LIST is the list of frames to display."
(cl-loop for frame in workspace-list
for i from 0 to (length workspace-list)
for workspace-name = (funcall exwm-workspace-index-map
(exwm-workspace--position frame))
for workspace-name = (exwm-modeline--workspace-index-map
(exwm-workspace--position frame))
with current-frame = (selected-frame)
if (= i 0) collect (nth 0 exwm-modeline-dividers)
collect
@ -196,12 +250,10 @@ face in the segment has to change."
(exwm-modeline-update))
(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:
- `exwm--update-hints'
- `exwm--on-ClientMessage'
Also to be put in the hook `exwm-workspace-switch-hook'.
This function is meant to be advised :after `exwm--update-hints' and
be in the hook `exwm-workspace-switch-hook'.
The modeline is updated if `exwm-workspace--switch-history-outdated'
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
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
`with-demoted-errors'."
(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
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.
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
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)
(advice-add #'exwm-manage--unmanage-window
:after #'exwm-modeline--unmanage-advice)
(add-hook 'exwm-workspace-switch-hook #'exwm-modeline--urgency-advice)
(advice-add #'exwm--update-hints :after #'exwm-modeline--urgency-advice)
(advice-add #'exwm--on-ClientMessage :after #'exwm-modeline--urgency-advice))
(when exwm-modeline-display-urgent
(advice-add #'exwm--update-hints :after #'exwm-modeline--urgency-advice)
(add-hook 'exwm-workspace-switch-hook #'exwm-modeline--urgency-advice)))
(setq global-mode-string (delete '(:eval (exwm-modeline-segment))
global-mode-string))
(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)
(advice-remove #'exwm-manage--unmanage-window
#'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--on-ClientMessage #'exwm-modeline--urgency-advice)))
(remove-hook 'exwm-workspace-switch-hook #'exwm-modeline--urgency-advice)))
(provide 'exwm-modeline)
;;; exwm-modeline.el ends here