Skip to content

Latest commit

 

History

History
583 lines (448 loc) · 20.6 KB

File metadata and controls

583 lines (448 loc) · 20.6 KB

This is intended to be a hack for emacs conf 22. Have fun ;) Don’t overengineer; wait for feedback first.

Experimentar -> Demo?

? 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

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

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

[X] Build tinywl (-> ewl?)

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

[X] 1st protoype

[2022-10-14 Fri] Scope: Just windows for now! No input handling in emacs.

4 components:

  1. ews A wayland compositor (wayland-server) that wraps emacs and talks to : Server in C
  2. ewc a wayland-client as dynamic module that responds to the compositor. : Client in C
  3. ewp A wayland protocol for this communication. : Protocol in XML
  4. 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!

Drive from emacs

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

Make emacs react to event:

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?)

Nested uses output_layout with several output=windows

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);

child frames are popup surfaces in wayland

~/s/emacs/ (magit-rev 6c1f0dd709) child frames on pgtk are gtk widgets inside the parent surface, rather than subsurfaces (in wayland terms),

LOG

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?

Layers

[2022-10-14 Fri] 3 layers

  1. emacs frame
  2. embedded windows (inside buffers)
  3. floating windows

floating: only position from elisp for now? usecase floating webcam for talk

Keyboard

[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?

Implementation

[2022-10-20 Thu]

ewc-protocols

Used like:in
protocol interface event -get> opcodeewc-objects-path->listener
protocol interface -get> event-countewc-objects-add
protocol interface opcode -get> bindat-typeewc-parse
protocol interface opcode -get> bindat-typeewc-print
protocol interface request -get> opcodeewc-print

ewc-objects

Used like:in
id -get> protocol interfaceewc-parse
id opcode -get> listenerewc-parse
protocol interface -get> idewc-print

Webcam & mpv

[2022-10-14 Fri] mpv –profile=low-latency –untimed /dev/video0

OK it’s here :) emacs can wayland now

[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.

Killing ewc kills emacs too -> Thanks GTK again for making this not flexible but brittle

https://gitlab.gnome.org/GNOME/gtk/-/issues/174

make: guix shell –manifest=manifest.scm – make check

Uses Makefile for logic & manifest for dependencies.

Multiple views

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

creating an xdg shell through the helper results in the following allocations:

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

How to implement ewp-surface

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);

Some pointers from EXWM

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

The hooks used by EXWM

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)

Window change hooks

;; 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)

ewc interface -> object scetch

[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 :)

Up and converenced => NEXT Replace EXWM for me

[2023-02-06 Mon]

[ ] NEXT UP Input handling

[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

  1. 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
  2. ews runs in direct mode = sends events directly to surface => Main Emacs is normally in direct mode
  3. 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.

Can interception tools be done in ewx?

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

Simulation keys idea

Keymap in emacs with function that sends key to buffer surface.

[ ] Sketch & decide what to implement (first)

Start with 1 Direct mode will be special case (maybe for games)

Look at other protocols

wlroots input-method-unstable-v2.xml

Also text-input-unstable-v3.xml in wayland-protocols

Input to textboxes? Similar to android input handling (that is comes from mobile usecases?)

wlr-input-inhibitor-unstable-v1.xml

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