From c87fa2d4af7f68e12389e405b5d5f12e4f23d47a Mon Sep 17 00:00:00 2001 From: Ben Buhse Date: Tue, 6 May 2025 21:47:22 -0500 Subject: [PATCH] Begin setting up infrastructure for the WM Mostly just created the very basic Wayland connection needed, but I also bind to the rwm protocol which is neat. --- LICENSES/ISC.txt | 8 + build.zig | 10 +- examples/init | 5 + protocol/river-window-management-v1.xml | 1690 +++++++++++++++++++++++ src/Backend.zig | 112 ++ src/event_loop.zig | 9 + src/main.zig | 31 +- 7 files changed, 1858 insertions(+), 7 deletions(-) create mode 100644 LICENSES/ISC.txt create mode 100755 examples/init create mode 100644 protocol/river-window-management-v1.xml create mode 100644 src/Backend.zig create mode 100644 src/event_loop.zig diff --git a/LICENSES/ISC.txt b/LICENSES/ISC.txt new file mode 100644 index 0000000..b9c199c --- /dev/null +++ b/LICENSES/ISC.txt @@ -0,0 +1,8 @@ +ISC License: + +Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC") +Copyright (c) 1995-2003 by Internet Software Consortium + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/build.zig b/build.zig index 00e3a33..7f8051a 100644 --- a/build.zig +++ b/build.zig @@ -5,9 +5,6 @@ const std = @import("std"); const Scanner = @import("wayland").Scanner; -// Although this function looks imperative, note that its job is to -// declaratively construct a build graph that will be executed by an external -// runner. pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); @@ -19,6 +16,13 @@ pub fn build(b: *std.Build) void { const scanner = Scanner.create(b, .{}); const wayland = b.createModule(.{ .root_source_file = scanner.result }); + scanner.addCustomProtocol(b.path("protocol/river-window-management-v1.xml")); + + scanner.generate("wl_compositor", 4); + scanner.generate("wl_shm", 1); + scanner.generate("wl_output", 4); + scanner.generate("river_window_manager_v1", 1); + const exe = b.addExecutable(.{ .name = "beansprout", .root_source_file = b.path("src/main.zig"), diff --git a/examples/init b/examples/init new file mode 100755 index 0000000..b76ba71 --- /dev/null +++ b/examples/init @@ -0,0 +1,5 @@ +#! /bin/sh + +./zig-out/bin/beansprout & + +/usr/bin/foot & diff --git a/protocol/river-window-management-v1.xml b/protocol/river-window-management-v1.xml new file mode 100644 index 0000000..b5cdbad --- /dev/null +++ b/protocol/river-window-management-v1.xml @@ -0,0 +1,1690 @@ + + + + + + + Copyright 2024 The River Developers + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + + + This protocol allows a single "window manager" client to determine the + window management policy of the compositor. State is globally + double-buffered allowing for frame perfect state changes involving multiple + windows. + + The key words "must", "must not", "required", "shall", "shall not", + "should", "should not", "recommended", "may", and "optional" in this + document are to be interpreted as described in IETF RFC 2119. + + Warning! The protocol described in this file is currently in the testing + phase. Backward compatible changes may be added together with the + corresponding interface version bump. Backward incompatible changes can only + be done by creating a new major version of the extension. + + + + + This global interface should only be advertised to the window manager + process. Only one window management client may be active at a time. The + compositor should use the unavailable event if necessary to enforce this. + + There are two disjoint categories of state managed by this protocol: + + Windowing state influences the communication between the server and + individual window clients (e.g. xdg_toplevels). Windowing state includes + window dimensions, fullscreen state, keyboard focus, keyboard bindings, + and more. + + Rendering state only affects the rendered output of the compositor and + does not influence communication between the server and individual window + clients. Rendering state includes the position and rendering order of + windows, shell surfaces, decoration surfaces, borders, window cropping, + and more. + + Windowing state may only be modified by the window manager as part of a + windowing update sequence. A windowing update sequence is started with the + update_windowing_start event and ended with the update_windowing_finish + request. It is a protocol error to modify window state outside of a + windowing update sequence. + + A windowing update sequence is always followed by at least one rendering + update sequence. A rendering update sequence is started with the + update_rendering_start event and ended with the update + update_rendering_finish request. + + Rendering state may be modified by the window manager during a windowing + update sequence or a rendering update sequence. Regardless of when the + rendering state is modified, it is applied with the next + update_rendering_finish request. It is a protocol error to modify + rendering state outside of a windowing or rendering update sequence. + + The server will start a windowing update sequence by sending new state and + the update_windowing_start event as soon as possible whenever there is a + change in state that must be communicated with the window manager. + + If the window manager client needs to ensure a windowing update sequence + is started due to a state change the compositor is not aware of, it may + send the update_windowing_dirty request. + + The server will start a rendering update sequence by sending new state and + the update_rendering_start event as soon as possible whenever there is a + change in window dimensions or position that must be communicated with the + window manager. Multiple rendering update sequences may be made + consecutively without a windowing update sequence in between, for example + during interactive move/resize of windows or if a window independently + changes its own dimensions. + + To summarize the main loop of this protocol is as follows: + + 1. The server sends events indicating all changes since the last + windowing update followed by the update_windowing_start event. + + 2. The client sends requests modifying windowing state or rendering state + (as defined above) followed by the update_windowing_finish request. + + 3. The server sends new state to windows and waits for responses. + + 4. The server sends new window position and/or dimensions to the client + followed by the update_rendering_start event. + + 5. The client sends requests modifying rendering state (as defined above) + followed by the update_rendering_finish request. + + 6. If window dimensions/positions change, loop back to step 4. + If state that requires a windowing update sequence changes or if the + client makes an update_windowing_dirty request, loop back to step 1. + + For the purposes of frame perfection, the server may delay rendering new + state committed by the windows in step 3 until after step 5 is finished. + + It is a protocol error for the client to make an update_windowing_finish + or update_rendering_finish request that violates this ordering. + + + + + + + + + + This event indicates that window management is not available to the + client, perhaps due to another window management client already + running. The circumstances causing this event to be sent are compositor + policy. + + If sent, this event is guaranteed to be the first and only event sent by + the server. + + The server will send no further events on this object. The client should + destroy this object and all objects created through this interface. + + + + + + This request indicates that the client no longer wishes to receive + events on this object. + + The Wayland protocol is asynchronous, which means the server may send + further events until the stop request is processed. The client must wait + for a river_window_manager_v1.finished event before destroying this + object. + + + + + + This event indicates that the server will send no further events on this + object. The client should destroy the object. See + river_window_manager_v1.destroy for more information. + + + + + + This request should be called after the finished event has been received + to complete destruction of the object. + + If a client wishes to destroy this object it should send a + river_window_manager_v1.stop request and wait for a + river_window_manager_v1.finished event. Once the finished event is + received it is safe to destroy this object and any other objects created + through this interface. + + + + + + This event indicates that the server has sent events indicating all + state changes since the last windowing update sequence. + + In response to this event, the client should make requests modifying + windowing state as it chooses. Then, the client must make the + update_windowing_finish request. + + See the description of the river_window_manager_v1 interface for a + complete overview of the update sequence loop. + + + + + + This request indicates that the client has made all changes to windowing + state it wishes to include in the current update sequence and that the + server should atomically send these state changes to the windows and + continue with the update sequence. + + After sending this request, it is a protocol error for the client to + make further changes to windowing state until the next + update_windowing_start event is received. + + See the description of the river_window_manager_v1 interface for a + complete overview of the update sequence loop. + + + + + + This request ensures a windowing update sequence is started and that an + update_windowing_start event is sent by the server. If this request is + made during an ongoing update sequence, a new update sequence will be + started as soon as the current one is completed. + + The client may want to use this request due to an internal state change + that the compositor is not aware of (e.g. a dbus event) which should + affect windowing or rendering state. + + + + + + This event indicates that the server has sent all river_node_v1.position + and river_window_v1.dimensions events necessary. + + In response to this event, the client should make requests modifying + rendering state as it chooses. Then, the client must make the + update_rendering_finish request. + + See the description of the river_window_manager_v1 interface for a + complete overview of the update sequence loop. + + + + + + This request indicates that the client has made all changes to rendering + state it wishes to include in the current update sequence and that the + server should atomically apply and display these state changes to the + user. + + After sending this request, it is a protocol error for the client to + make further changes to rendering state until the next + update_windowing_start or update_rendering_start event is received, + whichever comes first. + + See the description of the river_window_manager_v1 interface for a + complete overview of the update sequence loop. + + + + + + This event indicates that the session has been locked. + + The window manager may wish to restrict which key bindings are available + while locked or otherwise use this information. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + This event indicates that the session has been unlocked. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + A new window has been created. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + A new logical output has been created, perhaps due to a new physical + monitor being plugged in or perhaps due to a change in configuration. + + This event will be followed by river_output_v1.position and dimensions + events as well as an update_windowing_start event after all other new + state has been sent by the server. + + + + + + + A new seat has been created. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + Create a new shell surface for window manager UI and assign the + river_shell_surface_v1 role to the surface. + + Providing a wl_surface which already has a role or already has a buffer + attached or committed is a protocol error. + + + + + + + + + This represents a logical window. For example, a window may correspond to + an xdg_toplevel or Xwayland window. + + A newly created window will not be displayed until the window manager + proposes window dimensions with the propose_dimensions request as part of + a windowing update sequence, the server replies with a dimensions event as + part of a rendering update sequence, and that rendering update sequence is + finished. + + TODO: + - window cropping + + + + + + + + + This request indicates that the client will no longer use the window + object and that it may be safely destroyed. + + This request should be made after the river_window_v1.closed event or + river_window_manager_v1.finished is received to complete destruction of + the window. + + + + + + The window has been closed by the server, perhaps due to an + xdg_toplevel.close request or similar. + + The server will send no further events on this object and ignore any + request other than river_window_v1.destroy made after this event is + sent. The client should destroy this object with the + river_window_v1.destroy request to free up resources. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + Request that the window be closed. The window may ignore this request or + only close after some delay, perhaps opening a dialog asking the user to + save their work or similar. + + The server will send a river_window_v1.closed event if/when the window + has been closed. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + Get the node in the render list corresponding to the window. + + It is a protocol error to make this request more than once for a single + window. + + + + + + + This event informs the window manager of the window's preferred min/max + dimensions. These preferences are a hint, and the window manager is free + to propose dimensions outside of these bounds. + + All min/max width/height values must be strictly greater than or equal + to 0. A value of 0 indicates that the window has no preference for that + value. + + The min_width/min_height must be strictly less than or equal to the + max_width/max_height. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + + + + This event indicates the dimensions of the window in the compositor's + logical coordinate space. The width and height must be strictly greater + than zero. + + Note that the dimensions of a river_window_v1 refer to the dimensions of + the window content and are unaffected by the presence of borders or + decoration surfaces. + + This event is sent as part of a rendering update sequence before the + update_rendering_start event. + + It may be sent due to a propose_dimensions request in a previous + windowing update sequence or because a window independently decides to + change its dimensions. + + + + + + + + This request proposes dimensions for the window in the compositor's + logical coordinate space. + + The width and height must be greater than or equal to zero. If the width + or height is zero the window will be allowed to decide its own + dimensions. + + The window may not take the exact dimensions proposed. The actual + dimensions taken by the window will be sent in a subsequent + river_window_v1.dimensions event. For example, a terminal emulator may + only allow dimensions that are multiple of the cell size. + + When a propose_dimensions request is made, the server must send a + dimensions event in response as soon as possible. It may not be possible + to send a dimensions event in the very next rendering update sequence + if, for example, the window takes too long to respond to the first + proposed dimensions. In this case, the server will send the dimensions + event in a future rendering update sequence. The window will not be + displayed until the first dimensions event is received and the rendering + update sequence is finished. + + Note that the dimensions of a river_window_v1 refer to the dimensions of + the window content and are unaffected by the presence of borders or + decoration surfaces. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + + + Request that the window be hidden. Has no effect if the window is already + hidden. Hides any window borders and decorations as well. + + Newly created windows are considered shown unless explicitly hidden with + the hide request. + + This request modifies rendering state and may only be made as part of an + update sequence, see the river_window_manager_v1 description. + + + + + + Request that the window be shown. Has no effect if the window is not + hidden. Does not guarantee that the window is visible as it may be + completely obscured by other windows placed above it for example. + + Newly created windows are considered shown unless explicitly hidden with + the hide request. + + This request modifies rendering state and may only be made as part of an + update sequence, see the river_window_manager_v1 description. + + + + + + The window set an application ID. + + The app_id argument will be null if the window has never set an + application ID or if the window cleared its application ID. (Xwayland + windows may do this for example, though xdg-toplevels may not.) + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + The window set a title. + + The title argument will be null if the window has never set a title or + if the window cleared its title. (Xwayland windows may do this for + example, though xdg-toplevels may not.) + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + The window set a parent window. If this event is never received or if + the parent argument is null then the window has no parent. + + A surface with a parent set might be a dialog, file picker, or similar + for the parent window. + + Child windows should generally be rendered directly above their parent. + + The compositor must guarantee that there are no loops in the window + tree: a parent must not be the descendant of one of its children. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + + + + + + + + Information from the window about the supported and preferred client + side/server side decoration options. + + This event may be sent multiple times over the lifetime of the window if + the window changes its preferences. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + Tell the client to use client side decoration and draw its own title + bar, borders, etc. + + This is the default if neither this request nor the use_ssd request is + ever made. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + Tell the client to use server side decoration and not draw any client + side decorations. + + This request will have no effect if the client only supports client side + decoration, see the decoration_hint event. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + + + + + + + + + This request decorates the window with borders drawn by the compositor + on the specified edges of the window. Borders are drawn above the window + content. + + Corners are drawn only between borders on adjacent edges. If e.g. the + left edge has a border and the top edge does not, the border drawn on + the left edge will not extend vertically beyond the top edge of the + window. + + The color is defined by four 32-bit RGBA values. Unless specified in + another protocol extension, the RGBA values use pre-multiplied alpha. + + Setting the edges to none or the width to 0 disables the borders. + Setting a negative width is a protocol error. + + This request completely overrides all previous set_borders requests. + Only the most recent set_borders request has an effect. + + Note that the position/dimensions of a river_window_v1 refer to the + position/dimensions of the window content and are unaffected by the + presence of borders or decoration surfaces. + + This request modifies rendering state and may only be made as part of an + update sequence, see the river_window_manager_v1 description. + + + + + + + + + + + + Inform the window that it is part of a tiled layout and adjacent to + other elements in the tiled layout on the given edges. + + The window should use this information to change the style of its client + side decorations and avoid drawing e.g. drop shadows outside of the + window dimensions on the tiled edges. + + Setting the edges argument to none informs the window that it is not + part of a tiled layout. If this request is never made, the window is + informed that it is not part of a tiled layout. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + + Create a window decoration surface above the window and assign the + river_decoration_v1 role to the surface. + + Providing a wl_surface which already has a role or already has a buffer + attached or committed is a protocol error. + + + + + + + + Create a window decoration surface below the window and assign the + river_decoration_v1 role to the surface. + + Providing a wl_surface which already has a role or already has a buffer + attached or committed is a protocol error. + + + + + + + + This event informs the window manager that the window has requested to + be interactively moved using the pointer. The seat argument indicates + the seat for the move and the serial argument identifies the input event + (e.g. pointer button press or touch) that started the move. + + The xdg-shell protocol for example allows windows to request that an + interactive move be started, perhaps when a client-side rendered + titlebar is dragged. + + The window manager may use the river_seat_v1.op_start_serial request to + interactively move the window or ignore this event entirely. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + + This event informs the window manager that the window has requested to + be interactively resized using the pointer. The seat argument indicates + the seat for the resize and the serial argument identifies the input + event (e.g. pointer button press or touch) that started the resize. + + The edges argument indicates which edges the window has requested to be + resized from. The edges argument will never be none and will never have + both top and bottom or both left and right edges set. + + The xdg-shell protocol for example allows windows to request that an + interactive resize be started, perhaps when the corner of client-side + rendered decorations is dragged. + + The window manager may use the river_seat_v1.op_start_serial request to + interactively resize the window or ignore this event entirely. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + + + Inform the window that it is being resized. The window manager should + use this request to inform windows that are the target of an interactive + resize for example. + + The window manager remains responsible for handling the position and + dimensions of the window while it is resizing. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + Inform the window that it is no longer being resized. The window manager + should use this request to inform windows that are the target of an + interactive resize that the interactive resize has ended for example. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + + + + + + + + This request informs the window of the capabilities supported by the + window manager. If the window manager, for example, ignores requests + to be maximized from the window it should not tell the window that it + supports the maximize capability. + + The window might use this information to, for example, only show a + maximize button if the window manager supports the maximize capability. + + The window manager client should use this request to set capabilities + for all new windows. If this request is never made, the compositor will + inform windows that all capabilities are supported. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + + The xdg-shell protocol for example allows windows to request that a + window menu be shown, for example when the user right clicks on client + side window decorations. + + A window menu might include options to maximize or minimize the window. + + The window manager is free to ignore this request and decide what the + window menu contains if it does choose to show one. + + The x and y arguments indicate where the window requested that the + window menu be shown. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + + The xdg-shell protocol for example allows windows to request to be + maximized. + + The window manager is free to honor this request using + river_window_v1.inform_maximize or ignore it. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + The xdg-shell protocol for example allows windows to request to be + unmaximized. + + The window manager is free to honor this request using + river_window_v1.inform_unmaximized or ignore it. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + Inform the window that it is maximized. The window might use this + information to adapt the style of its client-side window decorations for + example. + + The window manager remains responsible for handling the position and + dimensions of the window while it is maximized. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + Inform the window that it is unmaximized. The window might use this + information to adapt the style of its client-side window decorations for + example. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + The xdg-shell protocol for example allows windows to request that they + be made fullscreen and allows them to provide an output preference. + + The window manager is free to honor this request using + river_window_v1.fullscreen or ignore it. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + The xdg-shell protocol for example allows windows to request to exit + fullscreen. + + The window manager is free to honor this request using + river_window_v1.exit_fullscreen or ignore it. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + Make the window fullscreen on the given output. If multiple windows are + fullscreen on the same output at the same time only the "top" window in + rendering order shall be displayed. + + The compositor will handle the position and dimensions of the window + while it is fullscreen. The set_position and propose_dimensions requests + shall not affect the current position and dimensions of a fullscreen + window. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + + Make the window not fullscreen. + + The new, post-fullscreen position and dimensions of the window will be + determined by the most recent set_position and propose_dimensions + requests. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + The xdg-shell protocol for example allows windows to request to be + minimized. + + The window manager is free to ignore this request, hide the window, or + do whatever else it chooses. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + The rendering order of windows with decorations is follows: + + 1. Decorations created with get_decoration_below at the bottom + 2. Window content + 3. Borders configured with river_window_v1.set_borders + 4. Decorations created with get_decoration_above at the top + + The relative ordering of decoration surfaces above/below a window is + undefined by this protocol and left up to the compositor. + + + + + + + + + This request indicates that the client will no longer use the decoration + object and that it may be safely destroyed. + + + + + + This request sets the offset of the decoration surface from the top left + corner of the window. + + If this request is never sent, the x and y offsets are undefined by this + protocol and left up to the compositor. + + This request modifies rendering state and may only be made as part of an + update sequence, see the river_window_manager_v1 description. + + + + + + + + Synchronize application of the next wl_surface.commit request on the + decoration surface with rest of the state atomically applied with the + next river_window_manager_v1.update_rendering_finish request. + + The client must make a wl_surface.commit request on the decoration + surface after this request and before the update_rendering_finish + request, failure to do so is a protocol error. + + This request modifies rendering state and may only be made as part of an + update sequence, see the river_window_manager_v1 description. + + + + + + + The window manager might use a shell surface to display a status bar, + background image, desktop notifications, launcher, desktop menu, or + whatever else it wants. + + + + + + + + + + This request indicates that the client will no longer use the shell + surface object and that it may be safely destroyed. + + + + + + Get the node in the render list corresponding to the shell surface. + + It is a protocol error to make this request more than once for a single + shell surface. + + + + + + + Synchronize application of the next wl_surface.commit request on the + shell surface with rest of the rendering state atomically applied with + the next river_window_manager_v1.update_rendering_finish request. + + The client must make a wl_surface.commit request on the shell surface + after this request and before the update_rendering_finish request, + failure to do so is a protocol error. + + This request modifies rendering state and may only be made as part of an + update sequence, see the river_window_manager_v1 description. + + + + + + + The render list is a list of nodes that determines the rendering order of + the compositor. Nodes may correspond to windows or shell surfaces. The + relative ordering of nodes may be changed with the place_above and + place_below requests, changing the rendering order. + + The initial position of a node in the render list is undefined, the window + manager client must use the place_above or place_below request to + guarantee a specific rendering order. + + + + + This request indicates that the client will no longer use the node + object and that it may be safely destroyed. + + + + + + Set the absolute position of the node in the compositor's logical + coordinate space. The x and y coordinates may be positive or negative. + + Note that the position of a river_window_v1 refers to the position of + the window content and is unaffected by the presence of borders or + decoration surfaces. + + If this request is never sent, the position of the node is undefined by + this protocol and left up to the compositor. + + This request modifies rendering state and may only be made as part of an + update sequence, see the river_window_manager_v1 description. + + + + + + + + This request places the node above all other nodes in the compositor's + render list. + + This request modifies rendering state and may only be made as part of an + update sequence, see the river_window_manager_v1 description. + + + + + + This request places the node below all other nodes in the compositor's + render list. + + This request modifies rendering state and may only be made as part of an + update sequence, see the river_window_manager_v1 description. + + + + + + This request places the node directly above another node in the + compositor's render list. + + Attempting to place a node above itself has no effect. + + This request modifies rendering state and may only be made as part of an + update sequence, see the river_window_manager_v1 description. + + + + + + + This request places the node directly below another node in the + compositor's render list. + + Attempting to place a node below itself has no effect. + + This request modifies rendering state and may only be made as part of an + update sequence, see the river_window_manager_v1 description. + + + + + + + + An area in the compositor's logical coordinate space that should be + treated as a single output for window management purposes. This area may + correspond to a single physical output or multiple physical outputs in the + case of mirroring or tiled monitors depending on the hardware and + compositor configuration. + + + + + This request indicates that the client will no longer use the output + object and that it may be safely destroyed. + + This request should be made after the river_output_v1.removed event is + received to complete destruction of the output. + + + + + + This event indicates that the logical output is no longer conceptually + part of window management space. + + The server will send no further events on this object and ignore any + request (other than river_output_v1.destroy) made after this event is + sent. The client should destroy this object with the + river_output_v1.destroy request to free up resources. + + This event may be sent because a corresponding physical output has been + physically unplugged or because some output configuration has changed. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + This event indicates the position of the output in the compositor's + logical coordinate space. The x and y coordinates may be positive or + negative. + + This event is sent once when the river_output_v1 is created and again + whenever the position changes. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + The server must guarantee that the position and dimensions events do not + cause the areas of multiple logical outputs to overlap when the + corresponding update_windowing_start event is received. + + + + + + + + This event indicates the dimensions of the output in the compositor's + logical coordinate space. The width and height will always be strictly + greater than zero. + + This event is sent once when the river_output_v1 is created and again + whenever the dimensions change. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + The server must guarantee that the position and dimensions events do not + cause the areas of multiple logical outputs to overlap when the + corresponding update_windowing_start event is received. + + + + + + + + + This object represents a single user's collection of input devices. It + allows the window manager to route keyboard input to windows, get + high-level information about pointer input, define keyboard and pointer + bindings, etc. + + TODO: + - touch input + - tablet input + + + + + This request indicates that the client will no longer use the seat + object and that it may be safely destroyed. + + This request should be made after the river_seat_v1.removed event is + received to complete destruction of the seat. + + + + + + This event indicates that seat is no longer in use and should be + destroyed. + + The server will send no further events on this object and ignore any + request (other than river_seat_v1.destroy) made after this event is sent. + The client should destroy this object with the river_seat_v1.destroy + request to free up resources. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + Request that the compositor send keyboard input to the given window. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + + Request that the compositor send keyboard input to the given shell + surface. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + + Request that the compositor not send keyboard input to any client. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + The seat's pointer entered the given window's area. + + The area of a window is defined to include the area defined by the + window dimensions, borders configured using river_window_v1.set_borders, + and the input regions of decoration surfaces. In particular, it does not + include input regions of surfaces belonging to the window that extend + outside the window dimensions. + + The pointer of a seat may only enter a single window at a time. When the + pointer moves between windows, the pointer_leave event for the old + window must be sent before the pointer_enter event for the new window. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + The seat's pointer left the given window. See pointer_enter for details. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + The seat's pointer was moved. + + Rationale: The motivating window manager feature for this event is the + "always" style of focus-follows-cursor. Carrying out a full update + sequence on every single pointer motion event is noisy, wasteful, and + unnecessary. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + A window has been interacted with beyond the pointer merely passing over + it. This event might be sent due to a pointer button press or due to a + touch/tablet tool interaction with the window. + + There are no guarantees regarding how this event is sent in relation to + the pointer_enter and pointer_leave events as the interaction may use + touch or tablet tool input. + + Rationale: this event gives window managers necessary information to + determine when to send keyboard focus, raise a window that already has + keyboard focus, etc. Rather than expose all pointer, touch, and tablet + events to window managers, a policy over mechanism approach is taken. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + A shell surface has been interacted with beyond the pointer merely + passing over it. This event might be sent due to a pointer button press + or due to a touch/tablet tool interaction with the shell_surface. + + There are no guarantees regarding how this event is sent in relation to + the pointer_enter and pointer_leave events as the interaction may use + touch or tablet tool input. + + Rationale: While the shell surface does receive all wl_pointer, + wl_touch, etc. input events for the surface directly, these events do + not necessarily trigger a windowing update sequence and therefore do not + allow the window manager to update focus or perform other actions in + response to the input in a race-free way. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + + + + + + Start an interactive seat operation with a serial from either the + river_window_v1.move_requested or river_window_v1.resize_requested + event. + + During the operation, op_delta events will be sent based on input + corresponding to the provided serial (e.g. pointer or touch input). + + The window manager may use this operation to implement interactive + move/resize of windows by setting the position of windows and proposing + dimensions based off of the op_delta events. + + The operation continues until the pointer button, touch point or similar + corresponding to the given serial is released or the op_end request is + made and applied during a windowing update sequence. + + This request is ignored if an operation is already in progress for a + given river_seat_v1. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + + Start an interactive pointer operation. During the operation, op_delta + events will be sent based on pointer input. + + The window manager may use this operation to implement interactive + move/resize of windows by setting the position of windows and proposing + dimensions based off of the op_delta events. + + The pointer operation continues until the op_end request is made during + a windowing update sequence and that update sequence is finished. + This request is ignored if an operation is already in progress. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + This event indicates the total change in position since the start of the + operation of the pointer/touch point/etc. + + + + + + + + End an interactive operation. + + This request is ignored if there is no operation in progress. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + This request tells the compositor to confine the movement of the pointer + to a given region, preventing movement of the input device from moving + the pointer outside the region. In addition, the pointer continues to be + confined to the bounds of the outputs. + + If the pointer is not inside the given region when this request is made + it will have no effect until the pointer is moved inside the region. + + If the pointer is warped to a point outside of the region it will + (temporarily) escape confinement. Moving or warping the pointer back + inside the region will resume the confinement. + + If the region argument is null the compositor will only confine the + cursor to the bounds of the outputs. This is also the behavior if this + request is never made. + + The region is defined in the compositor's logical coordinate space. + + This request may be useful to place bounds on an interactive pointer + move or resize for example. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + + Warp the pointer to the given position in the compositor's logical + coordinate space. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + + + This enum is used to describe the keyboard modifiers that must be held + down to trigger a key binding or pointer binding. + + Note that river and wlroots use the values 2 and 16 for capslock and + numlock internally. It doesn't make sense to use locked modifiers for + bindings however so these values are not included in this enum. + + + + + + + + + + + + + Define a key binding in terms of an xkbcommon keysym and other + configurable properties. + + The new key binding is not enabled until initial configuration is + completed and the enable request is made during a windowing update + sequence. + + + + + + + + + Define a pointer binding in terms of a pointer button, modifiers, and + other configurable properties. + + The button argument is a Linux input event code defined in the + linux/input-event-codes.h header file (e.g. BTN_RIGHT). + + The new pointer binding is not enabled until initial configuration is + completed and the enable request is made during a windowing update + sequence. + + + + + + + + + + This object allows the window manager to configure a xkbcommon key binding + and receive events when the key binding is triggered. + + The new key binding is not enabled until initial configuration is + completed and the enable request is made during a windowing update + sequence. + + Normally, all key events are sent to the surface with keyboard focus by + the compositor. Key events that trigger a key binding are not sent to the + surface with keyboard focus. + + If multiple key bindings would be triggered by a single physical key event + on the compositor side, it is compositor policy which key binding(s) will + receive press/release events or if all of the matched key bindings receive + press/release events. + + Key bindings might be matched by the same physical key event due to shared + keysym and modifiers. The layout override feature may also cause the same + physical key event to trigger two key bindings with different keysyms and + different layout overrides configured. + + + + + This request indicates that the client will no longer use the xkb key + binding object and that it may be safely destroyed. + + + + + + Specify an xkb layout that should be used to translate key events for + the purpose of triggering this key binding irrespective of the currently + active xkb layout. + + The layout argument is a 0-indexed xkbcommon layout number for the + keyboard that generated the key event. + + If this request is not made before the enable request the currently + active xkb layout of the keyboard that generated the key event will be + used. + + It is a protocol error to make this request after the first enable + request. + + + + + + + This request should be made after all initial configuration has been + completed and the window manager wishes the key binding to be able to be + triggered. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + This request may be used to temporarily disable the key binding. It may + be later re-enabled with the enable request. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + This event indicates that the physical key triggering the binding has + been pressed. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + The compositor should wait for the windowing update sequence to complete + before processing further input events. This allows the window manager + client to, for example, modify key bindings and keyboard focus without + racing against future input events. The window manager should of course + respond as soon as possible as the capacity of the compositor to buffer + incoming input events is finite. + + + + + + This event indicates that the physical key triggering the binding has + been released. + + Releasing the modifiers for the binding without releasing the "main" + physical key that produces the bound keysym does not trigger the release + event. This event is sent when the "main" key is released, even if the + modifiers have changed since the pressed event. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + The compositor should wait for the windowing update sequence to complete + before processing further input events. This allows the window manager + client to, for example, modify key bindings and keyboard focus without + racing against future input events. The window manager should of course + respond as soon as possible as the capacity of the compositor to buffer + incoming input events is finite. + + + + + + + This object allows the window manager to configure a pointer binding and + receive events when the binding is triggered. + + The new key binding is not enabled until initial configuration is + completed and the enable request is made during a windowing update + sequence. + + Normally, all pointer button events are sent to the surface with pointer + focus by the compositor. Pointer button events that trigger a pointer + binding are not sent to the surface with pointer focus. + + If multiple pointer bindings would be triggered by a single physical + pointer event on the compositor side, it is compositor policy which + pointer binding(s) will receive press/release events or if all of the + matched pointer bindings receive press/release events. + + + + + This request indicates that the client will no longer use the pointer + binding object and that it may be safely destroyed. + + + + + + This request should be made after all initial configuration has been + completed and the window manager wishes the pointer binding to be able + to be triggered. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + This request may be used to temporarily disable the pointer binding. It + may be later re-enabled with the enable request. + + This request modifies windowing state and may only be made as part of a + windowing update sequence, see the river_window_manager_v1 description. + + + + + + This event indicates that the pointer button triggering the binding has + been pressed. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + The compositor should wait for the windowing update sequence to complete + before processing further input events. This allows the window manager + client to, for example, modify key bindings and keyboard focus without + racing against future input events. The window manager should of course + respond as soon as possible as the capacity of the compositor to buffer + incoming input events is finite. + + + + + + This event indicates that the pointer button triggering the binding has + been released. + + Releasing the modifiers for the binding without releasing the pointer + button does not trigger the release event. This event is sent when the + pointer button is released, even if the modifiers have changed since + the pressed event. + + This event will be followed by an update_windowing_start event after all + other new state has been sent by the server. + + The compositor should wait for the windowing update sequence to complete + before processing further input events. This allows the window manager + client to, for example, modify key bindings and keyboard focus without + racing against future input events. The window manager should of course + respond as soon as possible as the capacity of the compositor to buffer + incoming input events is finite. + + + + diff --git a/src/Backend.zig b/src/Backend.zig new file mode 100644 index 0000000..2a4c59a --- /dev/null +++ b/src/Backend.zig @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: 2025 Ben Buhse +// +// SPDX-License-Identifier: EUPL-1.2 + +const std = @import("std"); +const mem = std.mem; + +const wayland = @import("wayland"); +const wl = wayland.client.wl; +const river = wayland.client.river; + +const log = std.log.scoped(.Backend); + +const Backend = @This(); + +allocator: mem.Allocator, +initialized: bool, + +display: *wl.Display, +registry: *wl.Registry, + +compositor: ?*wl.Compositor = null, +shm: ?*wl.Shm = null, + +window_manager: ?*river.WindowManagerV1 = null, + +// outputs: std.SinglyLinkedList(Output) = .{}, + +/// Return a new Backend +pub fn init(allocator: mem.Allocator) !Backend { + const wl_display = wl.Display.connect(null) catch { + log.err("Error connecting to Wayland. Exiting", .{}); + std.posix.exit(1); + }; + + var backend: Backend = .{ + .initialized = false, + .allocator = allocator, + .display = wl_display, + .registry = try wl_display.getRegistry(), + }; + backend.registry.setListener(*Backend, registry_listener, &backend); + + // Do an initial roundtrip so the registry globals fire + const errno = backend.display.roundtrip(); + if (errno != .SUCCESS) { + log.err("Initial roundtrip failed: E{s}", .{@tagName(errno)}); + std.posix.exit(1); + } + + // These are all required by beansprout. + // If we are missing any of them, then let's exit. + if (backend.compositor == null) interface_not_advertised(wl.Compositor); + if (backend.shm == null) interface_not_advertised(wl.Shm); + if (backend.window_manager == null) interface_not_advertised(river.WindowManagerV1); + + return backend; +} + +/// Deinitialize a Backend +pub fn deinit(backend: *Backend) void { + _ = backend; +} + +fn registry_listener(registry: *wl.Registry, event: wl.Registry.Event, backend: *Backend) void { + // Since we can't return errors from the listener, we use a helper function so that + // we can easily log any errors the same way. + registry_listener_helper(registry, event, backend) catch |err| { + log.err("{any}", .{err}); + return; + }; +} + +fn registry_listener_helper(registry: *wl.Registry, event: wl.Registry.Event, backend: *Backend) !void { + // Use a helper function to log errors; the actual listener can't return an error. + switch (event) { + .global => |ev| { + if (mem.orderZ(u8, ev.interface, wl.Compositor.interface.name) == .eq) { + if (ev.version < 4) version_not_supported(wl.Compositor, ev.version, 4); + backend.compositor = try registry.bind(ev.name, wl.Compositor, 4); + } else if (mem.orderZ(u8, ev.interface, wl.Shm.interface.name) == .eq) { + backend.shm = try registry.bind(ev.name, wl.Shm, 1); + } else if (mem.orderZ(u8, ev.interface, river.WindowManagerV1.interface.name) == .eq) { + backend.window_manager = try registry.bind(ev.name, river.WindowManagerV1, 1); + } + }, + .global_remove => |ev| { + log.debug("TODO...", .{}); + _ = ev; + }, + } +} + +fn window_manager_listener(window_manager: *river.WindowManagerV1, event: river.WindowManagerV1.Event, backend: *Backend) !void { + _ = backend; + _ = window_manager; + switch (event) { + .update_windowing_start => log.debug("RAAAA"), + .update_windowing_end => log.debug("REEE"), + else => log.debug("WOOOO"), + } +} + +fn interface_not_advertised(comptime WaylandGlobal: type) noreturn { + log.err("{s} not advertised. Exiting", .{WaylandGlobal.interface.name}); + std.posix.exit(1); +} + +fn version_not_supported(comptime WaylandGlobal: type, have_version: u32, need_version: u32) noreturn { + log.err("The compositor only advertised {s} version {d} but version {d} is required. Exiting", .{ WaylandGlobal.interface.name, have_version, need_version }); + std.posix.exit(1); +} diff --git a/src/event_loop.zig b/src/event_loop.zig new file mode 100644 index 0000000..1e02b12 --- /dev/null +++ b/src/event_loop.zig @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2025 Ben Buhse +// +// SPDX-License-Identifier: EUPL-1.2 + +const std = @import("std"); + +pub fn run() void { + while (true) {} +} diff --git a/src/main.zig b/src/main.zig index 9c0d968..c32f8ba 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2,8 +2,31 @@ // // SPDX-License-Identifier: EUPL-1.2 -pub fn main() !void { - std.debug.print("All your {s} are belong to us.\n", .{"protocol"}); -} - const std = @import("std"); + +const event_loop = @import("event_loop.zig"); +const Backend = @import("Backend.zig"); + +const log = std.log.scoped(.main); + +const usage: []const u8 = + \\usage: beansprout [options] + \\ + \\ -i, --image TODO TODO TODO TODO + \\ -h, --help TODO TODO TODO TODO + \\ -V, --version TODO TODO TODO TODO + \\ +; + +pub fn main() !void { + const allocator = std.heap.c_allocator; + + // Set up Wayland stuff + var backend = try Backend.init(allocator); + defer backend.deinit(); + + event_loop.run(); + + // TODO: REMOVEME + log.debug("Exiting...", .{}); +}