This is intended to be a hack for emacs conf 22. Have fun ;) Don’t overengineer; wait for feedback first.
? Suitable base:
ref/ wlroots cage mahogany river
tinywl & cage use wlr_scene mahogany is split into a c backend and a cl frontend river has a seperate program that specifies layout but both don’t use the newer wlr_scene api for damage-tracking
https://www.hjdskes.nl/projects/cage/ Cage is based on the annotated source of tinywl and rootston.
-> tinywl
Writing a wayland compositor with wlroots Fork tinywl. This tiny example is shipped with wlroots and is an excellent base for a serious compositor. Although short it implements in a basic way almost every core functionality you’ll need, and implicitly teaches a lot about Wayland API interaction (especially if using the C interface to events, listeners etc.) which scales very well when branching out into other protocols. Since tinywl is within the wlroots tree it is also guaranteed to be up to date.
tinywl main walktrought: main does setup
tinywl.c: Create a scene graph. This is a wlroots abstraction that handles all rendering and damage tracking. All the compositor author needs to do is add things that should be rendered to the scene graph at the proper positions and then call wlr_scene_output_commit() to render a frame if necessary.
Afterwards main sets setenv(“WAYLAND_DISPLAY”, socket, true); forks and runs the startup_cmd in the child
In the parent the event loop is started Run the Wayland event loop. This does not return until you exit the compositor. Starting the backend rigged up all of the necessary event loop configuration to listen to libinput events, DRM events, generate frame events at the refresh rate, and so on. */
Notable omissions from TinyWL:
- HiDPI support
- Any kind of configuration, e.g. output layout
- Any protocol other than xdg-shell (e.g. layer-shell, for panels/taskbars/etc; or Xwayland, for proxied X11 windows)
- Optional protocols, e.g. screen capture, primary selection, virtual keyboard, etc. Most of these are plug-and-play with wlroots, but they’re omitted for brevity.
wlr_scene: The scene-graph API provides a declarative way to display surfaces. The compositor creates a scene, adds surfaces, then renders the scene on outputs.
The scene-graph API only supports basic 2D composition operations (like the KMS API or the Wayland protocol does). For anything more complicated, compositors need to implement custom rendering logic.
Set the position of the node relative to its parent. wlr_scene_node_set_position
guix shell wlroots wayland-protocols gcc-toolchain make pkg-config – make CC=gcc
guix shell –manifest=manifest.scm – make CC=gcc guix shell –manifest=../../../manifest.scm – make CC=gcc
[2022-10-14 Fri] Scope: Just windows for now! No input handling in emacs.
4 components:
- ews A wayland compositor (wayland-server) that wraps emacs and talks to : Server in C
- ewc a wayland-client as dynamic module that responds to the compositor. : Client in C
- ewp A wayland protocol for this communication. : Protocol in XML
- ewm A elisp library that does the window management. : Lib in ELisp
Flow: A new window/shell(surface) is created.
- Server reacts to events.new_surface -> Creates datastructures but does not show the window
- instead it sends event to client. The event contains info about the new window (title, …)
- Client translates window info to elisp
- Emacs lib manages the window. It records and positions the window
- a show requests is send to server if window is visible
- The server shows the window as requested
?
- How does emacs recieve event?
Protocol:
- Events
- new, destroy
- Requests
- show (position), destroy
other: fullscreen
open questions: how to handle embedded -> later!
Emacs is the “server” (actually a client talking to the server, X style). Emacs is the server side decoration.
An idea in design space: C server is minimal.
- Do more in elisp
- Slower and single threaded
eg. if it wants to create window it has to ask emacs first emacs than positions the window and ews draws it.
-> A client wants a new toplevel window -> Emacs creats it’s decorations frame & waits for the event & positions both inside parent (window/frame/monitor)
Emacs does moving of windows; eg move by dragging modeline
Unification
Use make-network-process over socket driven by extra process? from within emacs? (possible?) extra process?
Process can activate filter function if pending output. And can be send input. From within emacs without extra code.
OR as event eg special event; but seems hardcoded in C
but should be part of emacs event loop
-> either event or process (= run if waiting for event)
Maybe use dynamic module with wayland protocol in filter function. call wl_display_dispatch (https://wayland-book.com/wayland-display/event-loop.html) on queue which is wl_list (a linked list of events; events are maybe defined by scanner?)
eg x11: wlr/backend/x11.h
- Creates a new X11 backend. This backend will be created with no outputs;
- you must use wlr_x11_output_create() to add them.
*
- The `x11_display` argument is the name of the X Display socket. Set
- to NULL for the default behaviour of XOpenDisplay().
*/ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, const char *x11_display);
/**
- Adds a new output to this backend. You may remove outputs by destroying them.
- Note that if called before initializing the backend, this will return NULL
- and your outputs will be created during initialization (and given to you via
- the new_output signal).
*/ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend);
~/s/emacs/ (magit-rev 6c1f0dd709) child frames on pgtk are gtk widgets inside the parent surface, rather than subsurfaces (in wayland terms),
start New toplevel XDG surface app_id=emacs title=emacs@muh emacs new frame New toplevel XDG surface app_id=emacs title=emacs@muh eshell mpv New toplevel XDG surface app_id=mpv title=10_Blues_Shuffle_EASY_.mp4
—
start 00:00:00.425 [ews.c:765] New toplevel XDG surface app_id=emacs title=emacs@muh pid=15343 new frame 00:02:23.492 [ews.c:765] New toplevel XDG surface app_id=emacs title=emacs@muh pid=15343 mpv 00:03:54.271 [ews.c:765] New toplevel XDG surface app_id=mpv title=10_Blues_Shuffle_EASY_.mp4 - mpv pid=15392 second emacs 00:07:23.856 [ews.c:765] New toplevel XDG surface app_id=emacs title=emacs@muh pid=15456 new frame in second emacs 00:07:42.647 [ews.c:765] New toplevel XDG surface app_id=emacs title=emacs@muh pid=15456
Wayland only?
[2022-10-14 Fri] 3 layers
- emacs frame
- embedded windows (inside buffers)
- floating windows
floating: only position from elisp for now? usecase floating webcam for talk
[2022-10-14 Fri]
Normally give all keys to focused window. But also some way to say from emacs: Give these keys to emacs. And simulated keys over protocol. Maybe keytranslation | interception tools style?
[2022-10-20 Thu]
| Used like: | in |
| protocol interface event -get> opcode | ewc-objects-path->listener |
| protocol interface -get> event-count | ewc-objects-add |
| protocol interface opcode -get> bindat-type | ewc-parse |
| protocol interface opcode -get> bindat-type | ewc-print |
| protocol interface request -get> opcode | ewc-print |
| Used like: | in |
| id -get> protocol interface | ewc-parse |
| id opcode -get> listener | ewc-parse |
| protocol interface -get> id | ewc-print |
[2022-10-14 Fri] mpv –profile=low-latency –untimed /dev/video0
[2022-10-21 Fri]
[X] Fang mit der Präsi an, sobald grob fertig, & mach was dafür gebraucht/ mach die rund und halt sie.
- Show a little 3d thing inside emacs (a shadertoy?). -> Future
- Do it in here
- Fractal video: Introduce yourself with fullscreen video, then go fractal (C-x 3, C-x o, C-x 2 … until window to small for splitting), then show (possible) features and how to implement Or show possible features first after intro? and then reveal it works already? But I want to show possible features with my video -> Try
- Nested under X | wayland => Emacs can be a WM anywhere linuxy
-> a backend like pgtk/X/win
My design is centered around the wayland protocol and mostly inspired by it. I think it came out quite functional and flexible but maybe a bit unemacsy. What do you think about it?
I explored a wayland future for emacs a possible an other
Multiple views for same buffer is quite emacsy Do you like it?
tinywl <-> cage keep c minimal, but tinywl not enough and at same time to much compare ews with tinywl
handling of keyboard: For now all events are passed to client. In future some/all are passed to emacs instead. Maybe using own protocol.
Several views must share same height and width I think! -> Why do them then? I hacked them now to try this emacsy way. But maybe remove later? Machinery is there: wlr_scene_buffer_set_dest_size et al & its a kinda neat and unique wayland feature maybe a little useless right now.
https://gitlab.gnome.org/GNOME/gtk/-/issues/174
Uses Makefile for logic & manifest for dependencies.
wlr_xdg_toplevel_set_size toplevel->scheduled.width = width; toplevel->scheduled.height = height; return wlr_xdg_surface_schedule_configure(toplevel->base); ewp_view@7.layout(0, 0, 663, 850) > layout runs here -> xdg_toplevel@28.configure(663, 850, array[0]) -> xdg_surface@27.configure(57)
set_buffer_with_surface_state(struct wlr_scene_buffer *scene_buffer, struct wlr_surface *surface) wlr_surface_state *state = &surface->current; … wlr_scene_buffer_set_dest_size(scene_buffer, state->width, state->height); wlr_scene_buffer_set_transform(scene_buffer, state->transform); …
used in handle_scene_surface_surface_commit wlr_scene_surface_create (~used in wlr_scene_subsurface_tree_create ~and wlr_scene_xdg_surface_create) wlr_surface->events.commit = handle_scene_surface_surface_commit
I use wlr_scene_xdg_surface_create view->scene_tree = wlr_scene_xdg_surface_create(&surface->server->scene->tree
which does: scene_xdg_surface->xdg_surface_commit.notify = scene_xdg_surface_handle_xdg_surface_commit; wl_signal_add(&xdg_surface->surface->events.commit, &scene_xdg_surface->xdg_surface_commit);
scene_xdg_surface_update_position(scene_xdg_surface);
=> scene_xdg_surface_update_position & handle_scene_surface_surface_commit are on same signal
wlr_xdg_surface -> wlr_surface -> wl_signal commit & wl_signal has a listener_list
https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3458 wlr_scene_xdg_surface scene tree for wlr_scene_xdg_surface wlr_scene_subsurface_tree scene tree for wlr_scene_subsurface_tree wlr_scene_surface wlr_scene_buffer
Inspiration from wlr_data_offer.c See: wl_data_device_send_data_offer
client = wl_resource_get_client(device_resource)
offer->resource = wl_resource_create(client, &wl_data_offer_interface, version, 0);
wl_resource_set_implementation(offer->resource, &data_offer_impl, offer,
data_offer_handle_resource_destroy);
wl_data_device_send_data_offer(device_resource, offer->resource);exwm–id-buffer-alist Alist of (<X window ID> . <Emacs buffer>).
exwm-workspace–workareas -> ([0 0 1920 1200]) for one output
exwm-manage–manage-window
exwm-layout–show Show window ID exactly fit in the Emacs window WINDOW.
exwm-workspace–set-fullscreen
layout (add-hook ‘window-configuration-change-hook #’exwm-layout–refresh) (add-hook ‘window-size-change-functions #’exwm-layout–refresh)) (add-hook ‘minibuffer-setup-hook #’exwm-layout–on-minibuffer-setup t) (add-hook ‘echo-area-clear-hook #’exwm-layout–on-echo-area-change)))
input (add-hook ‘pre-command-hook #’exwm-input–on-pre-command) (add-hook ‘post-command-hook #’exwm-input–on-post-command) (add-hook ‘minibuffer-setup-hook #’exwm-input–on-minibuffer-setup) (add-hook ‘minibuffer-exit-hook #’exwm-input–on-minibuffer-exit) (add-hook ‘echo-area-clear-hook #’exwm-input–on-echo-area-clear) (add-hook ‘buffer-list-update-hook #’exwm-input–on-buffer-list-update))
frames (add-hook ‘after-make-frame-functions #’exwm-manage–add-frame) (add-hook ‘delete-frame-functions #’exwm-manage–remove-frame)
init (setq frame-resize-pixelwise t ;mandatory; before init window-resize-pixelwise t) ;; Ignore unrecognized command line arguments. This can be helpful ;; when EXWM is launched by some session manager. (push #’vector command-line-functions) ;; In case EXWM is to be started from a graphical Emacs instance. (add-hook ‘window-setup-hook #’exwm-init t) ;; In case EXWM is to be started with emacsclient. (add-hook ‘after-make-frame-functions #’exwm-init t) ;; Manage the subordinate Emacs server. (add-hook ‘kill-emacs-hook #’exwm–server-stop) (dolist (i exwm-blocking-subrs) (advice-add i :around #’exwm–server-eval-at)
;; The change functions run are, in this order:
(add-hook ‘window-buffer-change-functions (lambda (&optional x) (message “window-buffer-change-functions %s” x)) nil t) (add-hook ‘window-size-change-functions (lambda (&optional x) (message “window-size-change-functions %s” x)) nil t) (add-hook ‘window-selected-change-functions (lambda (&optional x) (message “window-selected-change-functions %s” x)) nil t) (add-hook ‘window-state-change-functions (lambda (&optional x) (message “window-state-change-functions %s” x)) nil t) (add-hook ‘window-configuration-change-hook (lambda (&optional x) (message “window-configuration-change-hook %s” x)) nil t) (add-hook ‘window-state-change-hook (lambda (&optional x) (message “window-state-change-hook %s” x)) nil t)
(add-hook ‘window-configuration-change-hook (lambda () (message “local @ hook.el”)) nil t)
;; new strategy: ;; window-buffer-change-functions -> after window buffer change: ;; window was created, deleted or assigned another buffer. ;; window-size-change-functions -> after window size change: ;; window was created, assigned another buffer or changed its total or text area size (add-hook ‘window-buffer-change-functions (lambda (&optional x) (message “window-buffer-change-functions %s” x)) nil t) (add-hook ‘window-size-change-functions (lambda (&optional x) (message “window-size-change-functions %s” x)) nil t) (add-hook ‘window-size-change-functions (lambda (&optional x) (message “global size-changes”)) )
(current-window)
(add-hook ‘window-selection-change-functions (lambda (&optional x) (message “%s selected? %s” x (selected-window))) nil t)
[2022-10-20 Thu]
OBJECTS
interface -> object ;; the interface is the blueprint; it becomes alive as an object
(object) -> data (setf (object) data) | (object data)
(ewc-protocol xml . interface) -> (protocol (interface version ((name ue . listener) …) ((name opcode le . pe) …)) …) ; compiled ; only listener is rw
;; A listener is an event callback. (setf (ewc-listener protocol interface event) listener)
(ewc-object protocol interface) -> #s(ewc-object protocol interface id ; add new object to ewc-objects -> id data ; only rw field ((name ue . listener) …) ; shared ; set by name ((name opcode le . pe) …) ; shared ; looked up by name )
(ewc-request object request . (named args)) -> id & opcode & pe -> msg
(ewc-event str) -> id & opcode -> lookup object -> ue & listener -> (listener object msg)
Seems sound: short & concise :)
[2023-02-06 Mon]
[2023-02-06 Mon]
Start with keyboard & keep mouse simple for now (?)
How does ews handle input? ews does input focus in focus_surface: wlr_seat_keyboard_notify_enter makes wlroot send key events to the appropriate client (without additional work on your part)
There are also listeners for keyboard events:
- keyboard_handle_modifiers Raised when a modifier key, such as shift or alt, pressed. wlr_seat_keyboard_notify_modifiers communicates this to the client. (wlr_keyboard->modifiers)
- keyboard_handle_key -> wlr_keyboard_key_event -> keycode Raised when a key is pressed or released. wlr_seat_keyboard_notify_key communicates this to the client.
They seem more appropriate as interception point.
possible routes
- Emacs -ewp> ews Emacs sends keycode & modifiers to ews_surface While receiving all modifiers itself Seems simple but could add delays (That is passing every key through Elisp) Powerfull at same time. = Full input control form Emacs. ? How chatty is wayland with keyevents? wl_keyboard sends key event XXXX
- ews runs in direct mode = sends events directly to surface => Main Emacs is normally in direct mode
- hybrid: ews sends some events/ modifiers to the selected surface & intersects some to emacs (& emacs can then send them to the surface again) <> Keymap in ews
1 only seems simplest.
Yes but would need another mechanism than 1.
Sketch: Emacs says: Focus this but intercept it like this:
(SPACE & other) -> (RIGHTCTRL & other) = modifier & other (SPACE) -> (SPACE) = default
How to do protocol? How to do in C?
~ Emacs sends list of keysyms with possible “wildcards” with replacment list. This could implement simulation keys as well
Keymap in emacs with function that sends key to buffer surface.
Start with 1 Direct mode will be special case (maybe for games)
Also text-input-unstable-v3.xml in wayland-protocols
Input to textboxes? Similar to android input handling (that is comes from mobile usecases?)
Client gets inhibitor and than receives all keypresses. Meant for lockscreens. ~ solution 1
But means normal input handling still needs to be implemented in ews