ch11ng / exwm

Emacs X Window Manager
2.85k stars 134 forks source link

Add support for workspaces using mode line menu. Also minor annoyances with workspace switching #638

Open emacksnotes opened 5 years ago

emacksnotes commented 5 years ago

Add support for workspaces using mode line menu.

This report takes the discussions in Please add workspace number to mode line · Issue #556 · ch11ng/exwm a bit further.

How it (possibly) looks

Workspace indicator in the minibuffer

exwm-ws-1

Use mouse-1 on workspace indicator to switch / add / delete workspaces

exwm-ws-2

Use mouse-3 for a full workspace menu

exwm-ws-3

Elisp code for the suggestion above

(easy-menu-define exwm-workspace-menu nil
  "Menu for Exwm Workspace.

Also used in `exwm-mode-line-workspace-map'."
  '("Exwm Workspace"
    ["Add workspace" exwm-workspace-add]
    ["Delete current workspace" exwm-workspace-delete]
    ["Move workspace to" exwm-workspace-move]
    ["Swap workspaces" exwm-workspace-swap]
    ["Move X window to" exwm-workspace-move-window]
    ["Move X window from" exwm-workspace-switch-to-buffer]
    ["Toggle minibuffer" exwm-workspace-toggle-minibuffer]
    ["Switch workspace" exwm-workspace-switch]
    ;; Place this entry at bottom to avoid selecting others by accident.
    ("Switch to" :filter
     (lambda (&rest _args)
       (mapcar (lambda (i)
                 `[,(format "workspace %d" i)
                   (lambda ()
                     (interactive)
                     (exwm-workspace-switch ,i))
                   (/= ,i exwm-workspace-current-index)])
               (number-sequence 0 (1- (exwm-workspace--count))))))))

(defvar exwm-mode-line-workspace-map
  (let ((map (make-sparse-keymap)))
    (define-key map [mode-line mouse-1] 'exwm-workspace-switch)
    (define-key map [mode-line mouse-3] exwm-workspace-menu)
    map)
  "Local keymap for EXWM mode line string.  See `exwm-mode-line-format'.")

(defcustom exwm-mode-line-format
  `("["
    (:propertize (:eval (format "WS-%d" exwm-workspace-current-index))
         local-map ,exwm-mode-line-workspace-map
         face bold
         mouse-face mode-line-highlight
         help-echo "mouse-1: Switch to / add / delete to EXWM workspaces.
mouse-2: EXWM Workspace menu.
")
    "]")
  "EXWM workspace in the mode line."
  :type 'sexp)

;; FIXME: Don't push the value.  Instead push a symbol.  If done, (1)
;; this will avoid duplicate entries for EXWM workspace (2) The mode
;; line string will change in sync with the value of
;; `exwm-mode-line-format'.
(add-to-list 'mode-line-misc-info exwm-mode-line-format t) 
emacksnotes commented 5 years ago

Annoyance - 1: Typing + requires pressing shift.

Suggestion-1

So, add = (i.e., non-shifted + to the correspong transient map)


Annoyance - 2: The prompt string--Switch to [+/-]: [0] 1 2 3--is confusing on so many levels

The verb used is Switch. And the + and - in the prompt confused me initially, and I came up with the following interpretations for these keys in the context of switch workspaces.

My interpretation for + and -

  1. +: Switch to next workspace in sequence i.e., Add 1 to current workspace and swith to it
  2. -: Switch to previous workspace in sequence i.e., Subtract 1 from current workspace and swith to it

When I actually invoked those commands, what they didn't resemble what I imagined. To my surprise, my intepretation and reality were poles apart.

Whatever happened, I couldn't see any Switch happening

Suggestion - 2: Review the prompt sting (or preferably rework the implementation)

emacksnotes commented 5 years ago

... further updates after more experimentation

- did something because the screen flickered. And I am not sure what actually happened.

If I press +, a new workspace gets added, but switch to it happens

If press -, a workspace gets deleted, and it happens to the last of the workspaces.

My Question

  1. If the default behaviour is to NOT switch, then why does the prompt say Switch?
  2. + is repeatable. Why isn't - repeatable?
  3. When deleting a workspace, I would assume that it is the current workspace that gets deleted. Why is the last of the workspace that is getting deleted?

It is possible that much of the behaviour I notice above are "right" as such. Nevertheless, the behaviour surprises and confuses a new user like me, and need to be counted against "user interface" issues.

emacksnotes commented 5 years ago

Some improvements to my exwm-mode-line-format

(defcustom exwm-mode-line-format
 `("["
   (:propertize (:eval (format "WS-%d" exwm-workspace-current-index))
  1. Instead of showing all the workspace, show all the workspaces, and highlight the current one. This is what the GNOME Panel does. (_Once I have "figured out" how EXWM, I will do away with gnome-panel. Until then, the gnome-panel is a crutch that I cannot do away with. It is helping me ease in to the EXWM of doing things._)
  2. The tooltip for the workspace can show the user-assigned name for the workspace.
  3. Clicking on the workspace indicator can switch to that workspace.
  4. There could be a + operator in the modeline for creating a new workspace and switching to it.
  5. There could be a - operator for ripping down a workspace.
  6. Instead of an explicit + and - operators, the local map of workspace indicator can have additional mouse binding (with modifiers) for "create after this", "create before this", or "delete this".

I could be bike-shedding here, ... but I have argued in https://github.com/ch11ng/exwm/issues/639 that the workspace indicator is a must-have in form or the other.

ch11ng commented 5 years ago

Add support for workspaces using mode line menu.

This works well until you split that window. We'll have to find another place.

So, add = (i.e., non-shifted + to the correspong transient map)

I've seen people doing this before, but I wonder what is wrong with shift. Also it's not uncommon when pressing shift and = doesn't produce + on some keyboard layouts (Japanese I think).

Annoyance - 2: The prompt string--Switch to [+/-]: [0] 1 2 3--is confusing on so many levels

[+/-] means users can add an extra workspace with a +, or delete the selected workspace with a - right from the prompt. Perhaps the prompt message is a little confusing. We have dedicated commands for doing these and this is just a shortcut for the most frequent workspace manipulations. Workspaces can be further rearranged with exwm-workspace-move BTW.

  1. Instead of showing all the workspace, show all the workspaces, and highlight the current one. This is what the GNOME Panel does. (Once I have "figured out" how EXWM, I will do away with gnome-panel. Until then, the gnome-panel is a crutch that I cannot do away with. It is helping me ease in to the EXWM of doing things.)

The current workspace is the one being selected when the prompt first shows up. Please describe how exactly you want it rendered so we can make it more distinguishable.

  1. The tooltip for the workspace can show the user-assigned name for the workspace.

It's already possible. See exwm-workspace-index-map.

  1. Clicking on the workspace indicator can switch to that workspace.
  2. There could be a + operator in the modeline for creating a new workspace and switching to it.
  3. There could be a - operator for ripping down a workspace.
  4. Instead of an explicit + and - operators, the local map of workspace indicator can have additional mouse binding (with modifiers) for "create after this", "create before this", or "delete this".

Same as the first question.

emacksnotes commented 5 years ago

Add support for workspaces using mode line menu.

This works well until you split that window.

I followed your lead and split that window. The workspace indicator in the mode line works as intended. I believe your objection is that the indicator gets duplicated if there are multiple windows on display, and you would prefer to avoid this duplication.

We'll have to find another place.

Put the workspace indicator in the tool bar.

Demo code for having workspace indicator in tool-bar

(tool-bar-mode 1)

(setq exwm-workspace-index-map (lambda (i) (format "Workspace: %d" i)))

(defun exwm-update-workspaces-in-toolbar ()
  (cl-loop for i from 0 to (1- (exwm-workspace--count)) do
       (let* ((cmd (intern (format "exwm-workspace-switch-create-%d" i)))
          (name (mapconcat #'capitalize (split-string (symbol-name cmd) "[-]") " "))
          (key (intern name)))
         (define-key-after global-map (vector 'tool-bar key)
           (append `(menu-item ,name (lambda ()
                       (interactive)
                       (exwm-workspace-switch-create ,i))
                   :help (funcall exwm-workspace-index-map ,i)
                   :enable (/= ,i exwm-workspace-current-index))
               (list :image (create-image "/usr/share/icons/gnome/16x16/devices/computer.png"))))
         (force-mode-line-update))))

(add-hook 'menu-bar-update-hook 'exwm-update-workspaces-in-toolbar)

Screenshots of the demo code in action

Note that toolbar icon for the active workspace is rendered differently from the icons for the non-active workspaces

Workspace 2 is displaying Firefox

Screenshot from 2019-09-02 21-26-31

Workspace 1 is displaying the *info* node for define-key, specifically the description of :image property

Screenshot from 2019-09-02 21-28-58

Some improvements to above code

  1. May be put a frame--a :relief or a border--around the active workspace, much like what Gnome-panel does.
  2. Add the toolbar icons at fixed position in the toolbar. This will ensure that whenever the toolbar is re-drawn--for example because of the change in major mode of the current buffer--the icons stay put and doesn't shift around much.
   Instead of showing all the workspace, show all the workspaces, and highlight the current one. This is what the GNOME Panel does. 

The current workspace is the one being selected when the prompt first shows up. Please describe how exactly you want it rendered so we can make it more distinguishable.

Look at the workspace indicator on the bottom GNOME panel on any of the various screenshots and videos that I have shared here and elsewhere.

zevlg commented 5 years ago

Add support for workspaces using mode line menu.

This works well until you split that window.

I followed your lead and split that window. The workspace indicator in the mode line works as intended. I believe your objection is that the indicator gets duplicated if there are multiple windows on display, and you would prefer to avoid this duplication.

Here is the hack to avoid duplication of part of the mode-line - https://github.com/zevlg/telega.el/issues/83

ch11ng commented 5 years ago

Add support for workspaces using mode line menu.

This works well until you split that window.

I followed your lead and split that window. The workspace indicator in the mode line works as intended. I believe your objection is that the indicator gets duplicated if there are multiple windows on display, and you would prefer to avoid this duplication.

No. I mean with a crowded minibuffer and a narrow screen it'd get hidden if you split the window horizontally. The image from your original post shows the content already occupies more than half of that minibuffer.

We'll have to find another place.

Put the workspace indicator in the tool bar.

My feeling is most users don't use tool-bar/menu-bar in EXWM. The vertical space is precious as unluckily most monitors are in 16:9 nowadays.

Instead of showing all the workspace, show all the workspaces, and highlight the current one. This is what the GNOME Panel does. The current workspace is the one being selected when the prompt first shows up. Please describe how exactly you want it rendered so we can make it more distinguishable.

Look at the workspace indicator on the bottom GNOME panel on any of the various screenshots and videos that I have shared here and elsewhere.

I mean how to decorate that text. We've already used color and brackets for other purpose. Perhaps underline/box?

emacksnotes commented 5 years ago

Add support for workspaces using mode line menu.

This works well until you split that window.

I followed your lead and split that window. The workspace indicator in the mode line works as intended. I believe your objection is that the indicator gets duplicated if there are multiple windows on display, and you would prefer to avoid this duplication.

Here is the hack to avoid duplication of part of the mode-line - zevlg/telega.el#83

(setq telega-mode-line-format (list '(:eval (when (and (window-at-side-p nil 'bottom) (window-at-side-p nil 'right) (telega-server-live-p)) telega-mode-line-string))))

Checking for bottom-right window etc. helps with de-duplication of "global"--contrast this with "buffer local" or "window local"--information. @ch11ng has expressed the concern that when this "extreme" window gets split and becomes much smaller, the mode line also gets smaller and all the information that is shoved in to the mode line may overflow the screen.

There seems to be a general interest in having a EXWM panel. There seems to be three packages / approaches that are suggested in [this reddit thread]( See https://old.reddit.com/r/emacs/comments/cz3py2/pure_elisp_panel_for_exwm/)

  1. Dedicate a one-line tall window for the "panel" (cf lv.el --- Other echo area)

  2. Create a posframe for the panel. (cf. statusbar.el --- Emacs statusbar over the minibuffer with child frames ) and position it adjacent to the exwm-systemtray (see https://github.com/dakra/statusbar.el/blob/14588bac9928bc5b2d9af70bb3d5439014bbedbd/statusbar.el#L128)

  3. Multiplex the panel on to the minibuffer / echo area when it is empty. (cf. doom-modeline --- A fancy and fast mode-line inspired by minimalism design). emacs-mini-modeline : Display emacs mode line in minibuffer implements a slightly different variant of this idea. See @QiangF's recent message https://github.com/ch11ng/exwm/issues/637#issue-486682785.

  4. I have suggested use of toolbar.

Whatever be the approach one common theme is to re-use (or create) a window / frame that is global in scope (and locked in to place), and insert the stuff there.

When I loaded EXWM for the first time, I was a little bit disoriented. I was staring at a blank screen. There were no launchers or systemtray. I had to read the documentation to figure out how to launch Firefox, or ensure that Wifi router is up. I understand that window managers are for those that prefer minimal clutter on their screen. This minimalist design is an impediment for those who use deskop enviroments like GNOME, and want to switch over to window managers like EXWM . To cross over the impediment posed by minimalism, GUI features like launchers, workspace indicators and systemtrays are a an absolute necessity. I wish EXWM (or any of the sister packages) provides these facilities out of the box, and enable it by default. EXWM already implements systemtray. A cursory look at the Window Manager specification tells me that panels are first class citizens. I believe EXWM will ultimately get a panel of it's own.

emacksnotes commented 5 years ago

Put the workspace indicator in the tool bar.

My feeling is most users don't use tool-bar/menu-bar in EXWM. The vertical space is precious as unluckily most monitors are in 16:9 nowadays.

I am lobbying for new users of Emacs and EXWM. They do use menu bar, tool bar etc because they aren't very experienced enough to turn them off. If most experienced users, turn tool bar off then having something there wouldn't bother them at all, right?

IME, if EXWM already provided a workspace indicators, it would have not only helped me transition from Desktop Enviroments to Window Manager, but also get used to the quirkiness of EXWM workspaces vis-a-vis Emacs frames.

I agree that Toolbar takes too much space, even when it is less rarely packed than the menu bar. Nevertheless, it looks like a good candidate for "global" information, because it is already there, and can be used as a stand-in for an EXWM Panel.

One of the points I was making in this thread was this: EXWM should always generate the "panel" string, but it should NOT concern itself with where in the screen it gets placed. The placement is user's wish and responsibility. Once the "panel"-string is available, users can concern themselves with where this "panel"-string will go, be it tool bar, minibuffer, modeline, a posframe or even a "first class" EXWM provided panel.

I have already provided concrete examples on what I mean by an "EXWM Panel String", and demonstrated that they can go either in to a tool-bar and mode-line. I am working on a sample code that generates a "panel string" (of launchers and workspaces) that will go in to a posframe.

Olivia5k commented 5 years ago

This minimalist design is an impediment for those who use deskop enviroments like GNOME, and want to switch over to window managers like EXWM

That intersection of people is so small that it is insignificant in terms of where this kind of project should go. Even then, going from a DE to a WM is tricky enough that adding the layer of Emacs and elisp in there is going to guarantee a bad experience.

Furthermore, I feel like your suggestions are kind of missing the spirit of why something like EXWM exists - you get the building blocks and an environment to hack things. Nothing is stopping you from adding things; you seem to be doing that quite well already. But, trying to cram stuff into the project for a projected audience (that I believe, again, barely exists) is going to hurt and alienate the existing audience, and that's rarely something that you want to do as a maintainer.

emacksnotes commented 5 years ago

But, trying to cram stuff into the project for a projected audience (that I believe, again, barely exists) is going to hurt and alienate the existing audience, and that's rarely something that you want to do as a maintainer.

Are you one of the maintainers of this project? The mention of maintainer in that sentence gives me an impression that you are indeed one.

Whether you are a maintainer or not, I am not happy to hear what you are saying. For the sake of record, I have made positive contributions to this project for the brief few weeks I have been here. Just look at this issue https://github.com/ch11ng/exwm/issues/628. Not only I reported that bug, I have spent non-trivial amount of time in collecting debug logs.

I am not happy with @thiderman's comment.

@ch11ng, you will not see me any more in this forum. Good Bye!

zevlg commented 5 years ago

I rarely do horizontal splits, so I just use top window's header line to show all necessary information, such as list of X apps currently running in the workspace, workspace number, and the date. I'm pretty happy with such setup

here is the screenshot to get the insight exwm-screen

ch11ng commented 5 years ago

It appears I missed a lot of stuffs when absent and owe your some clarifications.

First off, there are no other maintainers than @medranocalvo and me. We are both busy guys and we'd appreciate it if others in the Emacs community can join us and help out.

As for the WM versus DE debate, EXWM is of course a WM but does contain some DE components. An X WM is by definition an X client only responsible for managing X windows, and no visual component is actually required. Other desktop components like panels/pagers/trays are not part of it. But as Emacs is so versatile we took the liberty to ship some minimal Elisp tools with EXWM in hope that they will make our life easier. Ideally they should be made individual packages. Users are also encouraged to contribute their own packages, be it a panel or a tray.

That said, as an X WM EXWM is also supposed to work with DE components not specifically designed for it. So it's perfectly fine to deploy things like gnome-panel along with EXWM.

emacksnotes commented 5 years ago

I have already provided concrete examples on what I mean by an "EXWM Panel String", and demonstrated that they can go either in to a tool-bar and mode-line. I am working on a sample code that generates a "panel string" (of launchers and workspaces) that will go in to a posframe.

Screenshot of Workspace Indicators and Launchers

The screenshot below shows what to look for in the Video linked below.

Note that the panel has

Note also that I can

Pay attention to how the workspace indicator moves as I switch to between different workspaces.

Emacs buffers have both a "posframe" and "exwm panel" Screenshot from 2019-09-14 14-58-33

EXWM-mode buffers have no "posframe". But they do have a "exwm panel" Screenshot from 2019-09-14 15-06-33

Video demonstrating Workspace Indicators and App Launchers using an EXWM-wide panel and Emacs-specific posframe

(To appreciate what happens in the video, please at the screenshots, and the associated notes in the previous section)

In the video, note that

  1. When I load exwm-panel.el, it adds a "Hello World" string to the panel.
  2. When I load exwm-panel-string.el, it replaces the above "Hello World" panel string with app launchers and workspace indicators.

Link to video

https://drive.google.com/file/d/1PCGgpXKv7ETLoAbQL97NQU78hpDloTau/view?usp=sharing

Emacs Lisp files used in above demo

exwm-panel-string.el.txt exwm-panel.el.txt

Implementation notes

exwm-panel.el is based on minibuffer-specific parts of exwm-workspace.el.

The changes I had to do were

  1. Remove minibuffer specific assumptions.
  2. Use of exwm-workspace--on-ConfigureNotify1 to handle EXWM-panel specific handling.

The panel code is only a Proof-of-Concept.

I code above demonstrates what a EXWM-panel library looks like. If such a panel library is extracted then much of the existing minibuffer code in exwm-workspace.el can "hook" in to this library.

Comparison with statusbar and other libraries

Status bar creates a posframe and places it adjacent to the exwm systemtray. This is a hack. (cc @dakra) My Emacs Lisp libraries above, creates a dedicated EXWM panel (I use the word panel more as a GUI element rather than as Free-desktop specific panel). Instead of relying on any of the existing global variables like global-mode-string or mode-line-misc-info, it actually creates a exwm-panel-string (which follows the same /format/ as a typical mode-line string) and leaves it at that. In a sense, the exwm-panel-string is /more/ global than the global stuff in mode line. In the panel string--think of it as mode line for EXWM desktop--I have included launchers as well as workspace indicators i.e., the EXWM panel string is as "configurable" and "extensible" as the Emacs mode line. It can also placed in a mode line, a tool bar, a posframe or a first-class EXWM panel.

My thought process as I worked on creating the above libraries

At the outset the question I had was "How can I create a frame that I can control with EXWM? And how do I "embed" text string in it?". To answer this question, I have looked at system tray, and learnt a bit about how it "embeds" icon. Systemtray has it's own protocol for embedding icons and text messages (aka notification bubbles). EXWM's Systemtray doesn't support bubbles, and I felt it was too heavyweight for the task at hand. While looking for X Window specific ways for "embedding" text / icons, I ended up with XCB-specific "recipes" for working "graphical context" and how text and drawing elements can be inserted in to an X window. (See https://xcb.freedesktop.org/tutorial/fonts/ and https://xcb.freedesktop.org/tutorial/basicwindowsanddrawing/)

Suggestions

Maintainers believe that EXWM is more of an infrastructure code, rather than a GUI fluff. Then it will be worthwhile providing recipes for

  1. how a Emacs frame can be enclosed within a EXWM container and controlled--show, hide, resize--using xcb messages.
  2. how to use "graphical context" to embed text strings or other obects using XCB library.
ch11ng commented 5 years ago

@emacksnotes I think it'd be much easier for such panel to interact/communicate with rather than controlled by EXWM. It could run in a dedicated Emacs instance so EXWM can treat it as a conventional X window and won't freeze (the downside is you cannot invoke commands directly). It can even work with other WMs when necessary. All it has to do is to comply with X window manager protocols (ICCCM/EWMH). Of course It should maintain its own X connection(s) and handle for events targeting its own X windows / frames.

Creating graphical elements with XCB is way more challenging as you'll encounter problems like i18n which can be too difficult to resolve. The modern way is to rely on GUI toolkits. In Elisp widget can be a good candidate I suppose.

dakra commented 5 years ago

Reply to @emacksnotes because you mentioned me/statusbar.el.

First, sorry if I missed something because I have only skipped over this very long thread and only really read your last message.

You're right that statusbar.el is a hack. I think I wanted the same thing as you, in that I want to have some info always diplayed (like with the systray icons) but also only once and not repeated for each buffer in the modeline. For me that is basically all that's in my golbal-mode-string, which is the current org task, battery status and time. I also don't want to "waste" an extra line by using an external statusbar.

How statusbar.el happened: Like you @emacksnotes, I also looked at the exwm systray code and thought about creating something similar to your library now, but then thought it would cost me too much time and is too complicated compared to how important this feature is for me.

Then I toyed around with posframe and thought that could be a good candidate to create that 'statusbar' that I wanted. It's just a hack but it works OK for my use-case. I'll soon make a bigger change to not use variable watchers but that's another topic not relevant for this discussion here.

@ch11ng there are plenty of external statusbars already and I guess most of them have some kind of IPC to e.g. display a text that comes from Emacs etc. But the problem with that for me is that I don't want to waste an extra 20 pixels or so just for the statusbar which I use only for the org task text and time. So a "simple" way of writing/updating some text next to the systray icons is what I want and a small borderless childframe seemed like the easiest solution.

ch11ng commented 5 years ago

@dakra

@ch11ng there are plenty of external statusbars already and I guess most of them have some kind of IPC to e.g. display a text that comes from Emacs etc.

Of course they are. It's up to each user to decide which one to choose, or even create a new one.

But the problem with that for me is that I don't want to waste an extra 20 pixels or so just for the statusbar which I use only for the org task text and time.

One solution is to make it hide itself according to some rules. Most panels already support this.

dakra commented 5 years ago

One solution is to make it hide itself according to some rules. Most panels already support this.

But I want it always visible, just like the systray icons. I also have a bit enough screen (as I think almost everyone has) where I don't mind if the last quarter of one line of the minibuffer is used to display text that otherwise is repeated multiple times in the modeline (if you split your buffers). I don't think I'm the only one who wants to have the time displayed on the bottom right of his screen ;)

emacksnotes commented 5 years ago

In regard to workspace indicator, I feel the Emacs 27.1's tab-bar could be used.

From EXWM perspective, tab-bar has all the needed UI components--list of buttons to show all workspaces, a x-button for closing of workspaces, a +-button for adding workspace. More importantly, there is now a Emacs-wide space apart from menu-bar and tool-bar to display global information. (I use global in the sense of not being buffer or window local)

From the core Emacs perspective, the re-purposing of tab-bar to manage workspaces (as opposed to manage window configurations) would also help improve the tab-bar mode.

emacksnotes commented 5 years ago

In regard to workspace indicator, I feel the Emacs 27.1's tab-bar could be used.

A screenshot of tab-bar for those of you who haven't explored this new feature ...

Screenshot from 2019-11-30 10-06-45

ch11ng commented 5 years ago

@emacksnotes Thanks for the info! We currently make no use of window configurations but may finally switch to it from the now frame-based workspace implementation. One problem remains to be resolved though: for a multi-monitor setup we'll still need an equal number of frames, and that's where things start to get ugly.