diff --git a/flake.nix b/flake.nix
index 5f5a943..86dddbb 100644
--- a/flake.nix
+++ b/flake.nix
@@ -23,6 +23,7 @@
pkg-config
glslang
shaderc
+ wayland-scanner
];
buildInputs = with pkgs; [
vulkan-loader
diff --git a/meson.build b/meson.build
index 0aa7aa7..e37cca0 100644
--- a/meson.build
+++ b/meson.build
@@ -1,4 +1,4 @@
-project('vr-compositor', 'cpp',
+project('vr-compositor', 'c', 'cpp',
version: '0.1',
default_options: [
'cpp_std=c++26',
@@ -17,14 +17,32 @@ fastgltf_opts.set_override_option('werror', 'false')
fastgltf = cmake.subproject('fastgltf', options: fastgltf_opts)
cc = meson.get_compiler('cpp')
+wl_mod = import('wayland')
wayland_dep = dependency('wayland-server', include_type: 'system')
+wayland_client_dep = dependency('wayland-client', include_type: 'system')
vulkan_dep = dependency('vulkan', include_type: 'system')
openxr_dep = dependency('openxr', include_type: 'system')
zlib_dep = dependency('zlib', include_type: 'system')
sdl3_dep = dependency('sdl3', include_type: 'system')
libinput_dep = dependency('libinput', include_type: 'system')
libudev_dep = dependency('libudev', include_type: 'system')
+wayland_protocol = wl_mod.scan_xml(
+ 'protocols/wayland.xml',
+ client: false,
+ server: true,
+)
+wayland_protocol_source = wayland_protocol[0]
+wayland_server_header = wayland_protocol[1]
+
+xdg_shell_protocol = wl_mod.scan_xml(
+ 'protocols/xdg-shell.xml',
+ client: true,
+ server: true,
+)
+xdg_shell_protocol_source = xdg_shell_protocol[0]
+xdg_shell_client_header = xdg_shell_protocol[1]
+xdg_shell_server_header = xdg_shell_protocol[2]
imgui_src = files(
'thirdparty/imgui/imgui.cpp',
'thirdparty/imgui/imgui_draw.cpp',
@@ -161,12 +179,24 @@ exe = executable('vr-compositor',
'src/Loader.cpp',
'src/DescriptorWriter.cpp',
'src/CPUTexture.cpp',
+ 'src/wayland/WaylandServer.cpp',
+ 'src/wayland/Surface.cpp',
+ 'src/wayland/Shm.cpp',
+ 'src/wayland/protocols/CompositorProtocol.cpp',
+ 'src/wayland/protocols/ShmProtocol.cpp',
+ 'src/wayland/protocols/XdgShellProtocol.cpp',
'src/Skybox.cpp',
'src/VulkanRenderer.cpp',
'src/Application.cpp',
+ wayland_protocol_source,
+ xdg_shell_protocol_source,
+ wayland_server_header,
+ xdg_shell_server_header,
],
+ c_args: ['-Wno-missing-variable-declarations'],
include_directories: [
vkbootstrap_inc,
+ include_directories('.'),
imgui_inc,
'thirdparty/smath/include'
],
@@ -176,3 +206,11 @@ exe = executable('vr-compositor',
'--embed-dir=' + join_paths(meson.project_build_root(), 'shaders'),
],
)
+
+executable('shm-life',
+ 'tools/shm_life.cpp',
+ dependencies: [wayland_client_dep],
+ sources: [xdg_shell_protocol_source, xdg_shell_client_header],
+ c_args: ['-Wno-missing-variable-declarations'],
+ cpp_args: ['-Wno-cast-qual'],
+)
diff --git a/protocols/wayland.xml b/protocols/wayland.xml
new file mode 100644
index 0000000..de6756b
--- /dev/null
+++ b/protocols/wayland.xml
@@ -0,0 +1,3314 @@
+
+
+
+
+ Copyright © 2008-2011 Kristian Høgsberg
+ Copyright © 2010-2011 Intel Corporation
+ Copyright © 2012-2013 Collabora, Ltd.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the
+ next paragraph) shall be included in all copies or substantial
+ portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+
+
+
+ The core global object. This is a special singleton object. It
+ is used for internal Wayland protocol features.
+
+
+
+
+ The sync request asks the server to emit the 'done' event
+ on the returned wl_callback object. Since requests are
+ handled in-order and events are delivered in-order, this can
+ be used as a barrier to ensure all previous requests and the
+ resulting events have been handled.
+
+ The object returned by this request will be destroyed by the
+ compositor after the callback is fired and as such the client must not
+ attempt to use it after that point.
+
+ The callback_data passed in the callback is undefined and should be ignored.
+
+
+
+
+
+
+ This request creates a registry object that allows the client
+ to list and bind the global objects available from the
+ compositor.
+
+ It should be noted that the server side resources consumed in
+ response to a get_registry request can only be released when the
+ client disconnects, not when the client side proxy is destroyed.
+ Therefore, clients should invoke get_registry as infrequently as
+ possible to avoid wasting memory.
+
+
+
+
+
+
+ The error event is sent out when a fatal (non-recoverable)
+ error has occurred. The object_id argument is the object
+ where the error occurred, most often in response to a request
+ to that object. The code identifies the error and is defined
+ by the object interface. As such, each interface defines its
+ own set of error codes. The message is a brief description
+ of the error, for (debugging) convenience.
+
+
+
+
+
+
+
+
+ These errors are global and can be emitted in response to any
+ server request.
+
+
+
+
+
+
+
+
+
+ This event is used internally by the object ID management
+ logic. When a client deletes an object that it had created,
+ the server will send this event to acknowledge that it has
+ seen the delete request. When the client receives this event,
+ it will know that it can safely reuse the object ID.
+
+
+
+
+
+
+
+ The singleton global registry object. The server has a number of
+ global objects that are available to all clients. These objects
+ typically represent an actual object in the server (for example,
+ an input device) or they are singleton objects that provide
+ extension functionality.
+
+ When a client creates a registry object, the registry object
+ will emit a global event for each global currently in the
+ registry. Globals come and go as a result of device or
+ monitor hotplugs, reconfiguration or other events, and the
+ registry will send out global and global_remove events to
+ keep the client up to date with the changes. To mark the end
+ of the initial burst of events, the client can use the
+ wl_display.sync request immediately after calling
+ wl_display.get_registry.
+
+ A client can bind to a global object by using the bind
+ request. This creates a client-side handle that lets the object
+ emit events to the client and lets the client invoke requests on
+ the object.
+
+
+
+
+ Binds a new, client-created object to the server using the
+ specified name as the identifier.
+
+
+
+
+
+
+
+ Notify the client of global objects.
+
+ The event notifies the client that a global object with
+ the given name is now available, and it implements the
+ given version of the given interface.
+
+
+
+
+
+
+
+
+ Notify the client of removed global objects.
+
+ This event notifies the client that the global identified
+ by name is no longer available. If the client bound to
+ the global using the bind request, the client should now
+ destroy that object.
+
+ The object remains valid and requests to the object will be
+ ignored until the client destroys it, to avoid races between
+ the global going away and a client sending a request to it.
+
+
+
+
+
+
+
+ Clients can handle the 'done' event to get notified when
+ the related request is done.
+
+ Note, because wl_callback objects are created from multiple independent
+ factory interfaces, the wl_callback interface is frozen at version 1.
+
+
+
+
+ Notify the client when the related request is done.
+
+
+
+
+
+
+
+ A compositor. This object is a singleton global. The
+ compositor is in charge of combining the contents of multiple
+ surfaces into one displayable output.
+
+
+
+
+ Ask the compositor to create a new surface.
+
+
+
+
+
+
+ Ask the compositor to create a new region.
+
+
+
+
+
+
+
+ The wl_shm_pool object encapsulates a piece of memory shared
+ between the compositor and client. Through the wl_shm_pool
+ object, the client can allocate shared memory wl_buffer objects.
+ All objects created through the same pool share the same
+ underlying mapped memory. Reusing the mapped memory avoids the
+ setup/teardown overhead and is useful when interactively resizing
+ a surface or for many small buffers.
+
+
+
+
+ Create a wl_buffer object from the pool.
+
+ The buffer is created offset bytes into the pool and has
+ width and height as specified. The stride argument specifies
+ the number of bytes from the beginning of one row to the beginning
+ of the next. The format is the pixel format of the buffer and
+ must be one of those advertised through the wl_shm.format event.
+
+ A buffer will keep a reference to the pool it was created from
+ so it is valid to destroy the pool immediately after creating
+ a buffer from it.
+
+
+
+
+
+
+
+
+
+
+
+ Destroy the shared memory pool.
+
+ The mmapped memory will be released when all
+ buffers that have been created from this pool
+ are gone.
+
+
+
+
+
+ This request will cause the server to remap the backing memory
+ for the pool from the file descriptor passed when the pool was
+ created, but using the new size. This request can only be
+ used to make the pool bigger.
+
+ This request only changes the amount of bytes that are mmapped
+ by the server and does not touch the file corresponding to the
+ file descriptor passed at creation time. It is the client's
+ responsibility to ensure that the file is at least as big as
+ the new pool size.
+
+
+
+
+
+
+
+ A singleton global object that provides support for shared
+ memory.
+
+ Clients can create wl_shm_pool objects using the create_pool
+ request.
+
+ On binding the wl_shm object one or more format events
+ are emitted to inform clients about the valid pixel formats
+ that can be used for buffers.
+
+
+
+
+ These errors can be emitted in response to wl_shm requests.
+
+
+
+
+
+
+
+
+ This describes the memory layout of an individual pixel.
+
+ All renderers should support argb8888 and xrgb8888 but any other
+ formats are optional and may not be supported by the particular
+ renderer in use.
+
+ The drm format codes match the macros defined in drm_fourcc.h, except
+ argb8888 and xrgb8888. The formats actually supported by the compositor
+ will be reported by the format event.
+
+ For all wl_shm formats and unless specified in another protocol
+ extension, pre-multiplied alpha is used for pixel values.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Create a new wl_shm_pool object.
+
+ The pool can be used to create shared memory based buffer
+ objects. The server will mmap size bytes of the passed file
+ descriptor, to use as backing memory for the pool.
+
+
+
+
+
+
+
+
+ Informs the client about a valid pixel format that
+ can be used for buffers. Known formats include
+ argb8888 and xrgb8888.
+
+
+
+
+
+
+
+
+ Using this request a client can tell the server that it is not going to
+ use the shm object anymore.
+
+ Objects created via this interface remain unaffected.
+
+
+
+
+
+
+ A buffer provides the content for a wl_surface. Buffers are
+ created through factory interfaces such as wl_shm, wp_linux_buffer_params
+ (from the linux-dmabuf protocol extension) or similar. It has a width and
+ a height and can be attached to a wl_surface, but the mechanism by which a
+ client provides and updates the contents is defined by the buffer factory
+ interface.
+
+ Color channels are assumed to be electrical rather than optical (in other
+ words, encoded with a transfer function) unless otherwise specified. If
+ the buffer uses a format that has an alpha channel, the alpha channel is
+ assumed to be premultiplied into the electrical color channel values
+ (after transfer function encoding) unless otherwise specified.
+
+ Note, because wl_buffer objects are created from multiple independent
+ factory interfaces, the wl_buffer interface is frozen at version 1.
+
+
+
+
+ Destroy a buffer. If and how you need to release the backing
+ storage is defined by the buffer factory interface.
+
+ For possible side-effects to a surface, see wl_surface.attach.
+
+
+
+
+
+ Sent when this wl_buffer is no longer used by the compositor.
+
+ For more information on when release events may or may not be sent,
+ and what consequences it has, please see the description of
+ wl_surface.attach.
+
+ If a client receives a release event before the frame callback
+ requested in the same wl_surface.commit that attaches this
+ wl_buffer to a surface, then the client is immediately free to
+ reuse the buffer and its backing storage, and does not need a
+ second buffer for the next surface content update. Typically
+ this is possible, when the compositor maintains a copy of the
+ wl_surface contents, e.g. as a GL texture. This is an important
+ optimization for GL(ES) compositors with wl_shm clients.
+
+
+
+
+
+
+ A wl_data_offer represents a piece of data offered for transfer
+ by another client (the source client). It is used by the
+ copy-and-paste and drag-and-drop mechanisms. The offer
+ describes the different mime types that the data can be
+ converted to and provides the mechanism for transferring the
+ data directly from the source client.
+
+
+
+
+
+
+
+
+
+
+
+ Indicate that the client can accept the given mime type, or
+ NULL for not accepted.
+
+ For objects of version 2 or older, this request is used by the
+ client to give feedback whether the client can receive the given
+ mime type, or NULL if none is accepted; the feedback does not
+ determine whether the drag-and-drop operation succeeds or not.
+
+ For objects of version 3 or newer, this request determines the
+ final result of the drag-and-drop operation. If the end result
+ is that no mime types were accepted, the drag-and-drop operation
+ will be cancelled and the corresponding drag source will receive
+ wl_data_source.cancelled. Clients may still use this event in
+ conjunction with wl_data_source.action for feedback.
+
+
+
+
+
+
+
+ To transfer the offered data, the client issues this request
+ and indicates the mime type it wants to receive. The transfer
+ happens through the passed file descriptor (typically created
+ with the pipe system call). The source client writes the data
+ in the mime type representation requested and then closes the
+ file descriptor.
+
+ The receiving client reads from the read end of the pipe until
+ EOF and then closes its end, at which point the transfer is
+ complete.
+
+ This request may happen multiple times for different mime types,
+ both before and after wl_data_device.drop. Drag-and-drop destination
+ clients may preemptively fetch data or examine it more closely to
+ determine acceptance.
+
+
+
+
+
+
+
+ Destroy the data offer.
+
+
+
+
+
+ Sent immediately after creating the wl_data_offer object. One
+ event per offered mime type.
+
+
+
+
+
+
+
+
+ Notifies the compositor that the drag destination successfully
+ finished the drag-and-drop operation.
+
+ Upon receiving this request, the compositor will emit
+ wl_data_source.dnd_finished on the drag source client.
+
+ It is a client error to perform other requests than
+ wl_data_offer.destroy after this one. It is also an error to perform
+ this request after a NULL mime type has been set in
+ wl_data_offer.accept or no action was received through
+ wl_data_offer.action.
+
+ If wl_data_offer.finish request is received for a non drag and drop
+ operation, the invalid_finish protocol error is raised.
+
+
+
+
+
+ Sets the actions that the destination side client supports for
+ this operation. This request may trigger the emission of
+ wl_data_source.action and wl_data_offer.action events if the compositor
+ needs to change the selected action.
+
+ This request can be called multiple times throughout the
+ drag-and-drop operation, typically in response to wl_data_device.enter
+ or wl_data_device.motion events.
+
+ This request determines the final result of the drag-and-drop
+ operation. If the end result is that no action is accepted,
+ the drag source will receive wl_data_source.cancelled.
+
+ The dnd_actions argument must contain only values expressed in the
+ wl_data_device_manager.dnd_actions enum, and the preferred_action
+ argument must only contain one of those values set, otherwise it
+ will result in a protocol error.
+
+ While managing an "ask" action, the destination drag-and-drop client
+ may perform further wl_data_offer.receive requests, and is expected
+ to perform one last wl_data_offer.set_actions request with a preferred
+ action other than "ask" (and optionally wl_data_offer.accept) before
+ requesting wl_data_offer.finish, in order to convey the action selected
+ by the user. If the preferred action is not in the
+ wl_data_offer.source_actions mask, an error will be raised.
+
+ If the "ask" action is dismissed (e.g. user cancellation), the client
+ is expected to perform wl_data_offer.destroy right away.
+
+ This request can only be made on drag-and-drop offers, a protocol error
+ will be raised otherwise.
+
+
+
+
+
+
+
+ This event indicates the actions offered by the data source. It
+ will be sent immediately after creating the wl_data_offer object,
+ or anytime the source side changes its offered actions through
+ wl_data_source.set_actions.
+
+
+
+
+
+
+ This event indicates the action selected by the compositor after
+ matching the source/destination side actions. Only one action (or
+ none) will be offered here.
+
+ This event can be emitted multiple times during the drag-and-drop
+ operation in response to destination side action changes through
+ wl_data_offer.set_actions.
+
+ This event will no longer be emitted after wl_data_device.drop
+ happened on the drag-and-drop destination, the client must
+ honor the last action received, or the last preferred one set
+ through wl_data_offer.set_actions when handling an "ask" action.
+
+ Compositors may also change the selected action on the fly, mainly
+ in response to keyboard modifier changes during the drag-and-drop
+ operation.
+
+ The most recent action received is always the valid one. Prior to
+ receiving wl_data_device.drop, the chosen action may change (e.g.
+ due to keyboard modifiers being pressed). At the time of receiving
+ wl_data_device.drop the drag-and-drop destination must honor the
+ last action received.
+
+ Action changes may still happen after wl_data_device.drop,
+ especially on "ask" actions, where the drag-and-drop destination
+ may choose another action afterwards. Action changes happening
+ at this stage are always the result of inter-client negotiation, the
+ compositor shall no longer be able to induce a different action.
+
+ Upon "ask" actions, it is expected that the drag-and-drop destination
+ may potentially choose a different action and/or mime type,
+ based on wl_data_offer.source_actions and finally chosen by the
+ user (e.g. popping up a menu with the available options). The
+ final wl_data_offer.set_actions and wl_data_offer.accept requests
+ must happen before the call to wl_data_offer.finish.
+
+
+
+
+
+
+
+ The wl_data_source object is the source side of a wl_data_offer.
+ It is created by the source client in a data transfer and
+ provides a way to describe the offered data and a way to respond
+ to requests to transfer the data.
+
+
+
+
+
+
+
+
+
+ This request adds a mime type to the set of mime types
+ advertised to targets. Can be called several times to offer
+ multiple types.
+
+
+
+
+
+
+ Destroy the data source.
+
+
+
+
+
+ Sent when a target accepts pointer_focus or motion events. If
+ a target does not accept any of the offered types, type is NULL.
+
+ Used for feedback during drag-and-drop.
+
+
+
+
+
+
+ Request for data from the client. Send the data as the
+ specified mime type over the passed file descriptor, then
+ close it.
+
+
+
+
+
+
+
+ This data source is no longer valid. There are several reasons why
+ this could happen:
+
+ - The data source has been replaced by another data source.
+ - The drag-and-drop operation was performed, but the drop destination
+ did not accept any of the mime types offered through
+ wl_data_source.target.
+ - The drag-and-drop operation was performed, but the drop destination
+ did not select any of the actions present in the mask offered through
+ wl_data_source.action.
+ - The drag-and-drop operation was performed but didn't happen over a
+ surface.
+ - The compositor cancelled the drag-and-drop operation (e.g. compositor
+ dependent timeouts to avoid stale drag-and-drop transfers).
+
+ The client should clean up and destroy this data source.
+
+ For objects of version 2 or older, wl_data_source.cancelled will
+ only be emitted if the data source was replaced by another data
+ source.
+
+
+
+
+
+
+
+ Sets the actions that the source side client supports for this
+ operation. This request may trigger wl_data_source.action and
+ wl_data_offer.action events if the compositor needs to change the
+ selected action.
+
+ The dnd_actions argument must contain only values expressed in the
+ wl_data_device_manager.dnd_actions enum, otherwise it will result
+ in a protocol error.
+
+ This request must be made once only, and can only be made on sources
+ used in drag-and-drop, so it must be performed before
+ wl_data_device.start_drag. Attempting to use the source other than
+ for drag-and-drop will raise a protocol error.
+
+
+
+
+
+
+ The user performed the drop action. This event does not indicate
+ acceptance, wl_data_source.cancelled may still be emitted afterwards
+ if the drop destination does not accept any mime type.
+
+ However, this event might however not be received if the compositor
+ cancelled the drag-and-drop operation before this event could happen.
+
+ Note that the data_source may still be used in the future and should
+ not be destroyed here.
+
+
+
+
+
+ The drop destination finished interoperating with this data
+ source, so the client is now free to destroy this data source and
+ free all associated data.
+
+ If the action used to perform the operation was "move", the
+ source can now delete the transferred data.
+
+
+
+
+
+ This event indicates the action selected by the compositor after
+ matching the source/destination side actions. Only one action (or
+ none) will be offered here.
+
+ This event can be emitted multiple times during the drag-and-drop
+ operation, mainly in response to destination side changes through
+ wl_data_offer.set_actions, and as the data device enters/leaves
+ surfaces.
+
+ It is only possible to receive this event after
+ wl_data_source.dnd_drop_performed if the drag-and-drop operation
+ ended in an "ask" action, in which case the final wl_data_source.action
+ event will happen immediately before wl_data_source.dnd_finished.
+
+ Compositors may also change the selected action on the fly, mainly
+ in response to keyboard modifier changes during the drag-and-drop
+ operation.
+
+ The most recent action received is always the valid one. The chosen
+ action may change alongside negotiation (e.g. an "ask" action can turn
+ into a "move" operation), so the effects of the final action must
+ always be applied in wl_data_offer.dnd_finished.
+
+ Clients can trigger cursor surface changes from this point, so
+ they reflect the current action.
+
+
+
+
+
+
+
+ There is one wl_data_device per seat which can be obtained
+ from the global wl_data_device_manager singleton.
+
+ A wl_data_device provides access to inter-client data transfer
+ mechanisms such as copy-and-paste and drag-and-drop.
+
+
+
+
+
+
+
+
+
+ This request asks the compositor to start a drag-and-drop
+ operation on behalf of the client.
+
+ The source argument is the data source that provides the data
+ for the eventual data transfer. If source is NULL, enter, leave
+ and motion events are sent only to the client that initiated the
+ drag and the client is expected to handle the data passing
+ internally. If source is destroyed, the drag-and-drop session will be
+ cancelled.
+
+ The origin surface is the surface where the drag originates and
+ the client must have an active implicit grab that matches the
+ serial.
+
+ The icon surface is an optional (can be NULL) surface that
+ provides an icon to be moved around with the cursor. Initially,
+ the top-left corner of the icon surface is placed at the cursor
+ hotspot, but subsequent wl_surface.offset requests can move the
+ relative position. Attach requests must be confirmed with
+ wl_surface.commit as usual. The icon surface is given the role of
+ a drag-and-drop icon. If the icon surface already has another role,
+ it raises a protocol error.
+
+ The input region is ignored for wl_surfaces with the role of a
+ drag-and-drop icon.
+
+ The given source may not be used in any further set_selection or
+ start_drag requests. Attempting to reuse a previously-used source
+ may send a used_source error.
+
+
+
+
+
+
+
+
+
+ This request asks the compositor to set the selection
+ to the data from the source on behalf of the client.
+
+ To unset the selection, set the source to NULL.
+
+ The given source may not be used in any further set_selection or
+ start_drag requests. Attempting to reuse a previously-used source
+ may send a used_source error.
+
+
+
+
+
+
+
+ The data_offer event introduces a new wl_data_offer object,
+ which will subsequently be used in either the
+ data_device.enter event (for drag-and-drop) or the
+ data_device.selection event (for selections). Immediately
+ following the data_device.data_offer event, the new data_offer
+ object will send out data_offer.offer events to describe the
+ mime types it offers.
+
+
+
+
+
+
+ This event is sent when an active drag-and-drop pointer enters
+ a surface owned by the client. The position of the pointer at
+ enter time is provided by the x and y arguments, in surface-local
+ coordinates.
+
+
+
+
+
+
+
+
+
+
+ This event is sent when the drag-and-drop pointer leaves the
+ surface and the session ends. The client must destroy the
+ wl_data_offer introduced at enter time at this point.
+
+
+
+
+
+ This event is sent when the drag-and-drop pointer moves within
+ the currently focused surface. The new position of the pointer
+ is provided by the x and y arguments, in surface-local
+ coordinates.
+
+
+
+
+
+
+
+
+ The event is sent when a drag-and-drop operation is ended
+ because the implicit grab is removed.
+
+ The drag-and-drop destination is expected to honor the last action
+ received through wl_data_offer.action, if the resulting action is
+ "copy" or "move", the destination can still perform
+ wl_data_offer.receive requests, and is expected to end all
+ transfers with a wl_data_offer.finish request.
+
+ If the resulting action is "ask", the action will not be considered
+ final. The drag-and-drop destination is expected to perform one last
+ wl_data_offer.set_actions request, or wl_data_offer.destroy in order
+ to cancel the operation.
+
+
+
+
+
+ The selection event is sent out to notify the client of a new
+ wl_data_offer for the selection for this device. The
+ data_device.data_offer and the data_offer.offer events are
+ sent out immediately before this event to introduce the data
+ offer object. The selection event is sent to a client
+ immediately before receiving keyboard focus and when a new
+ selection is set while the client has keyboard focus. The
+ data_offer is valid until a new data_offer or NULL is received
+ or until the client loses keyboard focus. Switching surface with
+ keyboard focus within the same client doesn't mean a new selection
+ will be sent. The client must destroy the previous selection
+ data_offer, if any, upon receiving this event.
+
+
+
+
+
+
+
+
+ This request destroys the data device.
+
+
+
+
+
+
+ The wl_data_device_manager is a singleton global object that
+ provides access to inter-client data transfer mechanisms such as
+ copy-and-paste and drag-and-drop. These mechanisms are tied to
+ a wl_seat and this interface lets a client get a wl_data_device
+ corresponding to a wl_seat.
+
+ Depending on the version bound, the objects created from the bound
+ wl_data_device_manager object will have different requirements for
+ functioning properly. See wl_data_source.set_actions,
+ wl_data_offer.accept and wl_data_offer.finish for details.
+
+
+
+
+ Create a new data source.
+
+
+
+
+
+
+ Create a new data device for a given seat.
+
+
+
+
+
+
+
+
+
+ This is a bitmask of the available/preferred actions in a
+ drag-and-drop operation.
+
+ In the compositor, the selected action is a result of matching the
+ actions offered by the source and destination sides. "action" events
+ with a "none" action will be sent to both source and destination if
+ there is no match. All further checks will effectively happen on
+ (source actions ∩ destination actions).
+
+ In addition, compositors may also pick different actions in
+ reaction to key modifiers being pressed. One common design that
+ is used in major toolkits (and the behavior recommended for
+ compositors) is:
+
+ - If no modifiers are pressed, the first match (in bit order)
+ will be used.
+ - Pressing Shift selects "move", if enabled in the mask.
+ - Pressing Control selects "copy", if enabled in the mask.
+
+ Behavior beyond that is considered implementation-dependent.
+ Compositors may for example bind other modifiers (like Alt/Meta)
+ or drags initiated with other buttons than BTN_LEFT to specific
+ actions (e.g. "ask").
+
+
+
+
+
+
+
+
+
+
+ This interface is implemented by servers that provide
+ desktop-style user interfaces.
+
+ It allows clients to associate a wl_shell_surface with
+ a basic surface.
+
+ Note! This protocol is deprecated and not intended for production use.
+ For desktop-style user interfaces, use xdg_shell. Compositors and clients
+ should not implement this interface.
+
+
+
+
+
+
+
+
+ Create a shell surface for an existing surface. This gives
+ the wl_surface the role of a shell surface. If the wl_surface
+ already has another role, it raises a protocol error.
+
+ Only one shell surface can be associated with a given surface.
+
+
+
+
+
+
+
+
+ An interface that may be implemented by a wl_surface, for
+ implementations that provide a desktop-style user interface.
+
+ It provides requests to treat surfaces like toplevel, fullscreen
+ or popup windows, move, resize or maximize them, associate
+ metadata like title and class, etc.
+
+ On the server side the object is automatically destroyed when
+ the related wl_surface is destroyed. On the client side,
+ wl_shell_surface_destroy() must be called before destroying
+ the wl_surface object.
+
+
+
+
+ A client must respond to a ping event with a pong request or
+ the client may be deemed unresponsive.
+
+
+
+
+
+
+ Start a pointer-driven move of the surface.
+
+ This request must be used in response to a button press event.
+ The server may ignore move requests depending on the state of
+ the surface (e.g. fullscreen or maximized).
+
+
+
+
+
+
+
+ These values are used to indicate which edge of a surface
+ is being dragged in a resize operation. The server may
+ use this information to adapt its behavior, e.g. choose
+ an appropriate cursor image.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Start a pointer-driven resizing of the surface.
+
+ This request must be used in response to a button press event.
+ The server may ignore resize requests depending on the state of
+ the surface (e.g. fullscreen or maximized).
+
+
+
+
+
+
+
+
+ Map the surface as a toplevel surface.
+
+ A toplevel surface is not fullscreen, maximized or transient.
+
+
+
+
+
+ These flags specify details of the expected behaviour
+ of transient surfaces. Used in the set_transient request.
+
+
+
+
+
+
+ Map the surface relative to an existing surface.
+
+ The x and y arguments specify the location of the upper left
+ corner of the surface relative to the upper left corner of the
+ parent surface, in surface-local coordinates.
+
+ The flags argument controls details of the transient behaviour.
+
+
+
+
+
+
+
+
+
+ Hints to indicate to the compositor how to deal with a conflict
+ between the dimensions of the surface and the dimensions of the
+ output. The compositor is free to ignore this parameter.
+
+
+
+
+
+
+
+
+
+ Map the surface as a fullscreen surface.
+
+ If an output parameter is given then the surface will be made
+ fullscreen on that output. If the client does not specify the
+ output then the compositor will apply its policy - usually
+ choosing the output on which the surface has the biggest surface
+ area.
+
+ The client may specify a method to resolve a size conflict
+ between the output size and the surface size - this is provided
+ through the method parameter.
+
+ The framerate parameter is used only when the method is set
+ to "driver", to indicate the preferred framerate. A value of 0
+ indicates that the client does not care about framerate. The
+ framerate is specified in mHz, that is framerate of 60000 is 60Hz.
+
+ A method of "scale" or "driver" implies a scaling operation of
+ the surface, either via a direct scaling operation or a change of
+ the output mode. This will override any kind of output scaling, so
+ that mapping a surface with a buffer size equal to the mode can
+ fill the screen independent of buffer_scale.
+
+ A method of "fill" means we don't scale up the buffer, however
+ any output scale is applied. This means that you may run into
+ an edge case where the application maps a buffer with the same
+ size of the output mode but buffer_scale 1 (thus making a
+ surface larger than the output). In this case it is allowed to
+ downscale the results to fit the screen.
+
+ The compositor must reply to this request with a configure event
+ with the dimensions for the output on which the surface will
+ be made fullscreen.
+
+
+
+
+
+
+
+
+ Map the surface as a popup.
+
+ A popup surface is a transient surface with an added pointer
+ grab.
+
+ An existing implicit grab will be changed to owner-events mode,
+ and the popup grab will continue after the implicit grab ends
+ (i.e. releasing the mouse button does not cause the popup to
+ be unmapped).
+
+ The popup grab continues until the window is destroyed or a
+ mouse button is pressed in any other client's window. A click
+ in any of the client's surfaces is reported as normal, however,
+ clicks in other clients' surfaces will be discarded and trigger
+ the callback.
+
+ The x and y arguments specify the location of the upper left
+ corner of the surface relative to the upper left corner of the
+ parent surface, in surface-local coordinates.
+
+
+
+
+
+
+
+
+
+
+
+ Map the surface as a maximized surface.
+
+ If an output parameter is given then the surface will be
+ maximized on that output. If the client does not specify the
+ output then the compositor will apply its policy - usually
+ choosing the output on which the surface has the biggest surface
+ area.
+
+ The compositor will reply with a configure event telling
+ the expected new surface size. The operation is completed
+ on the next buffer attach to this surface.
+
+ A maximized surface typically fills the entire output it is
+ bound to, except for desktop elements such as panels. This is
+ the main difference between a maximized shell surface and a
+ fullscreen shell surface.
+
+ The details depend on the compositor implementation.
+
+
+
+
+
+
+ Set a short title for the surface.
+
+ This string may be used to identify the surface in a task bar,
+ window list, or other user interface elements provided by the
+ compositor.
+
+ The string must be encoded in UTF-8.
+
+
+
+
+
+
+ Set a class for the surface.
+
+ The surface class identifies the general class of applications
+ to which the surface belongs. A common convention is to use the
+ file name (or the full path if it is a non-standard location) of
+ the application's .desktop file as the class.
+
+
+
+
+
+
+ Ping a client to check if it is receiving events and sending
+ requests. A client is expected to reply with a pong request.
+
+
+
+
+
+
+ The configure event asks the client to resize its surface.
+
+ The size is a hint, in the sense that the client is free to
+ ignore it if it doesn't resize, pick a smaller size (to
+ satisfy aspect ratio or resize in steps of NxM pixels).
+
+ The edges parameter provides a hint about how the surface
+ was resized. The client may use this information to decide
+ how to adjust its content to the new size (e.g. a scrolling
+ area might adjust its content position to leave the viewable
+ content unmoved).
+
+ The client is free to dismiss all but the last configure
+ event it received.
+
+ The width and height arguments specify the size of the window
+ in surface-local coordinates.
+
+
+
+
+
+
+
+
+ The popup_done event is sent out when a popup grab is broken,
+ that is, when the user clicks a surface that doesn't belong
+ to the client owning the popup surface.
+
+
+
+
+
+
+ A surface is a rectangular area that may be displayed on zero
+ or more outputs, and shown any number of times at the compositor's
+ discretion. They can present wl_buffers, receive user input, and
+ define a local coordinate system.
+
+ The size of a surface (and relative positions on it) is described
+ in surface-local coordinates, which may differ from the buffer
+ coordinates of the pixel content, in case a buffer_transform
+ or a buffer_scale is used.
+
+ A surface without a "role" is fairly useless: a compositor does
+ not know where, when or how to present it. The role is the
+ purpose of a wl_surface. Examples of roles are a cursor for a
+ pointer (as set by wl_pointer.set_cursor), a drag icon
+ (wl_data_device.start_drag), a sub-surface
+ (wl_subcompositor.get_subsurface), and a window as defined by a
+ shell protocol (e.g. wl_shell.get_shell_surface).
+
+ A surface can have only one role at a time. Initially a
+ wl_surface does not have a role. Once a wl_surface is given a
+ role, it is set permanently for the whole lifetime of the
+ wl_surface object. Giving the current role again is allowed,
+ unless explicitly forbidden by the relevant interface
+ specification.
+
+ Surface roles are given by requests in other interfaces such as
+ wl_pointer.set_cursor. The request should explicitly mention
+ that this request gives a role to a wl_surface. Often, this
+ request also creates a new protocol object that represents the
+ role and adds additional functionality to wl_surface. When a
+ client wants to destroy a wl_surface, they must destroy this role
+ object before the wl_surface, otherwise a defunct_role_object error is
+ sent.
+
+ Destroying the role object does not remove the role from the
+ wl_surface, but it may stop the wl_surface from "playing the role".
+ For instance, if a wl_subsurface object is destroyed, the wl_surface
+ it was created for will be unmapped and forget its position and
+ z-order. It is allowed to create a wl_subsurface for the same
+ wl_surface again, but it is not allowed to use the wl_surface as
+ a cursor (cursor is a different role than sub-surface, and role
+ switching is not allowed).
+
+
+
+
+ These errors can be emitted in response to wl_surface requests.
+
+
+
+
+
+
+
+
+
+
+ Deletes the surface and invalidates its object ID.
+
+
+
+
+
+ Set a buffer as the content of this surface.
+
+ The new size of the surface is calculated based on the buffer
+ size transformed by the inverse buffer_transform and the
+ inverse buffer_scale. This means that at commit time the supplied
+ buffer size must be an integer multiple of the buffer_scale. If
+ that's not the case, an invalid_size error is sent.
+
+ The x and y arguments specify the location of the new pending
+ buffer's upper left corner, relative to the current buffer's upper
+ left corner, in surface-local coordinates. In other words, the
+ x and y, combined with the new surface size define in which
+ directions the surface's size changes. Setting anything other than 0
+ as x and y arguments is discouraged, and should instead be replaced
+ with using the separate wl_surface.offset request.
+
+ When the bound wl_surface version is 5 or higher, passing any
+ non-zero x or y is a protocol violation, and will result in an
+ 'invalid_offset' error being raised. The x and y arguments are ignored
+ and do not change the pending state. To achieve equivalent semantics,
+ use wl_surface.offset.
+
+ Surface contents are double-buffered state, see wl_surface.commit.
+
+ The initial surface contents are void; there is no content.
+ wl_surface.attach assigns the given wl_buffer as the pending
+ wl_buffer. wl_surface.commit makes the pending wl_buffer the new
+ surface contents, and the size of the surface becomes the size
+ calculated from the wl_buffer, as described above. After commit,
+ there is no pending buffer until the next attach.
+
+ Committing a pending wl_buffer allows the compositor to read the
+ pixels in the wl_buffer. The compositor may access the pixels at
+ any time after the wl_surface.commit request. When the compositor
+ will not access the pixels anymore, it will send the
+ wl_buffer.release event. Only after receiving wl_buffer.release,
+ the client may reuse the wl_buffer. A wl_buffer that has been
+ attached and then replaced by another attach instead of committed
+ will not receive a release event, and is not used by the
+ compositor.
+
+ If a pending wl_buffer has been committed to more than one wl_surface,
+ the delivery of wl_buffer.release events becomes undefined. A well
+ behaved client should not rely on wl_buffer.release events in this
+ case. Alternatively, a client could create multiple wl_buffer objects
+ from the same backing storage or use a protocol extension providing
+ per-commit release notifications.
+
+ Destroying the wl_buffer after wl_buffer.release does not change
+ the surface contents. Destroying the wl_buffer before wl_buffer.release
+ is allowed as long as the underlying buffer storage isn't re-used (this
+ can happen e.g. on client process termination). However, if the client
+ destroys the wl_buffer before receiving the wl_buffer.release event and
+ mutates the underlying buffer storage, the surface contents become
+ undefined immediately.
+
+ If wl_surface.attach is sent with a NULL wl_buffer, the
+ following wl_surface.commit will remove the surface content.
+
+ If a pending wl_buffer has been destroyed, the result is not specified.
+ Many compositors are known to remove the surface content on the following
+ wl_surface.commit, but this behaviour is not universal. Clients seeking to
+ maximise compatibility should not destroy pending buffers and should
+ ensure that they explicitly remove content from surfaces, even after
+ destroying buffers.
+
+
+
+
+
+
+
+
+ This request is used to describe the regions where the pending
+ buffer is different from the current surface contents, and where
+ the surface therefore needs to be repainted. The compositor
+ ignores the parts of the damage that fall outside of the surface.
+
+ Damage is double-buffered state, see wl_surface.commit.
+
+ The damage rectangle is specified in surface-local coordinates,
+ where x and y specify the upper left corner of the damage rectangle.
+
+ The initial value for pending damage is empty: no damage.
+ wl_surface.damage adds pending damage: the new pending damage
+ is the union of old pending damage and the given rectangle.
+
+ wl_surface.commit assigns pending damage as the current damage,
+ and clears pending damage. The server will clear the current
+ damage as it repaints the surface.
+
+ Note! New clients should not use this request. Instead damage can be
+ posted with wl_surface.damage_buffer which uses buffer coordinates
+ instead of surface coordinates.
+
+
+
+
+
+
+
+
+
+ Request a notification when it is a good time to start drawing a new
+ frame, by creating a frame callback. This is useful for throttling
+ redrawing operations, and driving animations.
+
+ When a client is animating on a wl_surface, it can use the 'frame'
+ request to get notified when it is a good time to draw and commit the
+ next frame of animation. If the client commits an update earlier than
+ that, it is likely that some updates will not make it to the display,
+ and the client is wasting resources by drawing too often.
+
+ The frame request will take effect on the next wl_surface.commit.
+ The notification will only be posted for one frame unless
+ requested again. For a wl_surface, the notifications are posted in
+ the order the frame requests were committed.
+
+ The server must send the notifications so that a client
+ will not send excessive updates, while still allowing
+ the highest possible update rate for clients that wait for the reply
+ before drawing again. The server should give some time for the client
+ to draw and commit after sending the frame callback events to let it
+ hit the next output refresh.
+
+ A server should avoid signaling the frame callbacks if the
+ surface is not visible in any way, e.g. the surface is off-screen,
+ or completely obscured by other opaque surfaces.
+
+ The object returned by this request will be destroyed by the
+ compositor after the callback is fired and as such the client must not
+ attempt to use it after that point.
+
+ The callback_data passed in the callback is the current time, in
+ milliseconds, with an undefined base.
+
+
+
+
+
+
+ This request sets the region of the surface that contains
+ opaque content.
+
+ The opaque region is an optimization hint for the compositor
+ that lets it optimize the redrawing of content behind opaque
+ regions. Setting an opaque region is not required for correct
+ behaviour, but marking transparent content as opaque will result
+ in repaint artifacts.
+
+ The opaque region is specified in surface-local coordinates.
+
+ The compositor ignores the parts of the opaque region that fall
+ outside of the surface.
+
+ Opaque region is double-buffered state, see wl_surface.commit.
+
+ wl_surface.set_opaque_region changes the pending opaque region.
+ wl_surface.commit copies the pending region to the current region.
+ Otherwise, the pending and current regions are never changed.
+
+ The initial value for an opaque region is empty. Setting the pending
+ opaque region has copy semantics, and the wl_region object can be
+ destroyed immediately. A NULL wl_region causes the pending opaque
+ region to be set to empty.
+
+
+
+
+
+
+ This request sets the region of the surface that can receive
+ pointer and touch events.
+
+ Input events happening outside of this region will try the next
+ surface in the server surface stack. The compositor ignores the
+ parts of the input region that fall outside of the surface.
+
+ The input region is specified in surface-local coordinates.
+
+ Input region is double-buffered state, see wl_surface.commit.
+
+ wl_surface.set_input_region changes the pending input region.
+ wl_surface.commit copies the pending region to the current region.
+ Otherwise the pending and current regions are never changed,
+ except cursor and icon surfaces are special cases, see
+ wl_pointer.set_cursor and wl_data_device.start_drag.
+
+ The initial value for an input region is infinite. That means the
+ whole surface will accept input. Setting the pending input region
+ has copy semantics, and the wl_region object can be destroyed
+ immediately. A NULL wl_region causes the input region to be set
+ to infinite.
+
+
+
+
+
+
+ Surface state (input, opaque, and damage regions, attached buffers,
+ etc.) is double-buffered. Protocol requests modify the pending state,
+ as opposed to the active state in use by the compositor.
+
+ All requests that need a commit to become effective are documented
+ to affect double-buffered state.
+
+ Other interfaces may add further double-buffered surface state.
+
+ A commit request atomically creates a Content Update (CU) from the
+ pending state, even if the pending state has not been touched. The
+ content update is placed at the end of a per-surface queue until it
+ becomes active. After commit, the new pending state is as documented for
+ each related request.
+
+ A CU is either a Desync Content Update (DCU) or a Sync Content Update
+ (SCU). If the surface is effectively synchronized at the commit request,
+ it is a SCU, otherwise a DCU.
+
+ When a surface transitions from effectively synchronized to effectively
+ desynchronized, all SCUs in its queue which are not reachable by any
+ DCU become DCUs and dependency edges from outside the queue to these CUs
+ are removed.
+
+ See wl_subsurface for the definition of 'effectively synchronized' and
+ 'effectively desynchronized'.
+
+ When a CU is placed in the queue, the CU has a dependency on the CU in
+ front of it and to the SCU at end of the queue of every direct child
+ surface if that SCU exists and does not have another dependent. This can
+ form a directed acyclic graph of CUs with dependencies as edges.
+
+ In addition to surface state, the CU can have constraints that must be
+ satisfied before it can be applied. Other interfaces may add CU
+ constraints.
+
+ All DCUs which do not have a SCU in front of themselves in their queue,
+ are candidates. If the graph that's reachable by a candidate does not
+ have any unsatisfied constraints, the entire graph must be applied
+ atomically.
+
+ When a CU is applied, the wl_buffer is applied before all other state.
+ This means that all coordinates in double-buffered state are relative to
+ the newly attached wl_buffers, except for wl_surface.attach itself. If
+ there is no newly attached wl_buffer, the coordinates are relative to
+ the previous content update.
+
+
+
+
+
+ This is emitted whenever a surface's creation, movement, or resizing
+ results in some part of it being within the scanout region of an
+ output.
+
+ Note that a surface may be overlapping with zero or more outputs.
+
+
+
+
+
+
+ This is emitted whenever a surface's creation, movement, or resizing
+ results in it no longer having any part of it within the scanout region
+ of an output.
+
+ Clients should not use the number of outputs the surface is on for frame
+ throttling purposes. The surface might be hidden even if no leave event
+ has been sent, and the compositor might expect new surface content
+ updates even if no enter event has been sent. The frame event should be
+ used instead.
+
+
+
+
+
+
+
+
+ This request sets the transformation that the client has already applied
+ to the content of the buffer. The accepted values for the transform
+ parameter are the values for wl_output.transform.
+
+ The compositor applies the inverse of this transformation whenever it
+ uses the buffer contents.
+
+ Buffer transform is double-buffered state, see wl_surface.commit.
+
+ A newly created surface has its buffer transformation set to normal.
+
+ wl_surface.set_buffer_transform changes the pending buffer
+ transformation. wl_surface.commit copies the pending buffer
+ transformation to the current one. Otherwise, the pending and current
+ values are never changed.
+
+ The purpose of this request is to allow clients to render content
+ according to the output transform, thus permitting the compositor to
+ use certain optimizations even if the display is rotated. Using
+ hardware overlays and scanning out a client buffer for fullscreen
+ surfaces are examples of such optimizations. Those optimizations are
+ highly dependent on the compositor implementation, so the use of this
+ request should be considered on a case-by-case basis.
+
+ Note that if the transform value includes 90 or 270 degree rotation,
+ the width of the buffer will become the surface height and the height
+ of the buffer will become the surface width.
+
+ If transform is not one of the values from the
+ wl_output.transform enum the invalid_transform protocol error
+ is raised.
+
+
+
+
+
+
+
+
+ This request sets an optional scaling factor on how the compositor
+ interprets the contents of the buffer attached to the window.
+
+ Buffer scale is double-buffered state, see wl_surface.commit.
+
+ A newly created surface has its buffer scale set to 1.
+
+ wl_surface.set_buffer_scale changes the pending buffer scale.
+ wl_surface.commit copies the pending buffer scale to the current one.
+ Otherwise, the pending and current values are never changed.
+
+ The purpose of this request is to allow clients to supply higher
+ resolution buffer data for use on high resolution outputs. It is
+ intended that you pick the same buffer scale as the scale of the
+ output that the surface is displayed on. This means the compositor
+ can avoid scaling when rendering the surface on that output.
+
+ Note that if the scale is larger than 1, then you have to attach
+ a buffer that is larger (by a factor of scale in each dimension)
+ than the desired surface size.
+
+ If scale is not greater than 0 the invalid_scale protocol error is
+ raised.
+
+
+
+
+
+
+
+ This request is used to describe the regions where the pending
+ buffer is different from the current surface contents, and where
+ the surface therefore needs to be repainted. The compositor
+ ignores the parts of the damage that fall outside of the surface.
+
+ Damage is double-buffered state, see wl_surface.commit.
+
+ The damage rectangle is specified in buffer coordinates,
+ where x and y specify the upper left corner of the damage rectangle.
+
+ The initial value for pending damage is empty: no damage.
+ wl_surface.damage_buffer adds pending damage: the new pending
+ damage is the union of old pending damage and the given rectangle.
+
+ wl_surface.commit assigns pending damage as the current damage,
+ and clears pending damage. The server will clear the current
+ damage as it repaints the surface.
+
+ This request differs from wl_surface.damage in only one way - it
+ takes damage in buffer coordinates instead of surface-local
+ coordinates. While this generally is more intuitive than surface
+ coordinates, it is especially desirable when using wp_viewport
+ or when a drawing library (like EGL) is unaware of buffer scale
+ and buffer transform.
+
+ Note: Because buffer transformation changes and damage requests may
+ be interleaved in the protocol stream, it is impossible to determine
+ the actual mapping between surface and buffer damage until
+ wl_surface.commit time. Therefore, compositors wishing to take both
+ kinds of damage into account will have to accumulate damage from the
+ two requests separately and only transform from one to the other
+ after receiving the wl_surface.commit.
+
+
+
+
+
+
+
+
+
+
+
+ The x and y arguments specify the location of the new pending
+ buffer's upper left corner, relative to the current buffer's upper
+ left corner, in surface-local coordinates. In other words, the
+ x and y, combined with the new surface size define in which
+ directions the surface's size changes.
+
+ The exact semantics of wl_surface.offset are role-specific. Refer to
+ the documentation of specific roles for more information.
+
+ Surface location offset is double-buffered state, see
+ wl_surface.commit.
+
+ This request is semantically equivalent to and the replaces the x and y
+ arguments in the wl_surface.attach request in wl_surface versions prior
+ to 5. See wl_surface.attach for details.
+
+
+
+
+
+
+
+
+
+ This event indicates the preferred buffer scale for this surface. It is
+ sent whenever the compositor's preference changes.
+
+ Before receiving this event the preferred buffer scale for this surface
+ is 1.
+
+ It is intended that scaling aware clients use this event to scale their
+ content and use wl_surface.set_buffer_scale to indicate the scale they
+ have rendered with. This allows clients to supply a higher detail
+ buffer.
+
+ The compositor shall emit a scale value greater than 0.
+
+
+
+
+
+
+ This event indicates the preferred buffer transform for this surface.
+ It is sent whenever the compositor's preference changes.
+
+ Before receiving this event the preferred buffer transform for this
+ surface is normal.
+
+ Applying this transformation to the surface buffer contents and using
+ wl_surface.set_buffer_transform might allow the compositor to use the
+ surface buffer more efficiently.
+
+
+
+
+
+
+
+ A seat is a group of keyboards, pointer and touch devices. This
+ object is published as a global during start up, or when such a
+ device is hot plugged. A seat typically has a pointer and
+ maintains a keyboard focus and a pointer focus.
+
+
+
+
+ This is a bitmask of capabilities this seat has; if a member is
+ set, then it is present on the seat.
+
+
+
+
+
+
+
+
+ These errors can be emitted in response to wl_seat requests.
+
+
+
+
+
+
+ This is sent on binding to the seat global or whenever a seat gains
+ or loses the pointer, keyboard or touch capabilities.
+ The argument is a capability enum containing the complete set of
+ capabilities this seat has.
+
+ When the pointer capability is added, a client may create a
+ wl_pointer object using the wl_seat.get_pointer request. This object
+ will receive pointer events until the capability is removed in the
+ future.
+
+ When the pointer capability is removed, a client should destroy the
+ wl_pointer objects associated with the seat where the capability was
+ removed, using the wl_pointer.release request. No further pointer
+ events will be received on these objects.
+
+ In some compositors, if a seat regains the pointer capability and a
+ client has a previously obtained wl_pointer object of version 4 or
+ less, that object may start sending pointer events again. This
+ behavior is considered a misinterpretation of the intended behavior
+ and must not be relied upon by the client. wl_pointer objects of
+ version 5 or later must not send events if created before the most
+ recent event notifying the client of an added pointer capability.
+
+ The above behavior also applies to wl_keyboard and wl_touch with the
+ keyboard and touch capabilities, respectively.
+
+
+
+
+
+
+ The ID provided will be initialized to the wl_pointer interface
+ for this seat.
+
+ This request only takes effect if the seat has the pointer
+ capability, or has had the pointer capability in the past.
+ It is a protocol violation to issue this request on a seat that has
+ never had the pointer capability. The missing_capability error will
+ be sent in this case.
+
+
+
+
+
+
+ The ID provided will be initialized to the wl_keyboard interface
+ for this seat.
+
+ This request only takes effect if the seat has the keyboard
+ capability, or has had the keyboard capability in the past.
+ It is a protocol violation to issue this request on a seat that has
+ never had the keyboard capability. The missing_capability error will
+ be sent in this case.
+
+
+
+
+
+
+ The ID provided will be initialized to the wl_touch interface
+ for this seat.
+
+ This request only takes effect if the seat has the touch
+ capability, or has had the touch capability in the past.
+ It is a protocol violation to issue this request on a seat that has
+ never had the touch capability. The missing_capability error will
+ be sent in this case.
+
+
+
+
+
+
+
+
+ In a multi-seat configuration the seat name can be used by clients to
+ help identify which physical devices the seat represents.
+
+ The seat name is a UTF-8 string with no convention defined for its
+ contents. Each name is unique among all wl_seat globals. The name is
+ only guaranteed to be unique for the current compositor instance.
+
+ The same seat names are used for all clients. Thus, the name can be
+ shared across processes to refer to a specific wl_seat global.
+
+ The name event is sent after binding to the seat global, and should be sent
+ before announcing capabilities. This event only sent once per seat object,
+ and the name does not change over the lifetime of the wl_seat global.
+
+ Compositors may re-use the same seat name if the wl_seat global is
+ destroyed and re-created later.
+
+
+
+
+
+
+
+
+ Using this request a client can tell the server that it is not going to
+ use the seat object anymore.
+
+
+
+
+
+
+
+ The wl_pointer interface represents one or more input devices,
+ such as mice, which control the pointer location and pointer_focus
+ of a seat.
+
+ The wl_pointer interface generates motion, enter and leave
+ events for the surfaces that the pointer is located over,
+ and button and axis events for button presses, button releases
+ and scrolling.
+
+
+
+
+
+
+
+
+ Set the pointer surface, i.e., the surface that contains the
+ pointer image (cursor). This request gives the surface the role
+ of a cursor. If the surface already has another role, it raises
+ a protocol error.
+
+ The cursor actually changes only if the pointer
+ focus for this device is one of the requesting client's surfaces
+ or the surface parameter is the current pointer surface. If
+ there was a previous surface set with this request it is
+ replaced. If surface is NULL, the pointer image is hidden.
+
+ The parameters hotspot_x and hotspot_y define the position of
+ the pointer surface relative to the pointer location. Its
+ top-left corner is always at (x, y) - (hotspot_x, hotspot_y),
+ where (x, y) are the coordinates of the pointer location, in
+ surface-local coordinates.
+
+ On wl_surface.offset requests to the pointer surface, hotspot_x
+ and hotspot_y are decremented by the x and y parameters
+ passed to the request. The offset must be applied by
+ wl_surface.commit as usual.
+
+ The hotspot can also be updated by passing the currently set
+ pointer surface to this request with new values for hotspot_x
+ and hotspot_y.
+
+ The input region is ignored for wl_surfaces with the role of
+ a cursor. When the use as a cursor ends, the wl_surface is
+ unmapped.
+
+ The serial parameter must match the latest wl_pointer.enter
+ serial number sent to the client. Otherwise the request will be
+ ignored.
+
+
+
+
+
+
+
+
+
+ Notification that this seat's pointer is focused on a certain
+ surface.
+
+ When a seat's focus enters a surface, the pointer image
+ is undefined and a client should respond to this event by setting
+ an appropriate pointer image with the set_cursor request.
+
+
+
+
+
+
+
+
+
+ Notification that this seat's pointer is no longer focused on
+ a certain surface.
+
+ The leave notification is sent before the enter notification
+ for the new focus.
+
+
+
+
+
+
+
+ Notification of pointer location change. The arguments
+ surface_x and surface_y are the location relative to the
+ focused surface.
+
+
+
+
+
+
+
+
+ Describes the physical state of a button that produced the button
+ event.
+
+
+
+
+
+
+
+ Mouse button click and release notifications.
+
+ The location of the click is given by the last motion or
+ enter event.
+ The time argument is a timestamp with millisecond
+ granularity, with an undefined base.
+
+ The button is a button code as defined in the Linux kernel's
+ linux/input-event-codes.h header file, e.g. BTN_LEFT.
+
+ Any 16-bit button code value is reserved for future additions to the
+ kernel's event code list. All other button codes above 0xFFFF are
+ currently undefined but may be used in future versions of this
+ protocol.
+
+
+
+
+
+
+
+
+
+ Describes the axis types of scroll events.
+
+
+
+
+
+
+
+ Scroll and other axis notifications.
+
+ For scroll events (vertical and horizontal scroll axes), the
+ value parameter is the length of a vector along the specified
+ axis in a coordinate space identical to those of motion events,
+ representing a relative movement along the specified axis.
+
+ For devices that support movements non-parallel to axes multiple
+ axis events will be emitted.
+
+ When applicable, for example for touch pads, the server can
+ choose to emit scroll events where the motion vector is
+ equivalent to a motion event vector.
+
+ When applicable, a client can transform its content relative to the
+ scroll distance.
+
+
+
+
+
+
+
+
+
+
+ Using this request a client can tell the server that it is not going to
+ use the pointer object anymore.
+
+ This request destroys the pointer proxy object, so clients must not call
+ wl_pointer_destroy() after using this request.
+
+
+
+
+
+
+
+ Indicates the end of a set of events that logically belong together.
+ A client is expected to accumulate the data in all events within the
+ frame before proceeding.
+
+ All wl_pointer events before a wl_pointer.frame event belong
+ logically together. For example, in a diagonal scroll motion the
+ compositor will send an optional wl_pointer.axis_source event, two
+ wl_pointer.axis events (horizontal and vertical) and finally a
+ wl_pointer.frame event. The client may use this information to
+ calculate a diagonal vector for scrolling.
+
+ When multiple wl_pointer.axis events occur within the same frame,
+ the motion vector is the combined motion of all events.
+ When a wl_pointer.axis and a wl_pointer.axis_stop event occur within
+ the same frame, this indicates that axis movement in one axis has
+ stopped but continues in the other axis.
+ When multiple wl_pointer.axis_stop events occur within the same
+ frame, this indicates that these axes stopped in the same instance.
+
+ A wl_pointer.frame event is sent for every logical event group,
+ even if the group only contains a single wl_pointer event.
+ Specifically, a client may get a sequence: motion, frame, button,
+ frame, axis, frame, axis_stop, frame.
+
+ The wl_pointer.enter and wl_pointer.leave events are logical events
+ generated by the compositor and not the hardware. These events are
+ also grouped by a wl_pointer.frame. When a pointer moves from one
+ surface to another, a compositor should group the
+ wl_pointer.leave event within the same wl_pointer.frame.
+ However, a client must not rely on wl_pointer.leave and
+ wl_pointer.enter being in the same wl_pointer.frame.
+ Compositor-specific policies may require the wl_pointer.leave and
+ wl_pointer.enter event being split across multiple wl_pointer.frame
+ groups.
+
+
+
+
+
+ Describes the source types for axis events. This indicates to the
+ client how an axis event was physically generated; a client may
+ adjust the user interface accordingly. For example, scroll events
+ from a "finger" source may be in a smooth coordinate space with
+ kinetic scrolling whereas a "wheel" source may be in discrete steps
+ of a number of lines.
+
+ The "continuous" axis source is a device generating events in a
+ continuous coordinate space, but using something other than a
+ finger. One example for this source is button-based scrolling where
+ the vertical motion of a device is converted to scroll events while
+ a button is held down.
+
+ The "wheel tilt" axis source indicates that the actual device is a
+ wheel but the scroll event is not caused by a rotation but a
+ (usually sideways) tilt of the wheel.
+
+
+
+
+
+
+
+
+
+ Source information for scroll and other axes.
+
+ This event does not occur on its own. It is sent before a
+ wl_pointer.frame event and carries the source information for
+ all events within that frame.
+
+ The source specifies how this event was generated. If the source is
+ wl_pointer.axis_source.finger, a wl_pointer.axis_stop event will be
+ sent when the user lifts the finger off the device.
+
+ If the source is wl_pointer.axis_source.wheel,
+ wl_pointer.axis_source.wheel_tilt or
+ wl_pointer.axis_source.continuous, a wl_pointer.axis_stop event may
+ or may not be sent. Whether a compositor sends an axis_stop event
+ for these sources is hardware-specific and implementation-dependent;
+ clients must not rely on receiving an axis_stop event for these
+ scroll sources and should treat scroll sequences from these scroll
+ sources as unterminated by default.
+
+ This event is optional. If the source is unknown for a particular
+ axis event sequence, no event is sent.
+ Only one wl_pointer.axis_source event is permitted per frame.
+
+ The order of wl_pointer.axis_discrete and wl_pointer.axis_source is
+ not guaranteed.
+
+
+
+
+
+
+ Stop notification for scroll and other axes.
+
+ For some wl_pointer.axis_source types, a wl_pointer.axis_stop event
+ is sent to notify a client that the axis sequence has terminated.
+ This enables the client to implement kinetic scrolling.
+ See the wl_pointer.axis_source documentation for information on when
+ this event may be generated.
+
+ Any wl_pointer.axis events with the same axis_source after this
+ event should be considered as the start of a new axis motion.
+
+ The timestamp is to be interpreted identical to the timestamp in the
+ wl_pointer.axis event. The timestamp value may be the same as a
+ preceding wl_pointer.axis event.
+
+
+
+
+
+
+
+ Discrete step information for scroll and other axes.
+
+ This event carries the axis value of the wl_pointer.axis event in
+ discrete steps (e.g. mouse wheel clicks).
+
+ This event is deprecated with wl_pointer version 8 - this event is not
+ sent to clients supporting version 8 or later.
+
+ This event does not occur on its own, it is coupled with a
+ wl_pointer.axis event that represents this axis value on a
+ continuous scale. The protocol guarantees that each axis_discrete
+ event is always followed by exactly one axis event with the same
+ axis number within the same wl_pointer.frame. Note that the protocol
+ allows for other events to occur between the axis_discrete and
+ its coupled axis event, including other axis_discrete or axis
+ events. A wl_pointer.frame must not contain more than one axis_discrete
+ event per axis type.
+
+ This event is optional; continuous scrolling devices
+ like two-finger scrolling on touchpads do not have discrete
+ steps and do not generate this event.
+
+ The discrete value carries the directional information. e.g. a value
+ of -2 is two steps towards the negative direction of this axis.
+
+ The axis number is identical to the axis number in the associated
+ axis event.
+
+ The order of wl_pointer.axis_discrete and wl_pointer.axis_source is
+ not guaranteed.
+
+
+
+
+
+
+
+ Discrete high-resolution scroll information.
+
+ This event carries high-resolution wheel scroll information,
+ with each multiple of 120 representing one logical scroll step
+ (a wheel detent). For example, an axis_value120 of 30 is one quarter of
+ a logical scroll step in the positive direction, a value120 of
+ -240 are two logical scroll steps in the negative direction within the
+ same hardware event.
+ Clients that rely on discrete scrolling should accumulate the
+ value120 to multiples of 120 before processing the event.
+
+ The value120 must not be zero.
+
+ This event replaces the wl_pointer.axis_discrete event in clients
+ supporting wl_pointer version 8 or later.
+
+ Where a wl_pointer.axis_source event occurs in the same
+ wl_pointer.frame, the axis source applies to this event.
+
+ The order of wl_pointer.axis_value120 and wl_pointer.axis_source is
+ not guaranteed.
+
+
+
+
+
+
+
+
+
+ This specifies the direction of the physical motion that caused a
+ wl_pointer.axis event, relative to the wl_pointer.axis direction.
+
+
+
+
+
+
+
+ Relative directional information of the entity causing the axis
+ motion.
+
+ For a wl_pointer.axis event, the wl_pointer.axis_relative_direction
+ event specifies the movement direction of the entity causing the
+ wl_pointer.axis event. For example:
+ - if a user's fingers on a touchpad move down and this
+ causes a wl_pointer.axis vertical_scroll down event, the physical
+ direction is 'identical'
+ - if a user's fingers on a touchpad move down and this causes a
+ wl_pointer.axis vertical_scroll up scroll up event ('natural
+ scrolling'), the physical direction is 'inverted'.
+
+ A client may use this information to adjust scroll motion of
+ components. Specifically, enabling natural scrolling causes the
+ content to change direction compared to traditional scrolling.
+ Some widgets like volume control sliders should usually match the
+ physical direction regardless of whether natural scrolling is
+ active. This event enables clients to match the scroll direction of
+ a widget to the physical direction.
+
+ This event does not occur on its own, it is coupled with a
+ wl_pointer.axis event that represents this axis value.
+ The protocol guarantees that each axis_relative_direction event is
+ always followed by exactly one axis event with the same
+ axis number within the same wl_pointer.frame. Note that the protocol
+ allows for other events to occur between the axis_relative_direction
+ and its coupled axis event.
+
+ The axis number is identical to the axis number in the associated
+ axis event.
+
+ The order of wl_pointer.axis_relative_direction,
+ wl_pointer.axis_discrete and wl_pointer.axis_source is not
+ guaranteed.
+
+
+
+
+
+
+
+
+ The wl_keyboard interface represents one or more keyboards
+ associated with a seat.
+
+ Each wl_keyboard has the following logical state:
+
+ - an active surface (possibly null),
+ - the keys currently logically down,
+ - the active modifiers,
+ - the active group.
+
+ By default, the active surface is null, the keys currently logically down
+ are empty, the active modifiers and the active group are 0.
+
+
+
+
+ This specifies the format of the keymap provided to the
+ client with the wl_keyboard.keymap event.
+
+
+
+
+
+
+
+ This event provides a file descriptor to the client which can be
+ memory-mapped in read-only mode to provide a keyboard mapping
+ description.
+
+ From version 7 onwards, the fd must be mapped with MAP_PRIVATE by
+ the recipient, as MAP_SHARED may fail.
+
+
+
+
+
+
+
+
+ Notification that this seat's keyboard focus is on a certain
+ surface.
+
+ The compositor must send the wl_keyboard.modifiers event after this
+ event.
+
+ In the wl_keyboard logical state, this event sets the active surface to
+ the surface argument and the keys currently logically down to the keys
+ in the keys argument. The compositor must not send this event if the
+ wl_keyboard already had an active surface immediately before this event.
+
+ Clients should not use the list of pressed keys to emulate key-press
+ events. The order of keys in the list is unspecified.
+
+
+
+
+
+
+
+
+ Notification that this seat's keyboard focus is no longer on
+ a certain surface.
+
+ The leave notification is sent before the enter notification
+ for the new focus.
+
+ In the wl_keyboard logical state, this event resets all values to their
+ defaults. The compositor must not send this event if the active surface
+ of the wl_keyboard was not equal to the surface argument immediately
+ before this event.
+
+
+
+
+
+
+
+ Describes the physical state of a key that produced the key event.
+
+ Since version 10, the key can be in a "repeated" pseudo-state which
+ means the same as "pressed", but is used to signal repetition in the
+ key event.
+
+ The key may only enter the repeated state after entering the pressed
+ state and before entering the released state. This event may be
+ generated multiple times while the key is down.
+
+
+
+
+
+
+
+
+ A key was pressed or released.
+ The time argument is a timestamp with millisecond
+ granularity, with an undefined base.
+
+ The key is a platform-specific key code that can be interpreted
+ by feeding it to the keyboard mapping (see the keymap event).
+
+ If this event produces a change in modifiers, then the resulting
+ wl_keyboard.modifiers event must be sent after this event.
+
+ In the wl_keyboard logical state, this event adds the key to the keys
+ currently logically down (if the state argument is pressed) or removes
+ the key from the keys currently logically down (if the state argument is
+ released). The compositor must not send this event if the wl_keyboard
+ did not have an active surface immediately before this event. The
+ compositor must not send this event if state is pressed (resp. released)
+ and the key was already logically down (resp. was not logically down)
+ immediately before this event.
+
+ Since version 10, compositors may send key events with the "repeated"
+ key state when a wl_keyboard.repeat_info event with a rate argument of
+ 0 has been received. This allows the compositor to take over the
+ responsibility of key repetition.
+
+
+
+
+
+
+
+
+
+ Notifies clients that the modifier and/or group state has
+ changed, and it should update its local state.
+
+ The compositor may send this event without a surface of the client
+ having keyboard focus, for example to tie modifier information to
+ pointer focus instead. If a modifier event with pressed modifiers is sent
+ without a prior enter event, the client can assume the modifier state is
+ valid until it receives the next wl_keyboard.modifiers event. In order to
+ reset the modifier state again, the compositor can send a
+ wl_keyboard.modifiers event with no pressed modifiers.
+
+ In the wl_keyboard logical state, this event updates the modifiers and
+ group.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Informs the client about the keyboard's repeat rate and delay.
+
+ This event is sent as soon as the wl_keyboard object has been created,
+ and is guaranteed to be received by the client before any key press
+ event.
+
+ Negative values for either rate or delay are illegal. A rate of zero
+ will disable any repeating (regardless of the value of delay).
+
+ This event can be sent later on as well with a new value if necessary,
+ so clients should continue listening for the event past the creation
+ of wl_keyboard.
+
+
+
+
+
+
+
+
+ The wl_touch interface represents a touchscreen
+ associated with a seat.
+
+ Touch interactions can consist of one or more contacts.
+ For each contact, a series of events is generated, starting
+ with a down event, followed by zero or more motion events,
+ and ending with an up event. Events relating to the same
+ contact point can be identified by the ID of the sequence.
+
+
+
+
+ A new touch point has appeared on the surface. This touch point is
+ assigned a unique ID. Future events from this touch point reference
+ this ID. The ID ceases to be valid after a touch up event and may be
+ reused in the future.
+
+
+
+
+
+
+
+
+
+
+
+ The touch point has disappeared. No further events will be sent for
+ this touch point and the touch point's ID is released and may be
+ reused in a future touch down event.
+
+
+
+
+
+
+
+
+ A touch point has changed coordinates.
+
+
+
+
+
+
+
+
+
+ Indicates the end of a set of events that logically belong together.
+ A client is expected to accumulate the data in all events within the
+ frame before proceeding.
+
+ A wl_touch.frame terminates at least one event but otherwise no
+ guarantee is provided about the set of events within a frame. A client
+ must assume that any state not updated in a frame is unchanged from the
+ previously known state.
+
+
+
+
+
+ Sent if the compositor decides the touch stream is a global
+ gesture. No further events are sent to the clients from that
+ particular gesture. Touch cancellation applies to all touch points
+ currently active on this client's surface. The client is
+ responsible for finalizing the touch points, future touch points on
+ this surface may reuse the touch point ID.
+
+ No frame event is required after the cancel event.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sent when a touchpoint has changed its shape.
+
+ This event does not occur on its own. It is sent before a
+ wl_touch.frame event and carries the new shape information for
+ any previously reported, or new touch points of that frame.
+
+ Other events describing the touch point such as wl_touch.down,
+ wl_touch.motion or wl_touch.orientation may be sent within the
+ same wl_touch.frame. A client should treat these events as a single
+ logical touch point update. The order of wl_touch.shape,
+ wl_touch.orientation and wl_touch.motion is not guaranteed.
+ A wl_touch.down event is guaranteed to occur before the first
+ wl_touch.shape event for this touch ID but both events may occur within
+ the same wl_touch.frame.
+
+ A touchpoint shape is approximated by an ellipse through the major and
+ minor axis length. The major axis length describes the longer diameter
+ of the ellipse, while the minor axis length describes the shorter
+ diameter. Major and minor are orthogonal and both are specified in
+ surface-local coordinates. The center of the ellipse is always at the
+ touchpoint location as reported by wl_touch.down or wl_touch.move.
+
+ This event is only sent by the compositor if the touch device supports
+ shape reports. The client has to make reasonable assumptions about the
+ shape if it did not receive this event.
+
+
+
+
+
+
+
+
+ Sent when a touchpoint has changed its orientation.
+
+ This event does not occur on its own. It is sent before a
+ wl_touch.frame event and carries the new shape information for
+ any previously reported, or new touch points of that frame.
+
+ Other events describing the touch point such as wl_touch.down,
+ wl_touch.motion or wl_touch.shape may be sent within the
+ same wl_touch.frame. A client should treat these events as a single
+ logical touch point update. The order of wl_touch.shape,
+ wl_touch.orientation and wl_touch.motion is not guaranteed.
+ A wl_touch.down event is guaranteed to occur before the first
+ wl_touch.orientation event for this touch ID but both events may occur
+ within the same wl_touch.frame.
+
+ The orientation describes the clockwise angle of a touchpoint's major
+ axis to the positive surface y-axis and is normalized to the -180 to
+ +180 degree range. The granularity of orientation depends on the touch
+ device, some devices only support binary rotation values between 0 and
+ 90 degrees.
+
+ This event is only sent by the compositor if the touch device supports
+ orientation reports.
+
+
+
+
+
+
+
+
+ An output describes part of the compositor geometry. The
+ compositor works in the 'compositor coordinate system' and an
+ output corresponds to a rectangular area in that space that is
+ actually visible. This typically corresponds to a monitor that
+ displays part of the compositor space. This object is published
+ as global during start up, or when a monitor is hotplugged.
+
+
+
+
+ This enumeration describes how the physical
+ pixels on an output are laid out.
+
+
+
+
+
+
+
+
+
+
+
+ This describes transformations that clients and compositors apply to
+ buffer contents.
+
+ The flipped values correspond to an initial flip around a
+ vertical axis followed by rotation.
+
+ The purpose is mainly to allow clients to render accordingly and
+ tell the compositor, so that for fullscreen surfaces, the
+ compositor will still be able to scan out directly from client
+ surfaces.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The geometry event describes geometric properties of the output.
+ The event is sent when binding to the output object and whenever
+ any of the properties change.
+
+ The physical size can be set to zero if it doesn't make sense for this
+ output (e.g. for projectors or virtual outputs).
+
+ The geometry event will be followed by a done event (starting from
+ version 2).
+
+ Clients should use wl_surface.preferred_buffer_transform instead of the
+ transform advertised by this event to find the preferred buffer
+ transform to use for a surface.
+
+ Note: wl_output only advertises partial information about the output
+ position and identification. Some compositors, for instance those not
+ implementing a desktop-style output layout or those exposing virtual
+ outputs, might fake this information. Instead of using x and y, clients
+ should use xdg_output.logical_position. Instead of using make and model,
+ clients should use name and description.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ These flags describe properties of an output mode.
+ They are used in the flags bitfield of the mode event.
+
+
+
+
+
+
+
+ The mode event describes an available mode for the output.
+
+ The event is sent when binding to the output object and there
+ will always be one mode, the current mode. The event is sent
+ again if an output changes mode, for the mode that is now
+ current. In other words, the current mode is always the last
+ mode that was received with the current flag set.
+
+ Non-current modes are deprecated. A compositor can decide to only
+ advertise the current mode and never send other modes. Clients
+ should not rely on non-current modes.
+
+ The size of a mode is given in physical hardware units of
+ the output device. This is not necessarily the same as
+ the output size in the global compositor space. For instance,
+ the output may be scaled, as described in wl_output.scale,
+ or transformed, as described in wl_output.transform. Clients
+ willing to retrieve the output size in the global compositor
+ space should use xdg_output.logical_size instead.
+
+ The vertical refresh rate can be set to zero if it doesn't make
+ sense for this output (e.g. for virtual outputs).
+
+ The mode event will be followed by a done event (starting from
+ version 2).
+
+ Clients should not use the refresh rate to schedule frames. Instead,
+ they should use the wl_surface.frame event or the presentation-time
+ protocol.
+
+ Note: this information is not always meaningful for all outputs. Some
+ compositors, such as those exposing virtual outputs, might fake the
+ refresh rate or the size.
+
+
+
+
+
+
+
+
+
+
+
+ This event is sent after all other properties have been
+ sent after binding to the output object and after any
+ other property changes done after that. This allows
+ changes to the output properties to be seen as
+ atomic, even if they happen via multiple events.
+
+
+
+
+
+ This event contains scaling geometry information
+ that is not in the geometry event. It may be sent after
+ binding the output object or if the output scale changes
+ later. The compositor will emit a non-zero, positive
+ value for scale. If it is not sent, the client should
+ assume a scale of 1.
+
+ A scale larger than 1 means that the compositor will
+ automatically scale surface buffers by this amount
+ when rendering. This is used for very high resolution
+ displays where applications rendering at the native
+ resolution would be too small to be legible.
+
+ Clients should use wl_surface.preferred_buffer_scale
+ instead of this event to find the preferred buffer
+ scale to use for a surface.
+
+ The scale event will be followed by a done event.
+
+
+
+
+
+
+
+
+ Using this request a client can tell the server that it is not going to
+ use the output object anymore.
+
+
+
+
+
+
+
+ Many compositors will assign user-friendly names to their outputs, show
+ them to the user, allow the user to refer to an output, etc. The client
+ may wish to know this name as well to offer the user similar behaviors.
+
+ The name is a UTF-8 string with no convention defined for its contents.
+ Each name is unique among all wl_output globals. The name is only
+ guaranteed to be unique for the compositor instance.
+
+ The same output name is used for all clients for a given wl_output
+ global. Thus, the name can be shared across processes to refer to a
+ specific wl_output global.
+
+ The name is not guaranteed to be persistent across sessions, thus cannot
+ be used to reliably identify an output in e.g. configuration files.
+
+ Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do
+ not assume that the name is a reflection of an underlying DRM connector,
+ X11 connection, etc.
+
+ The name event is sent after binding the output object. This event is
+ only sent once per output object, and the name does not change over the
+ lifetime of the wl_output global.
+
+ Compositors may re-use the same output name if the wl_output global is
+ destroyed and re-created later. Compositors should avoid re-using the
+ same name if possible.
+
+ The name event will be followed by a done event.
+
+
+
+
+
+
+ Many compositors can produce human-readable descriptions of their
+ outputs. The client may wish to know this description as well, e.g. for
+ output selection purposes.
+
+ The description is a UTF-8 string with no convention defined for its
+ contents. The description is not guaranteed to be unique among all
+ wl_output globals. Examples might include 'Foocorp 11" Display' or
+ 'Virtual X11 output via :1'.
+
+ The description event is sent after binding the output object and
+ whenever the description changes. The description is optional, and may
+ not be sent at all.
+
+ The description event will be followed by a done event.
+
+
+
+
+
+
+
+ A region object describes an area.
+
+ Region objects are used to describe the opaque and input
+ regions of a surface.
+
+
+
+
+ Destroy the region. This will invalidate the object ID.
+
+
+
+
+
+ Add the specified rectangle to the region.
+
+
+
+
+
+
+
+
+
+ Subtract the specified rectangle from the region.
+
+
+
+
+
+
+
+
+
+
+ The global interface exposing sub-surface compositing capabilities.
+ A wl_surface, that has sub-surfaces associated, is called the
+ parent surface. Sub-surfaces can be arbitrarily nested and create
+ a tree of sub-surfaces.
+
+ The root surface in a tree of sub-surfaces is the main
+ surface. The main surface cannot be a sub-surface, because
+ sub-surfaces must always have a parent.
+
+ A main surface with its sub-surfaces forms a (compound) window.
+ For window management purposes, this set of wl_surface objects is
+ to be considered as a single window, and it should also behave as
+ such.
+
+ The aim of sub-surfaces is to offload some of the compositing work
+ within a window from clients to the compositor. A prime example is
+ a video player with decorations and video in separate wl_surface
+ objects. This should allow the compositor to pass YUV video buffer
+ processing to dedicated overlay hardware when possible.
+
+
+
+
+ Informs the server that the client will not be using this
+ protocol object anymore. This does not affect any other
+ objects, wl_subsurface objects included.
+
+
+
+
+
+
+
+
+
+
+ Create a sub-surface interface for the given surface, and
+ associate it with the given parent surface. This turns a
+ plain wl_surface into a sub-surface.
+
+ The to-be sub-surface must not already have another role, and it
+ must not have an existing wl_subsurface object. Otherwise the
+ bad_surface protocol error is raised.
+
+ Adding sub-surfaces to a parent is a double-buffered operation on the
+ parent (see wl_surface.commit). The effect of adding a sub-surface
+ becomes visible on the next time the state of the parent surface is
+ applied.
+
+ The parent surface must not be one of the child surface's descendants,
+ and the parent must be different from the child surface, otherwise the
+ bad_parent protocol error is raised.
+
+ This request modifies the behaviour of wl_surface.commit request on
+ the sub-surface, see the documentation on wl_subsurface interface.
+
+
+
+
+
+
+
+
+
+ An additional interface to a wl_surface object, which has been
+ made a sub-surface. A sub-surface has one parent surface. A
+ sub-surface's size and position are not limited to that of the parent.
+ Particularly, a sub-surface is not automatically clipped to its
+ parent's area.
+
+ A sub-surface becomes mapped, when a non-NULL wl_buffer is applied
+ and the parent surface is mapped. The order of which one happens
+ first is irrelevant. A sub-surface is hidden if the parent becomes
+ hidden, or if a NULL wl_buffer is applied. These rules apply
+ recursively through the tree of surfaces.
+
+ A sub-surface can be in one of two modes. The possible modes are
+ synchronized and desynchronized, see methods wl_subsurface.set_sync and
+ wl_subsurface.set_desync.
+
+ The main surface can be thought to be always in desynchronized mode,
+ since it does not have a parent in the sub-surfaces sense.
+
+ Even if a sub-surface is in desynchronized mode, it will behave as
+ in synchronized mode, if its parent surface behaves as in
+ synchronized mode. This rule is applied recursively throughout the
+ tree of surfaces. This means, that one can set a sub-surface into
+ synchronized mode, and then assume that all its child and grand-child
+ sub-surfaces are synchronized, too, without explicitly setting them.
+
+ If a surface behaves as in synchronized mode, it is effectively
+ synchronized, otherwise it is effectively desynchronized.
+
+ A sub-surface is initially in the synchronized mode.
+
+ The wl_subsurface interface has requests which modify double-buffered
+ state of the parent surface (wl_subsurface.set_position, .place_above and
+ .place_below).
+
+ Destroying a sub-surface takes effect immediately. If you need to
+ synchronize the removal of a sub-surface to the parent surface update,
+ unmap the sub-surface first by attaching a NULL wl_buffer, update parent,
+ and then destroy the sub-surface.
+
+ If the parent wl_surface object is destroyed, the sub-surface is
+ unmapped.
+
+ A sub-surface never has the keyboard focus of any seat.
+
+ The wl_surface.offset request is ignored: clients must use set_position
+ instead to move the sub-surface.
+
+
+
+
+ The sub-surface interface is removed from the wl_surface object
+ that was turned into a sub-surface with a
+ wl_subcompositor.get_subsurface request. The wl_surface's association
+ to the parent is deleted. The wl_surface is unmapped immediately.
+
+
+
+
+
+
+
+
+
+ This sets the position of the sub-surface, relative to the parent
+ surface.
+
+ The sub-surface will be moved so that its origin (top left
+ corner pixel) will be at the location x, y of the parent surface
+ coordinate system. The coordinates are not restricted to the parent
+ surface area. Negative values are allowed.
+
+ The initial position is 0, 0.
+
+ Position is double-buffered state on the parent surface, see
+ wl_subsurface and wl_surface.commit for more information.
+
+
+
+
+
+
+
+ This sub-surface is taken from the stack, and put back just
+ above the reference surface, changing the z-order of the sub-surfaces.
+ The reference surface must be one of the sibling surfaces, or the
+ parent surface. Using any other surface, including this sub-surface,
+ will cause a protocol error.
+
+ A new sub-surface is initially added as the top-most in the stack
+ of its siblings and parent.
+
+ Z-order is double-buffered state on the parent surface, see
+ wl_subsurface and wl_surface.commit for more information.
+
+
+
+
+
+
+ The sub-surface is placed just below the reference surface.
+
+ See wl_subsurface.place_above.
+
+
+
+
+
+
+ Change the commit behaviour of the sub-surface to synchronized
+ mode.
+
+ See wl_subsurface and wl_surface.commit for more information.
+
+
+
+
+
+ Change the commit behaviour of the sub-surface to desynchronized
+ mode.
+
+ See wl_subsurface and wl_surface.commit for more information.
+
+
+
+
+
+
+ This global fixes problems with other core-protocol interfaces that
+ cannot be fixed in these interfaces themselves.
+
+
+
+
+
+
+
+
+ This request destroys a wl_registry object.
+
+ The client should no longer use the wl_registry after making this
+ request.
+
+ The compositor will emit a wl_display.delete_id event with the object ID
+ of the registry and will no longer emit any events on the registry. The
+ client should re-use the object ID once it receives the
+ wl_display.delete_id event.
+
+
+
+
+
+
diff --git a/protocols/xdg-shell.xml b/protocols/xdg-shell.xml
new file mode 100644
index 0000000..39ecf8a
--- /dev/null
+++ b/protocols/xdg-shell.xml
@@ -0,0 +1,1418 @@
+
+
+
+
+ Copyright © 2008-2013 Kristian Høgsberg
+ Copyright © 2013 Rafael Antognolli
+ Copyright © 2013 Jasper St. Pierre
+ Copyright © 2010-2013 Intel Corporation
+ Copyright © 2015-2017 Samsung Electronics Co., Ltd
+ Copyright © 2015-2017 Red Hat Inc.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+
+ The xdg_wm_base interface is exposed as a global object enabling clients
+ to turn their wl_surfaces into windows in a desktop environment. It
+ defines the basic functionality needed for clients and the compositor to
+ create windows that can be dragged, resized, maximized, etc, as well as
+ creating transient windows such as popup menus.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Destroy this xdg_wm_base object.
+
+ Destroying a bound xdg_wm_base object while there are surfaces
+ still alive created by this xdg_wm_base object instance is illegal
+ and will result in a defunct_surfaces error.
+
+
+
+
+
+ Create a positioner object. A positioner object is used to position
+ surfaces relative to some parent surface. See the interface description
+ and xdg_surface.get_popup for details.
+
+
+
+
+
+
+ This creates an xdg_surface for the given surface. While xdg_surface
+ itself is not a role, the corresponding surface may only be assigned
+ a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is
+ illegal to create an xdg_surface for a wl_surface which already has an
+ assigned role and this will result in a role error.
+
+ This creates an xdg_surface for the given surface. An xdg_surface is
+ used as basis to define a role to a given surface, such as xdg_toplevel
+ or xdg_popup. It also manages functionality shared between xdg_surface
+ based surface roles.
+
+ See the documentation of xdg_surface for more details about what an
+ xdg_surface is and how it is used.
+
+
+
+
+
+
+
+ A client must respond to a ping event with a pong request or
+ the client may be deemed unresponsive. See xdg_wm_base.ping
+ and xdg_wm_base.error.unresponsive.
+
+
+
+
+
+
+ The ping event asks the client if it's still alive. Pass the
+ serial specified in the event back to the compositor by sending
+ a "pong" request back with the specified serial. See xdg_wm_base.pong.
+
+ Compositors can use this to determine if the client is still
+ alive. It's unspecified what will happen if the client doesn't
+ respond to the ping request, or in what timeframe. Clients should
+ try to respond in a reasonable amount of time. The “unresponsive”
+ error is provided for compositors that wish to disconnect unresponsive
+ clients.
+
+ A compositor is free to ping in any way it wants, but a client must
+ always respond to any xdg_wm_base object it created.
+
+
+
+
+
+
+
+ The xdg_positioner provides a collection of rules for the placement of a
+ child surface relative to a parent surface. Rules can be defined to ensure
+ the child surface remains within the visible area's borders, and to
+ specify how the child surface changes its position, such as sliding along
+ an axis, or flipping around a rectangle. These positioner-created rules are
+ constrained by the requirement that a child surface must intersect with or
+ be at least partially adjacent to its parent surface.
+
+ See the various requests for details about possible rules.
+
+ At the time of the request, the compositor makes a copy of the rules
+ specified by the xdg_positioner. Thus, after the request is complete the
+ xdg_positioner object can be destroyed or reused; further changes to the
+ object will have no effect on previous usages.
+
+ For an xdg_positioner object to be considered complete, it must have a
+ non-zero size set by set_size, and a non-zero anchor rectangle set by
+ set_anchor_rect. Passing an incomplete xdg_positioner object when
+ positioning a surface raises an invalid_positioner error.
+
+
+
+
+
+
+
+
+ Notify the compositor that the xdg_positioner will no longer be used.
+
+
+
+
+
+ Set the size of the surface that is to be positioned with the positioner
+ object. The size is in surface-local coordinates and corresponds to the
+ window geometry. See xdg_surface.set_window_geometry.
+
+ If a zero or negative size is set the invalid_input error is raised.
+
+
+
+
+
+
+
+ Specify the anchor rectangle within the parent surface that the child
+ surface will be placed relative to. The rectangle is relative to the
+ window geometry as defined by xdg_surface.set_window_geometry of the
+ parent surface.
+
+ When the xdg_positioner object is used to position a child surface, the
+ anchor rectangle may not extend outside the window geometry of the
+ positioned child's parent surface.
+
+ If a negative size is set the invalid_input error is raised.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Defines the anchor point for the anchor rectangle. The specified anchor
+ is used derive an anchor point that the child surface will be
+ positioned relative to. If a corner anchor is set (e.g. 'top_left' or
+ 'bottom_right'), the anchor point will be at the specified corner;
+ otherwise, the derived anchor point will be centered on the specified
+ edge, or in the center of the anchor rectangle if no edge is specified.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Defines in what direction a surface should be positioned, relative to
+ the anchor point of the parent surface. If a corner gravity is
+ specified (e.g. 'bottom_right' or 'top_left'), then the child surface
+ will be placed towards the specified gravity; otherwise, the child
+ surface will be centered over the anchor point on any axis that had no
+ gravity specified. If the gravity is not in the ‘gravity’ enum, an
+ invalid_input error is raised.
+
+
+
+
+
+
+ The constraint adjustment value define ways the compositor will adjust
+ the position of the surface, if the unadjusted position would result
+ in the surface being partly constrained.
+
+ Whether a surface is considered 'constrained' is left to the compositor
+ to determine. For example, the surface may be partly outside the
+ compositor's defined 'work area', thus necessitating the child surface's
+ position be adjusted until it is entirely inside the work area.
+
+ The adjustments can be combined, according to a defined precedence: 1)
+ Flip, 2) Slide, 3) Resize.
+
+
+
+ Don't alter the surface position even if it is constrained on some
+ axis, for example partially outside the edge of an output.
+
+
+
+
+ Slide the surface along the x axis until it is no longer constrained.
+
+ First try to slide towards the direction of the gravity on the x axis
+ until either the edge in the opposite direction of the gravity is
+ unconstrained or the edge in the direction of the gravity is
+ constrained.
+
+ Then try to slide towards the opposite direction of the gravity on the
+ x axis until either the edge in the direction of the gravity is
+ unconstrained or the edge in the opposite direction of the gravity is
+ constrained.
+
+
+
+
+ Slide the surface along the y axis until it is no longer constrained.
+
+ First try to slide towards the direction of the gravity on the y axis
+ until either the edge in the opposite direction of the gravity is
+ unconstrained or the edge in the direction of the gravity is
+ constrained.
+
+ Then try to slide towards the opposite direction of the gravity on the
+ y axis until either the edge in the direction of the gravity is
+ unconstrained or the edge in the opposite direction of the gravity is
+ constrained.
+
+
+
+
+ Invert the anchor and gravity on the x axis if the surface is
+ constrained on the x axis. For example, if the left edge of the
+ surface is constrained, the gravity is 'left' and the anchor is
+ 'left', change the gravity to 'right' and the anchor to 'right'.
+
+ If the adjusted position also ends up being constrained, the resulting
+ position of the flip_x adjustment will be the one before the
+ adjustment.
+
+
+
+
+ Invert the anchor and gravity on the y axis if the surface is
+ constrained on the y axis. For example, if the bottom edge of the
+ surface is constrained, the gravity is 'bottom' and the anchor is
+ 'bottom', change the gravity to 'top' and the anchor to 'top'.
+
+ The adjusted position is calculated given the original anchor
+ rectangle and offset, but with the new flipped anchor and gravity
+ values.
+
+ If the adjusted position also ends up being constrained, the resulting
+ position of the flip_y adjustment will be the one before the
+ adjustment.
+
+
+
+
+ Resize the surface horizontally so that it is completely
+ unconstrained.
+
+
+
+
+ Resize the surface vertically so that it is completely unconstrained.
+
+
+
+
+
+
+ Specify how the window should be positioned if the originally intended
+ position caused the surface to be constrained, meaning at least
+ partially outside positioning boundaries set by the compositor. The
+ adjustment is set by constructing a bitmask describing the adjustment to
+ be made when the surface is constrained on that axis.
+
+ If no bit for one axis is set, the compositor will assume that the child
+ surface should not change its position on that axis when constrained.
+
+ If more than one bit for one axis is set, the order of how adjustments
+ are applied is specified in the corresponding adjustment descriptions.
+
+ The default adjustment is none.
+
+
+
+
+
+
+ Specify the surface position offset relative to the position of the
+ anchor on the anchor rectangle and the anchor on the surface. For
+ example if the anchor of the anchor rectangle is at (x, y), the surface
+ has the gravity bottom|right, and the offset is (ox, oy), the calculated
+ surface position will be (x + ox, y + oy). The offset position of the
+ surface is the one used for constraint testing. See
+ set_constraint_adjustment.
+
+ An example use case is placing a popup menu on top of a user interface
+ element, while aligning the user interface element of the parent surface
+ with some user interface element placed somewhere in the popup surface.
+
+
+
+
+
+
+
+
+
+ When set reactive, the surface is reconstrained if the conditions used
+ for constraining changed, e.g. the parent window moved.
+
+ If the conditions changed and the popup was reconstrained, an
+ xdg_popup.configure event is sent with updated geometry, followed by an
+ xdg_surface.configure event.
+
+
+
+
+
+ Set the parent window geometry the compositor should use when
+ positioning the popup. The compositor may use this information to
+ determine the future state the popup should be constrained using. If
+ this doesn't match the dimension of the parent the popup is eventually
+ positioned against, the behavior is undefined.
+
+ The arguments are given in the surface-local coordinate space.
+
+
+
+
+
+
+
+ Set the serial of an xdg_surface.configure event this positioner will be
+ used in response to. The compositor may use this information together
+ with set_parent_size to determine what future state the popup should be
+ constrained using.
+
+
+
+
+
+
+
+ An interface that may be implemented by a wl_surface, for
+ implementations that provide a desktop-style user interface.
+
+ It provides a base set of functionality required to construct user
+ interface elements requiring management by the compositor, such as
+ toplevel windows, menus, etc. The types of functionality are split into
+ xdg_surface roles.
+
+ Creating an xdg_surface does not set the role for a wl_surface. In order
+ to map an xdg_surface, the client must create a role-specific object
+ using, e.g., get_toplevel, get_popup. The wl_surface for any given
+ xdg_surface can have at most one role, and may not be assigned any role
+ not based on xdg_surface.
+
+ A role must be assigned before any other requests are made to the
+ xdg_surface object.
+
+ The client must call wl_surface.commit on the corresponding wl_surface
+ for the xdg_surface state to take effect.
+
+ Creating an xdg_surface from a wl_surface which has a buffer attached or
+ committed is a client error, and any attempts by a client to attach or
+ manipulate a buffer prior to the first xdg_surface.configure call must
+ also be treated as errors.
+
+ After creating a role-specific object and setting it up (e.g. by sending
+ the title, app ID, size constraints, parent, etc), the client must
+ perform an initial commit without any buffer attached. The compositor
+ will reply with initial wl_surface state such as
+ wl_surface.preferred_buffer_scale followed by an xdg_surface.configure
+ event. The client must acknowledge it and is then allowed to attach a
+ buffer to map the surface.
+
+ Mapping an xdg_surface-based role surface is defined as making it
+ possible for the surface to be shown by the compositor. Note that
+ a mapped surface is not guaranteed to be visible once it is mapped.
+
+ For an xdg_surface to be mapped by the compositor, the following
+ conditions must be met:
+ (1) the client has assigned an xdg_surface-based role to the surface
+ (2) the client has set and committed the xdg_surface state and the
+ role-dependent state to the surface
+ (3) the client has committed a buffer to the surface
+
+ A newly-unmapped surface is considered to have met condition (1) out
+ of the 3 required conditions for mapping a surface if its role surface
+ has not been destroyed, i.e. the client must perform the initial commit
+ again before attaching a buffer.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Destroy the xdg_surface object. An xdg_surface must only be destroyed
+ after its role object has been destroyed, otherwise
+ a defunct_role_object error is raised.
+
+
+
+
+
+ This creates an xdg_toplevel object for the given xdg_surface and gives
+ the associated wl_surface the xdg_toplevel role.
+
+ See the documentation of xdg_toplevel for more details about what an
+ xdg_toplevel is and how it is used.
+
+
+
+
+
+
+ This creates an xdg_popup object for the given xdg_surface and gives
+ the associated wl_surface the xdg_popup role.
+
+ If null is passed as a parent, a parent surface must be specified using
+ some other protocol, before committing the initial state.
+
+ See the documentation of xdg_popup for more details about what an
+ xdg_popup is and how it is used.
+
+
+
+
+
+
+
+
+ The window geometry of a surface is its "visible bounds" from the
+ user's perspective. Client-side decorations often have invisible
+ portions like drop-shadows which should be ignored for the
+ purposes of aligning, placing and constraining windows. Note that
+ in some situations, compositors may clip rendering to the window
+ geometry, so the client should avoid putting functional elements
+ outside of it.
+
+ The window geometry is double-buffered state, see wl_surface.commit.
+
+ When maintaining a position, the compositor should treat the (x, y)
+ coordinate of the window geometry as the top left corner of the window.
+ A client changing the (x, y) window geometry coordinate should in
+ general not alter the position of the window.
+
+ Once the window geometry of the surface is set, it is not possible to
+ unset it, and it will remain the same until set_window_geometry is
+ called again, even if a new subsurface or buffer is attached.
+
+ If never set, the value is the full bounds of the surface,
+ including any subsurfaces. This updates dynamically on every
+ commit. This unset is meant for extremely simple clients.
+
+ The arguments are given in the surface-local coordinate space of
+ the wl_surface associated with this xdg_surface, and may extend outside
+ of the wl_surface itself to mark parts of the subsurface tree as part of
+ the window geometry.
+
+ When applied, the effective window geometry will be the set window
+ geometry clamped to the bounding rectangle of the combined
+ geometry of the surface of the xdg_surface and the associated
+ subsurfaces.
+
+ The effective geometry will not be recalculated unless a new call to
+ set_window_geometry is done and the new pending surface state is
+ subsequently applied.
+
+ The width and height of the effective window geometry must be
+ greater than zero. Setting an invalid size will raise an
+ invalid_size error.
+
+
+
+
+
+
+
+
+
+ When a configure event is received, if a client commits the
+ surface in response to the configure event, then the client
+ must make an ack_configure request sometime before the commit
+ request, passing along the serial of the configure event.
+
+ For instance, for toplevel surfaces the compositor might use this
+ information to move a surface to the top left only when the client has
+ drawn itself for the maximized or fullscreen state.
+
+ If the client receives multiple configure events before it
+ can respond to one, it only has to ack the last configure event.
+ Acking a configure event that was never sent raises an invalid_serial
+ error.
+
+ A client is not required to commit immediately after sending
+ an ack_configure request - it may even ack_configure several times
+ before its next surface commit.
+
+ A client may send multiple ack_configure requests before committing, but
+ only the last request sent before a commit indicates which configure
+ event the client really is responding to.
+
+ Sending an ack_configure request consumes the serial number sent with
+ the request, as well as serial numbers sent by all configure events
+ sent on this xdg_surface prior to the configure event referenced by
+ the committed serial.
+
+ It is an error to issue multiple ack_configure requests referencing a
+ serial from the same configure event, or to issue an ack_configure
+ request referencing a serial from a configure event issued before the
+ event identified by the last ack_configure request for the same
+ xdg_surface. Doing so will raise an invalid_serial error.
+
+
+
+
+
+
+ The configure event marks the end of a configure sequence. A configure
+ sequence is a set of one or more events configuring the state of the
+ xdg_surface, including the final xdg_surface.configure event.
+
+ Where applicable, xdg_surface surface roles will during a configure
+ sequence extend this event as a latched state sent as events before the
+ xdg_surface.configure event. Such events should be considered to make up
+ a set of atomically applied configuration states, where the
+ xdg_surface.configure commits the accumulated state.
+
+ Clients should arrange their surface for the new states, and then send
+ an ack_configure request with the serial sent in this configure event at
+ some point before committing the new surface.
+
+ If the client receives multiple configure events before it can respond
+ to one, it is free to discard all but the last event it received.
+
+
+
+
+
+
+
+
+ This interface defines an xdg_surface role which allows a surface to,
+ among other things, set window-like properties such as maximize,
+ fullscreen, and minimize, set application-specific metadata like title and
+ id, and well as trigger user interactive operations such as interactive
+ resize and move.
+
+ A xdg_toplevel by default is responsible for providing the full intended
+ visual representation of the toplevel, which depending on the window
+ state, may mean things like a title bar, window controls and drop shadow.
+
+ Unmapping an xdg_toplevel means that the surface cannot be shown
+ by the compositor until it is explicitly mapped again.
+ All active operations (e.g., move, resize) are canceled and all
+ attributes (e.g. title, state, stacking, ...) are discarded for
+ an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to
+ the state it had right after xdg_surface.get_toplevel. The client
+ can re-map the toplevel by performing a commit without any buffer
+ attached, waiting for a configure event and handling it as usual (see
+ xdg_surface description).
+
+ Attaching a null buffer to a toplevel unmaps the surface.
+
+
+
+
+ This request destroys the role surface and unmaps the surface;
+ see "Unmapping" behavior in interface section for details.
+
+
+
+
+
+
+
+
+
+
+
+ Set the "parent" of this surface. This surface should be stacked
+ above the parent surface and all other ancestor surfaces.
+
+ Parent surfaces should be set on dialogs, toolboxes, or other
+ "auxiliary" surfaces, so that the parent is raised when the dialog
+ is raised.
+
+ Setting a null parent for a child surface unsets its parent. Setting
+ a null parent for a surface which currently has no parent is a no-op.
+
+ Only mapped surfaces can have child surfaces. Setting a parent which
+ is not mapped is equivalent to setting a null parent. If a surface
+ becomes unmapped, its children's parent is set to the parent of
+ the now-unmapped surface. If the now-unmapped surface has no parent,
+ its children's parent is unset. If the now-unmapped surface becomes
+ mapped again, its parent-child relationship is not restored.
+
+ The parent toplevel must not be one of the child toplevel's
+ descendants, and the parent must be different from the child toplevel,
+ otherwise the invalid_parent protocol error is raised.
+
+
+
+
+
+
+ Set a short title for the surface.
+
+ This string may be used to identify the surface in a task bar,
+ window list, or other user interface elements provided by the
+ compositor.
+
+ The string must be encoded in UTF-8.
+
+
+
+
+
+
+ Set an application identifier for the surface.
+
+ The app ID identifies the general class of applications to which
+ the surface belongs. The compositor can use this to group multiple
+ surfaces together, or to determine how to launch a new application.
+
+ For D-Bus activatable applications, the app ID is used as the D-Bus
+ service name.
+
+ The compositor shell will try to group application surfaces together
+ by their app ID. As a best practice, it is suggested to select app
+ ID's that match the basename of the application's .desktop file.
+ For example, "org.freedesktop.FooViewer" where the .desktop file is
+ "org.freedesktop.FooViewer.desktop".
+
+ Like other properties, a set_app_id request can be sent after the
+ xdg_toplevel has been mapped to update the property.
+
+ See the desktop-entry specification [0] for more details on
+ application identifiers and how they relate to well-known D-Bus
+ names and .desktop files.
+
+ [0] https://standards.freedesktop.org/desktop-entry-spec/
+
+
+
+
+
+
+ Clients implementing client-side decorations might want to show
+ a context menu when right-clicking on the decorations, giving the
+ user a menu that they can use to maximize or minimize the window.
+
+ This request asks the compositor to pop up such a window menu at
+ the given position, relative to the local surface coordinates of
+ the parent surface. There are no guarantees as to what menu items
+ the window menu contains, or even if a window menu will be drawn
+ at all.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event.
+
+
+
+
+
+
+
+
+
+ Start an interactive, user-driven move of the surface.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event. The passed
+ serial is used to determine the type of interactive move (touch,
+ pointer, etc).
+
+ The server may ignore move requests depending on the state of
+ the surface (e.g. fullscreen or maximized), or if the passed serial
+ is no longer valid.
+
+ If triggered, the surface will lose the focus of the device
+ (wl_pointer, wl_touch, etc) used for the move. It is up to the
+ compositor to visually indicate that the move is taking place, such as
+ updating a pointer cursor, during the move. There is no guarantee
+ that the device focus will return when the move is completed.
+
+
+
+
+
+
+
+ These values are used to indicate which edge of a surface
+ is being dragged in a resize operation.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Start a user-driven, interactive resize of the surface.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event. The passed
+ serial is used to determine the type of interactive resize (touch,
+ pointer, etc).
+
+ The server may ignore resize requests depending on the state of
+ the surface (e.g. fullscreen or maximized).
+
+ If triggered, the client will receive configure events with the
+ "resize" state enum value and the expected sizes. See the "resize"
+ enum value for more details about what is required. The client
+ must also acknowledge configure events using "ack_configure". After
+ the resize is completed, the client will receive another "configure"
+ event without the resize state.
+
+ If triggered, the surface also will lose the focus of the device
+ (wl_pointer, wl_touch, etc) used for the resize. It is up to the
+ compositor to visually indicate that the resize is taking place,
+ such as updating a pointer cursor, during the resize. There is no
+ guarantee that the device focus will return when the resize is
+ completed.
+
+ The edges parameter specifies how the surface should be resized, and
+ is one of the values of the resize_edge enum. Values not matching
+ a variant of the enum will cause the invalid_resize_edge protocol error.
+ The compositor may use this information to update the surface position
+ for example when dragging the top left corner. The compositor may also
+ use this information to adapt its behavior, e.g. choose an appropriate
+ cursor image.
+
+
+
+
+
+
+
+
+ The different state values used on the surface. This is designed for
+ state values like maximized, fullscreen. It is paired with the
+ configure event to ensure that both the client and the compositor
+ setting the state can be synchronized.
+
+ States set in this way are double-buffered, see wl_surface.commit.
+
+
+
+ The surface is maximized. The window geometry specified in the configure
+ event must be obeyed by the client, or the xdg_wm_base.invalid_surface_state
+ error is raised.
+
+ The client should draw without shadow or other
+ decoration outside of the window geometry.
+
+
+
+
+ The surface is fullscreen. The window geometry specified in the
+ configure event is a maximum; the client cannot resize beyond it. For
+ a surface to cover the whole fullscreened area, the geometry
+ dimensions must be obeyed by the client. For more details, see
+ xdg_toplevel.set_fullscreen.
+
+
+
+
+ The surface is being resized. The window geometry specified in the
+ configure event is a maximum; the client cannot resize beyond it.
+ Clients that have aspect ratio or cell sizing configuration can use
+ a smaller size, however.
+
+
+
+
+ Client window decorations should be painted as if the window is
+ active. Do not assume this means that the window actually has
+ keyboard or pointer focus.
+
+
+
+
+ The window is currently in a tiled layout and the left edge is
+ considered to be adjacent to another part of the tiling grid.
+
+ The client should draw without shadow or other decoration outside of
+ the window geometry on the left edge.
+
+
+
+
+ The window is currently in a tiled layout and the right edge is
+ considered to be adjacent to another part of the tiling grid.
+
+ The client should draw without shadow or other decoration outside of
+ the window geometry on the right edge.
+
+
+
+
+ The window is currently in a tiled layout and the top edge is
+ considered to be adjacent to another part of the tiling grid.
+
+ The client should draw without shadow or other decoration outside of
+ the window geometry on the top edge.
+
+
+
+
+ The window is currently in a tiled layout and the bottom edge is
+ considered to be adjacent to another part of the tiling grid.
+
+ The client should draw without shadow or other decoration outside of
+ the window geometry on the bottom edge.
+
+
+
+
+ The surface is currently not ordinarily being repainted; for
+ example because its content is occluded by another window, or its
+ outputs are switched off due to screen locking.
+
+
+
+
+ The left edge of the window is currently constrained, meaning it
+ shouldn't attempt to resize from that edge. It can for example mean
+ it's tiled next to a monitor edge on the constrained side of the
+ window.
+
+
+
+
+ The right edge of the window is currently constrained, meaning it
+ shouldn't attempt to resize from that edge. It can for example mean
+ it's tiled next to a monitor edge on the constrained side of the
+ window.
+
+
+
+
+ The top edge of the window is currently constrained, meaning it
+ shouldn't attempt to resize from that edge. It can for example mean
+ it's tiled next to a monitor edge on the constrained side of the
+ window.
+
+
+
+
+ The bottom edge of the window is currently constrained, meaning it
+ shouldn't attempt to resize from that edge. It can for example mean
+ it's tiled next to a monitor edge on the constrained side of the
+ window.
+
+
+
+
+
+
+ Set a maximum size for the window.
+
+ The client can specify a maximum size so that the compositor does
+ not try to configure the window beyond this size.
+
+ The width and height arguments are in window geometry coordinates.
+ See xdg_surface.set_window_geometry.
+
+ Values set in this way are double-buffered, see wl_surface.commit.
+
+ The compositor can use this information to allow or disallow
+ different states like maximize or fullscreen and draw accurate
+ animations.
+
+ Similarly, a tiling window manager may use this information to
+ place and resize client windows in a more effective way.
+
+ The client should not rely on the compositor to obey the maximum
+ size. The compositor may decide to ignore the values set by the
+ client and request a larger size.
+
+ If never set, or a value of zero in the request, means that the
+ client has no expected maximum size in the given dimension.
+ As a result, a client wishing to reset the maximum size
+ to an unspecified state can use zero for width and height in the
+ request.
+
+ Requesting a maximum size to be smaller than the minimum size of
+ a surface is illegal and will result in an invalid_size error.
+
+ The width and height must be greater than or equal to zero. Using
+ strictly negative values for width or height will result in a
+ invalid_size error.
+
+
+
+
+
+
+
+ Set a minimum size for the window.
+
+ The client can specify a minimum size so that the compositor does
+ not try to configure the window below this size.
+
+ The width and height arguments are in window geometry coordinates.
+ See xdg_surface.set_window_geometry.
+
+ Values set in this way are double-buffered, see wl_surface.commit.
+
+ The compositor can use this information to allow or disallow
+ different states like maximize or fullscreen and draw accurate
+ animations.
+
+ Similarly, a tiling window manager may use this information to
+ place and resize client windows in a more effective way.
+
+ The client should not rely on the compositor to obey the minimum
+ size. The compositor may decide to ignore the values set by the
+ client and request a smaller size.
+
+ If never set, or a value of zero in the request, means that the
+ client has no expected minimum size in the given dimension.
+ As a result, a client wishing to reset the minimum size
+ to an unspecified state can use zero for width and height in the
+ request.
+
+ Requesting a minimum size to be larger than the maximum size of
+ a surface is illegal and will result in an invalid_size error.
+
+ The width and height must be greater than or equal to zero. Using
+ strictly negative values for width and height will result in a
+ invalid_size error.
+
+
+
+
+
+
+
+ Maximize the surface.
+
+ After requesting that the surface should be maximized, the compositor
+ will respond by emitting a configure event. Whether this configure
+ actually sets the window maximized is subject to compositor policies.
+ The client must then update its content, drawing in the configured
+ state. The client must also acknowledge the configure when committing
+ the new content (see ack_configure).
+
+ It is up to the compositor to decide how and where to maximize the
+ surface, for example which output and what region of the screen should
+ be used.
+
+ If the surface was already maximized, the compositor will still emit
+ a configure event with the "maximized" state.
+
+ If the surface is in a fullscreen state, this request has no direct
+ effect. It may alter the state the surface is returned to when
+ unmaximized unless overridden by the compositor.
+
+
+
+
+
+ Unmaximize the surface.
+
+ After requesting that the surface should be unmaximized, the compositor
+ will respond by emitting a configure event. Whether this actually
+ un-maximizes the window is subject to compositor policies.
+ If available and applicable, the compositor will include the window
+ geometry dimensions the window had prior to being maximized in the
+ configure event. The client must then update its content, drawing it in
+ the configured state. The client must also acknowledge the configure
+ when committing the new content (see ack_configure).
+
+ It is up to the compositor to position the surface after it was
+ unmaximized; usually the position the surface had before maximizing, if
+ applicable.
+
+ If the surface was already not maximized, the compositor will still
+ emit a configure event without the "maximized" state.
+
+ If the surface is in a fullscreen state, this request has no direct
+ effect. It may alter the state the surface is returned to when
+ unmaximized unless overridden by the compositor.
+
+
+
+
+
+ Make the surface fullscreen.
+
+ After requesting that the surface should be fullscreened, the
+ compositor will respond by emitting a configure event. Whether the
+ client is actually put into a fullscreen state is subject to compositor
+ policies. The client must also acknowledge the configure when
+ committing the new content (see ack_configure).
+
+ The output passed by the request indicates the client's preference as
+ to which display it should be set fullscreen on. If this value is NULL,
+ it's up to the compositor to choose which display will be used to map
+ this surface.
+
+ If the surface doesn't cover the whole output, the compositor will
+ position the surface in the center of the output and compensate with
+ with border fill covering the rest of the output. The content of the
+ border fill is undefined, but should be assumed to be in some way that
+ attempts to blend into the surrounding area (e.g. solid black).
+
+ If the fullscreened surface is not opaque, the compositor must make
+ sure that other screen content not part of the same surface tree (made
+ up of subsurfaces, popups or similarly coupled surfaces) are not
+ visible below the fullscreened surface.
+
+
+
+
+
+
+ Make the surface no longer fullscreen.
+
+ After requesting that the surface should be unfullscreened, the
+ compositor will respond by emitting a configure event.
+ Whether this actually removes the fullscreen state of the client is
+ subject to compositor policies.
+
+ Making a surface unfullscreen sets states for the surface based on the following:
+ * the state(s) it may have had before becoming fullscreen
+ * any state(s) decided by the compositor
+ * any state(s) requested by the client while the surface was fullscreen
+
+ The compositor may include the previous window geometry dimensions in
+ the configure event, if applicable.
+
+ The client must also acknowledge the configure when committing the new
+ content (see ack_configure).
+
+
+
+
+
+ Request that the compositor minimize your surface. There is no
+ way to know if the surface is currently minimized, nor is there
+ any way to unset minimization on this surface.
+
+ If you are looking to throttle redrawing when minimized, please
+ instead use the wl_surface.frame event for this, as this will
+ also work with live previews on windows in Alt-Tab, Expose or
+ similar compositor features.
+
+
+
+
+
+ This configure event asks the client to resize its toplevel surface or
+ to change its state. The configured state should not be applied
+ immediately. See xdg_surface.configure for details.
+
+ The width and height arguments specify a hint to the window
+ about how its surface should be resized in window geometry
+ coordinates. See set_window_geometry.
+
+ If the width or height arguments are zero, it means the client
+ should decide its own window dimension. This may happen when the
+ compositor needs to configure the state of the surface but doesn't
+ have any information about any previous or expected dimension.
+
+ The states listed in the event specify how the width/height
+ arguments should be interpreted, and possibly how it should be
+ drawn.
+
+ Clients must send an ack_configure in response to this event. See
+ xdg_surface.configure and xdg_surface.ack_configure for details.
+
+
+
+
+
+
+
+
+ The close event is sent by the compositor when the user
+ wants the surface to be closed. This should be equivalent to
+ the user clicking the close button in client-side decorations,
+ if your application has any.
+
+ This is only a request that the user intends to close the
+ window. The client may choose to ignore this request, or show
+ a dialog to ask the user to save their data, etc.
+
+
+
+
+
+
+
+ The configure_bounds event may be sent prior to a xdg_toplevel.configure
+ event to communicate the bounds a window geometry size is recommended
+ to constrain to.
+
+ The passed width and height are in surface coordinate space. If width
+ and height are 0, it means bounds is unknown and equivalent to as if no
+ configure_bounds event was ever sent for this surface.
+
+ The bounds can for example correspond to the size of a monitor excluding
+ any panels or other shell components, so that a surface isn't created in
+ a way that it cannot fit.
+
+ The bounds may change at any point, and in such a case, a new
+ xdg_toplevel.configure_bounds will be sent, followed by
+ xdg_toplevel.configure and xdg_surface.configure.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This event advertises the capabilities supported by the compositor. If
+ a capability isn't supported, clients should hide or disable the UI
+ elements that expose this functionality. For instance, if the
+ compositor doesn't advertise support for minimized toplevels, a button
+ triggering the set_minimized request should not be displayed.
+
+ The compositor will ignore requests it doesn't support. For instance,
+ a compositor which doesn't advertise support for minimized will ignore
+ set_minimized requests.
+
+ Compositors must send this event once before the first
+ xdg_surface.configure event. When the capabilities change, compositors
+ must send this event again and then send an xdg_surface.configure
+ event.
+
+ The configured state should not be applied immediately. See
+ xdg_surface.configure for details.
+
+ The capabilities are sent as an array of 32-bit unsigned integers in
+ native endianness.
+
+
+
+
+
+
+
+ A popup surface is a short-lived, temporary surface. It can be used to
+ implement for example menus, popovers, tooltips and other similar user
+ interface concepts.
+
+ A popup can be made to take an explicit grab. See xdg_popup.grab for
+ details.
+
+ When the popup is dismissed, a popup_done event will be sent out, and at
+ the same time the surface will be unmapped. See the xdg_popup.popup_done
+ event for details.
+
+ Explicitly destroying the xdg_popup object will also dismiss the popup and
+ unmap the surface. Clients that want to dismiss the popup when another
+ surface of their own is clicked should dismiss the popup using the destroy
+ request.
+
+ A newly created xdg_popup will be stacked on top of all previously created
+ xdg_popup surfaces associated with the same xdg_toplevel.
+
+ The parent of an xdg_popup must be mapped (see the xdg_surface
+ description) before the xdg_popup itself.
+
+ The client must call wl_surface.commit on the corresponding wl_surface
+ for the xdg_popup state to take effect.
+
+
+
+
+
+
+
+
+ This destroys the popup. Explicitly destroying the xdg_popup
+ object will also dismiss the popup, and unmap the surface.
+
+ If this xdg_popup is not the "topmost" popup, the
+ xdg_wm_base.not_the_topmost_popup protocol error will be sent.
+
+
+
+
+
+ This request makes the created popup take an explicit grab. An explicit
+ grab will be dismissed when the user dismisses the popup, or when the
+ client destroys the xdg_popup. This can be done by the user clicking
+ outside the surface, using the keyboard, or even locking the screen
+ through closing the lid or a timeout.
+
+ If the compositor denies the grab, the popup will be immediately
+ dismissed.
+
+ This request must be used in response to some sort of user action like a
+ button press, key press, or touch down event. The serial number of the
+ event should be passed as 'serial'.
+
+ The parent of a grabbing popup must either be an xdg_toplevel surface or
+ another xdg_popup with an explicit grab. If the parent is another
+ xdg_popup it means that the popups are nested, with this popup now being
+ the topmost popup.
+
+ Nested popups must be destroyed in the reverse order they were created
+ in, e.g. the only popup you are allowed to destroy at all times is the
+ topmost one.
+
+ When compositors choose to dismiss a popup, they may dismiss every
+ nested grabbing popup as well. When a compositor dismisses popups, it
+ will follow the same dismissing order as required from the client.
+
+ If the topmost grabbing popup is destroyed, the grab will be returned to
+ the parent of the popup, if that parent previously had an explicit grab.
+
+ If the parent is a grabbing popup which has already been dismissed, this
+ popup will be immediately dismissed. If the parent is a popup that did
+ not take an explicit grab, an error will be raised.
+
+ During a popup grab, the client owning the grab will receive pointer
+ and touch events for all their surfaces as normal (similar to an
+ "owner-events" grab in X11 parlance), while the top most grabbing popup
+ will always have keyboard focus.
+
+
+
+
+
+
+
+ This event asks the popup surface to configure itself given the
+ configuration. The configured state should not be applied immediately.
+ See xdg_surface.configure for details.
+
+ The x and y arguments represent the position the popup was placed at
+ given the xdg_positioner rule, relative to the upper left corner of the
+ window geometry of the parent surface.
+
+ For version 2 or older, the configure event for an xdg_popup is only
+ ever sent once for the initial configuration. Starting with version 3,
+ it may be sent again if the popup is setup with an xdg_positioner with
+ set_reactive requested, or in response to xdg_popup.reposition requests.
+
+
+
+
+
+
+
+
+
+ The popup_done event is sent out when a popup is dismissed by the
+ compositor. The client should destroy the xdg_popup object at this
+ point.
+
+
+
+
+
+
+
+ Reposition an already-mapped popup. The popup will be placed given the
+ details in the passed xdg_positioner object, and a
+ xdg_popup.repositioned followed by xdg_popup.configure and
+ xdg_surface.configure will be emitted in response. Any parameters set
+ by the previous positioner will be discarded.
+
+ The passed token will be sent in the corresponding
+ xdg_popup.repositioned event. The new popup position will not take
+ effect until the corresponding configure event is acknowledged by the
+ client. See xdg_popup.repositioned for details. The token itself is
+ opaque, and has no other special meaning.
+
+ If multiple reposition requests are sent, the compositor may skip all
+ but the last one.
+
+ If the popup is repositioned in response to a configure event for its
+ parent, the client should send an xdg_positioner.set_parent_configure
+ and possibly an xdg_positioner.set_parent_size request to allow the
+ compositor to properly constrain the popup.
+
+ If the popup is repositioned together with a parent that is being
+ resized, but not in response to a configure event, the client should
+ send an xdg_positioner.set_parent_size request.
+
+
+
+
+
+
+
+ The repositioned event is sent as part of a popup configuration
+ sequence, together with xdg_popup.configure and lastly
+ xdg_surface.configure to notify the completion of a reposition request.
+
+ The repositioned event is to notify about the completion of a
+ xdg_popup.reposition request. The token argument is the token passed
+ in the xdg_popup.reposition request.
+
+ Immediately after this event is emitted, xdg_popup.configure and
+ xdg_surface.configure will be sent with the updated size and position,
+ as well as a new configure serial.
+
+ The client should optionally update the content of the popup, but must
+ acknowledge the new popup configuration for the new position to take
+ effect. See xdg_surface.ack_configure for details.
+
+
+
+
+
+
diff --git a/src/Application.cpp b/src/Application.cpp
index 3d039be..fb0f245 100644
--- a/src/Application.cpp
+++ b/src/Application.cpp
@@ -24,6 +24,8 @@
#define XR_USE_GRAPHICS_API_VULKAN
#include
+#include "wayland-server-protocol.h"
+
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wreserved-identifier"
@@ -59,6 +61,9 @@
#include "Util.h"
#include "VulkanRenderer.h"
+#include "wayland/Shm.h"
+#include "wayland/Surface.h"
+#include "wayland/WaylandServer.h"
#if defined(TRACY_ENABLE)
# include
@@ -416,11 +421,11 @@ auto split_extension_list(std::string_view list) -> std::vector
if (start >= list.size()) {
break;
}
- auto end = list.find(' ', start);
+ auto end { list.find(' ', start) };
if (end == std::string_view::npos) {
end = list.size();
}
- auto token = list.substr(start, end - start);
+ auto token { list.substr(start, end - start) };
if (!token.empty()) {
extensions.emplace_back(token);
}
@@ -433,9 +438,9 @@ auto xr_rotate_vector(XrQuaternionf q, smath::Vec3 v) -> smath::Vec3
{
smath::Vec3 u { q.x, q.y, q.z };
float const s = q.w;
- auto const dot = u.dot(v);
- auto const u_dot = u.dot(u);
- auto const cross = u.cross(v);
+ auto const dot { u.dot(v) };
+ auto const u_dot { u.dot(u) };
+ auto const cross { u.cross(v) };
return (u * (2.0f * dot)) + (v * (s * s - u_dot)) + (cross * (2.0f * s));
}
@@ -447,6 +452,45 @@ auto xr_rotate_vector(XrQuaternionf q, smath::Vec3 v) -> smath::Vec3
return std::string { buffer.data() };
}
+auto make_shm_texture(Lunar::Wayland::ShmBuffer const &buffer)
+ -> std::optional
+{
+ if (!buffer.pool || !buffer.data()) {
+ return {};
+ }
+ if (buffer.width <= 0 || buffer.height <= 0 || buffer.stride <= 0) {
+ return {};
+ }
+ if (buffer.format != WL_SHM_FORMAT_ARGB8888
+ && buffer.format != WL_SHM_FORMAT_XRGB8888) {
+ return {};
+ }
+
+ auto const width { static_cast(buffer.width) };
+ auto const height { static_cast(buffer.height) };
+ auto const row_bytes { static_cast(width) * 4 };
+ if (buffer.stride < static_cast(row_bytes)) {
+ return {};
+ }
+
+ std::vector pixels(row_bytes * height);
+ auto const *src { reinterpret_cast(buffer.data()) };
+ for (uint32_t y = 0; y < height; ++y) {
+ auto const *row_src { src + static_cast(buffer.stride) * y };
+ auto const dst_y { height - y - 1 };
+ auto *row_dst { pixels.data() + row_bytes * dst_y };
+ std::memcpy(row_dst, row_src, row_bytes);
+ if (buffer.format == WL_SHM_FORMAT_XRGB8888) {
+ for (uint32_t x = 0; x < width; ++x) {
+ row_dst[x * 4 + 3] = 0xff;
+ }
+ }
+ }
+
+ return Lunar::CPUTexture(
+ std::move(pixels), width, height, vk::Format::eB8G8R8A8Unorm);
+}
+
} // namespace
namespace Lunar {
@@ -494,11 +538,12 @@ Application::Application()
bool const has_display
= (display_env && *display_env) || (wayland_env && *wayland_env);
m_backend = has_display ? Backend::SDL : Backend::KMS;
+ m_wayland = std::make_unique(m_logger);
init_openxr();
- auto instance_extensions = std::span {};
- auto device_extensions = std::span {};
+ auto instance_extensions { std::span {} };
+ auto device_extensions { std::span {} };
if (m_openxr) {
instance_extensions = m_openxr->instance_extensions;
device_extensions = m_openxr->device_extensions;
@@ -595,12 +640,12 @@ auto Application::asset_directory() -> std::filesystem::path
candidates.emplace_back(base / "lunar" / "assets");
} };
- if (auto const *xdg_data_home = getenv("XDG_DATA_HOME");
+ if (auto const *xdg_data_home { getenv("XDG_DATA_HOME") };
xdg_data_home && *xdg_data_home) {
add_xdg_path(xdg_data_home);
}
- if (auto const *xdg_data_dirs = getenv("XDG_DATA_DIRS");
+ if (auto const *xdg_data_dirs { getenv("XDG_DATA_DIRS") };
xdg_data_dirs && *xdg_data_dirs) {
std::string_view dirs_view { xdg_data_dirs };
size_t start { 0 };
@@ -651,11 +696,10 @@ auto Application::init_test_meshes() -> void
auto Application::init_wayland() -> void
{
- // TODO: Replace with new name, we might have conflicts!
- auto const *WAYLAND_SOCKET_NAME { "wayland-5" };
- m_wayland.display.add_socket(WAYLAND_SOCKET_NAME);
- assert(setenv("WAYLAND_DISPLAY", WAYLAND_SOCKET_NAME, true) == 0);
- m_logger.info("Started Wayland display socket on {}", WAYLAND_SOCKET_NAME);
+ auto const socket_name { m_wayland->socket_name() };
+ assert(setenv("WAYLAND_DISPLAY", socket_name.data(), true) == 0);
+ m_logger.info("Started Wayland display socket on {}", socket_name);
+ std::println("WAYLAND_DISPLAY={}", socket_name);
}
auto Application::run() -> void
@@ -675,6 +719,10 @@ auto Application::run() -> void
float fps { 0.0f };
while (m_running) {
GZoneScopedN("Frame");
+ if (m_wayland) {
+ m_wayland->dispatch();
+ m_wayland->flush();
+ }
if (m_openxr) {
poll_openxr_events();
}
@@ -682,7 +730,7 @@ auto Application::run() -> void
if (use_sdl) {
now = SDL_GetTicks();
} else {
- auto const now_tp = std::chrono::steady_clock::now();
+ auto const now_tp { std::chrono::steady_clock::now() };
now = static_cast(
std::chrono::duration_cast(
now_tp.time_since_epoch())
@@ -827,9 +875,9 @@ auto Application::run() -> void
if (!m_show_imgui) {
m_camera.up = camera_up;
- auto const distance = target_distance > 0.0f
- ? target_distance
- : std::max(1.0f, m_cursor.r);
+ auto const distance { target_distance > 0.0f
+ ? target_distance
+ : std::max(1.0f, m_cursor.r) };
m_camera.target = m_camera.position + look_dir * distance;
}
@@ -963,15 +1011,15 @@ auto Application::run() -> void
ImGui::Render();
}
- auto record_scene = [&](VulkanRenderer::GL &gl) {
+ auto record_scene { [&](VulkanRenderer::GL &gl) {
GZoneScopedN("Render");
auto view { smath::matrix_look_at(
m_camera.position, m_camera.target, m_camera.up) };
- auto const draw_extent = m_renderer->draw_extent();
- auto const aspect = draw_extent.height == 0
- ? 1.0f
- : static_cast(draw_extent.width)
- / static_cast(draw_extent.height);
+ auto const draw_extent { m_renderer->draw_extent() };
+ auto const aspect { draw_extent.height == 0
+ ? 1.0f
+ : static_cast(draw_extent.width)
+ / static_cast(draw_extent.height) };
auto projection { smath::matrix_perspective(
m_camera.fovy, aspect, 0.1f, 10000.0f) };
projection[1][1] *= -1;
@@ -987,7 +1035,7 @@ auto Application::run() -> void
gl.set_texture();
auto const &meshes { m_test_meshes };
if (meshes.size() > 2 && !meshes[2]->surfaces.empty()) {
- auto const &surface = meshes[2]->surfaces[0];
+ auto const &surface { meshes[2]->surfaces[0] };
gl.draw_mesh(meshes[2]->mesh_buffers,
view_projection
* smath::translate(smath::Vec3 { 0.0f, 0.0f, -5.0f }),
@@ -1033,7 +1081,48 @@ auto Application::run() -> void
if (m_openxr && m_openxr->hand_tracking_supported) {
render_hands(gl, view_projection);
}
- };
+
+ if (m_wayland) {
+ auto const wayland_draw_extent { m_renderer->draw_extent() };
+ auto const draw_width { static_cast(
+ wayland_draw_extent.width) };
+ auto const draw_height { static_cast(
+ wayland_draw_extent.height) };
+ if (draw_width > 0.0f && draw_height > 0.0f) {
+ gl.set_transform(smath::Mat4::identity());
+ gl.set_culling(false);
+ gl.use_pipeline(m_renderer->wayland_pipeline());
+ for (auto *surface : m_wayland->surfaces()) {
+ auto buffer { surface->current_buffer() };
+ if (!buffer) {
+ continue;
+ }
+ auto texture { make_shm_texture(*buffer) };
+ if (!texture) {
+ continue;
+ }
+ auto const width { static_cast(buffer->width) };
+ auto const height { static_cast(
+ buffer->height) };
+ auto const size { smath::Vec2 {
+ (width / draw_width) * 2.0f,
+ (height / draw_height) * 2.0f,
+ } };
+ auto const pos { smath::Vec2 {
+ -1.0f, 1.0f - size.y() } };
+ auto image { m_renderer->create_image(
+ *texture, vk::ImageUsageFlagBits::eSampled) };
+ gl.set_texture(&image);
+ gl.draw_rectangle(pos, size);
+ gl.flush();
+ gl.set_texture(std::nullopt);
+ m_renderer->destroy_image_later(image);
+ }
+ gl.use_pipeline(m_renderer->mesh_pipeline());
+ gl.set_culling(true);
+ }
+ }
+ } };
if (xr_active) {
if (m_openxr && m_openxr->session_running) {
@@ -1100,7 +1189,7 @@ auto Application::shutdown_input() -> void
auto Application::init_openxr() -> void
{
- if (auto const *no_xr = getenv("LUNAR_NO_XR"); no_xr && *no_xr) {
+ if (auto const *no_xr { getenv("LUNAR_NO_XR") }; no_xr && *no_xr) {
return;
}
m_openxr = std::make_unique();
@@ -1562,7 +1651,8 @@ auto Application::init_openxr_session() -> void
};
m_openxr->color_format = formats.front();
for (auto const preferred : preferred_formats) {
- auto const found = std::find(formats.begin(), formats.end(), preferred);
+ auto const found { std::find(
+ formats.begin(), formats.end(), preferred) };
if (found != formats.end()) {
m_openxr->color_format = *found;
break;
diff --git a/src/Application.h b/src/Application.h
index 79efed8..2935f01 100644
--- a/src/Application.h
+++ b/src/Application.h
@@ -29,6 +29,9 @@ namespace Lunar {
struct VulkanRenderer;
struct OpenXrState;
+namespace Wayland {
+struct WaylandServer;
+}
struct Application {
auto run() -> void;
@@ -80,10 +83,6 @@ private:
auto render_hands(
VulkanRenderer::GL &gl, smath::Mat4 const &view_projection) -> void;
- struct {
- Wayland::Display display;
- } m_wayland;
-
SDL_Window *m_window { nullptr };
Backend m_backend { Backend::SDL };
Logger m_logger { "Lunar" };
@@ -95,6 +94,7 @@ private:
libinput *m_libinput { nullptr };
std::unique_ptr m_openxr {};
+ std::unique_ptr m_wayland {};
bool m_running { true };
bool m_mouse_captured { false };
diff --git a/src/CPUTexture.cpp b/src/CPUTexture.cpp
index 565f796..fbd6847 100644
--- a/src/CPUTexture.cpp
+++ b/src/CPUTexture.cpp
@@ -76,4 +76,13 @@ CPUTexture::CPUTexture(std::filesystem::path const &path)
stbi_image_free(data);
}
+CPUTexture::CPUTexture(std::vector pixels, uint32_t width,
+ uint32_t height, vk::Format format)
+ : pixels(std::move(pixels))
+ , width(width)
+ , height(height)
+ , format(format)
+{
+}
+
} // namespace Lunar
diff --git a/src/CPUTexture.h b/src/CPUTexture.h
index df39161..d8f4f10 100644
--- a/src/CPUTexture.h
+++ b/src/CPUTexture.h
@@ -15,6 +15,8 @@ struct CPUTexture {
vk::Format format { vk::Format::eR8G8B8A8Unorm };
explicit CPUTexture(std::filesystem::path const &path);
+ CPUTexture(std::vector pixels, uint32_t width, uint32_t height,
+ vk::Format format);
};
} // namespace Lunar
diff --git a/src/VulkanRenderer.cpp b/src/VulkanRenderer.cpp
index aee3bdb..baca8f2 100644
--- a/src/VulkanRenderer.cpp
+++ b/src/VulkanRenderer.cpp
@@ -62,10 +62,10 @@ auto VulkanRenderer::GL::begin_drawing(vk::CommandBuffer cmd,
m_current_uv = { 0.0f, 0.0f };
m_bound_texture = &m_renderer.m_vk.error_image;
- auto const extent = vk::Extent2D {
+ auto const extent { vk::Extent2D {
m_color_target->extent.width,
m_color_target->extent.height,
- };
+ } };
vk::RenderingAttachmentInfo color_att {};
vk::ClearValue clear {};
@@ -189,7 +189,8 @@ auto VulkanRenderer::GL::set_culling(bool enabled) -> void
}
if (m_active_pipeline == &m_renderer.m_vk.mesh_pipeline
- || m_active_pipeline == &m_renderer.m_vk.mesh_pipeline_culled) {
+ || m_active_pipeline == &m_renderer.m_vk.mesh_pipeline_culled
+ || m_active_pipeline == &m_renderer.m_vk.wayland_pipeline) {
m_active_pipeline = enabled ? &m_renderer.m_vk.mesh_pipeline_culled
: &m_renderer.m_vk.mesh_pipeline;
} else if (m_active_pipeline == &m_renderer.m_vk.triangle_pipeline
@@ -206,7 +207,7 @@ auto VulkanRenderer::GL::end() -> void
if (!m_inside_primitive)
return;
- auto const count = m_vertices.size() - m_primitive_start;
+ auto const count { m_vertices.size() - m_primitive_start };
emit_indices(m_primitive_start, count);
m_inside_primitive = false;
}
@@ -220,8 +221,8 @@ auto VulkanRenderer::GL::flush() -> void
auto const index_data_size { m_indices.size() * sizeof(uint32_t) };
auto const staging_size { vertex_data_size + index_data_size };
- auto staging = m_renderer.create_buffer(staging_size,
- vk::BufferUsageFlagBits::eTransferSrc, VMA_MEMORY_USAGE_CPU_ONLY);
+ auto staging { m_renderer.create_buffer(staging_size,
+ vk::BufferUsageFlagBits::eTransferSrc, VMA_MEMORY_USAGE_CPU_ONLY) };
void *staging_dst = staging.info.pMappedData;
bool staging_mapped_here { false };
@@ -273,7 +274,8 @@ auto VulkanRenderer::GL::flush() -> void
bind_pipeline_if_needed();
if (m_active_pipeline == &m_renderer.m_vk.mesh_pipeline
- || m_active_pipeline == &m_renderer.m_vk.mesh_pipeline_culled) {
+ || m_active_pipeline == &m_renderer.m_vk.mesh_pipeline_culled
+ || m_active_pipeline == &m_renderer.m_vk.wayland_pipeline) {
auto const image_set {
m_renderer.m_vk.get_current_frame().frame_descriptors.allocate(
m_renderer.m_logger, m_renderer.m_vkb.dev.device,
@@ -327,6 +329,8 @@ auto VulkanRenderer::GL::use_pipeline(Pipeline &pipeline) -> void
resolved_pipeline = m_culling_enabled
? &m_renderer.m_vk.mesh_pipeline_culled
: &m_renderer.m_vk.mesh_pipeline;
+ } else if (&pipeline == &m_renderer.m_vk.wayland_pipeline) {
+ resolved_pipeline = &m_renderer.m_vk.wayland_pipeline;
} else if (&pipeline == &m_renderer.m_vk.triangle_pipeline
|| &pipeline == &m_renderer.m_vk.triangle_pipeline_culled) {
resolved_pipeline = m_culling_enabled
@@ -369,23 +373,23 @@ auto VulkanRenderer::GL::pop_transform() -> void
auto VulkanRenderer::GL::draw_rectangle(smath::Vec2 pos, smath::Vec2 size,
smath::Vec4 rect_color, float rotation) -> void
{
- auto const half_size = size * 0.5f;
- auto const center = pos + half_size;
+ auto const half_size { size * 0.5f };
+ auto const center { pos + half_size };
- auto rotate = [&](smath::Vec2 const &p) {
+ auto rotate { [&](smath::Vec2 const &p) {
float const c = std::cos(rotation);
float const s = std::sin(rotation);
return smath::Vec2 { c * p.x() - s * p.y(), s * p.x() + c * p.y() };
- };
+ } };
- auto const br
- = center + rotate(smath::Vec2 { half_size.x(), -half_size.y() });
- auto const tr
- = center + rotate(smath::Vec2 { half_size.x(), half_size.y() });
- auto const bl
- = center + rotate(smath::Vec2 { -half_size.x(), -half_size.y() });
- auto const tl
- = center + rotate(smath::Vec2 { -half_size.x(), half_size.y() });
+ auto const br { center
+ + rotate(smath::Vec2 { half_size.x(), -half_size.y() }) };
+ auto const tr { center
+ + rotate(smath::Vec2 { half_size.x(), half_size.y() }) };
+ auto const bl { center
+ + rotate(smath::Vec2 { -half_size.x(), -half_size.y() }) };
+ auto const tl { center
+ + rotate(smath::Vec2 { -half_size.x(), half_size.y() }) };
begin(GeometryKind::Quads);
@@ -710,6 +714,7 @@ VulkanRenderer::~VulkanRenderer()
m_vk.triangle_pipeline_culled.reset();
m_vk.mesh_pipeline.reset();
m_vk.mesh_pipeline_culled.reset();
+ m_vk.wayland_pipeline.reset();
m_vk.default_sampler_linear.reset();
m_vk.default_sampler_nearest.reset();
@@ -783,7 +788,7 @@ auto VulkanRenderer::set_antialiasing_immediate(AntiAliasingKind kind) -> void
auto VulkanRenderer::apply_antialiasing(AntiAliasingKind kind) -> void
{
- auto requested_samples = [&](AntiAliasingKind aa) {
+ auto requested_samples { [&](AntiAliasingKind aa) {
switch (aa) {
case AntiAliasingKind::NONE:
return vk::SampleCountFlagBits::e1;
@@ -795,14 +800,14 @@ auto VulkanRenderer::apply_antialiasing(AntiAliasingKind kind) -> void
return vk::SampleCountFlagBits::e8;
}
return vk::SampleCountFlagBits::e1;
- }(kind);
+ }(kind) };
- auto best_supported = [&](vk::SampleCountFlagBits requested) {
- auto const supported = m_vk.supported_framebuffer_samples;
+ auto best_supported { [&](vk::SampleCountFlagBits requested) {
+ auto const supported { m_vk.supported_framebuffer_samples };
- auto pick_if_supported = [&](vk::SampleCountFlagBits candidate) {
+ auto pick_if_supported { [&](vk::SampleCountFlagBits candidate) {
return (supported & candidate) == candidate;
- };
+ } };
if (requested >= vk::SampleCountFlagBits::e64
&& pick_if_supported(vk::SampleCountFlagBits::e64)) {
@@ -829,9 +834,9 @@ auto VulkanRenderer::apply_antialiasing(AntiAliasingKind kind) -> void
return vk::SampleCountFlagBits::e2;
}
return vk::SampleCountFlagBits::e1;
- }(requested_samples);
+ }(requested_samples) };
- auto kind_for_samples = [](vk::SampleCountFlagBits samples) {
+ auto kind_for_samples { [](vk::SampleCountFlagBits samples) {
switch (samples) {
case vk::SampleCountFlagBits::e2:
return AntiAliasingKind::MSAA_2X;
@@ -842,9 +847,9 @@ auto VulkanRenderer::apply_antialiasing(AntiAliasingKind kind) -> void
default:
return AntiAliasingKind::NONE;
}
- };
+ } };
- auto const effective_kind = kind_for_samples(best_supported);
+ auto const effective_kind { kind_for_samples(best_supported) };
if (m_vk.antialiasing_kind == effective_kind
&& m_vk.msaa_samples == best_supported) {
return;
@@ -933,7 +938,7 @@ auto VulkanRenderer::immediate_submit(
auto VulkanRenderer::setup_kms_surface() -> void
{
- auto const devices = m_instance.enumeratePhysicalDevices();
+ auto const devices { m_instance.enumeratePhysicalDevices() };
if (devices.empty()) {
m_logger.err("No Vulkan physical devices available for KMS");
throw std::runtime_error("App init fail");
@@ -942,10 +947,10 @@ auto VulkanRenderer::setup_kms_surface() -> void
m_logger.info("Found {} Vulkan physical device(s)", devices.size());
for (auto const &device : devices) {
- auto const props = device.getProperties();
+ auto const props { device.getProperties() };
m_logger.info("Checking device: {}", std::string(props.deviceName));
- auto const displays = device.getDisplayPropertiesKHR();
+ auto const displays { device.getDisplayPropertiesKHR() };
if (displays.empty()) {
m_logger.info(" Device has no display properties");
continue;
@@ -968,24 +973,24 @@ auto VulkanRenderer::setup_kms_surface() -> void
m_logger.info(" Display has {} mode(s)", modes.size());
- auto const best_mode_it = std::max_element(modes.begin(),
- modes.end(), [](auto const &lhs, auto const &rhs) {
- auto const lhs_extent = lhs.parameters.visibleRegion;
- auto const rhs_extent = rhs.parameters.visibleRegion;
- auto const lhs_area
- = static_cast(lhs_extent.width)
- * static_cast(lhs_extent.height);
- auto const rhs_area
- = static_cast(rhs_extent.width)
- * static_cast(rhs_extent.height);
+ auto const best_mode_it { std::max_element(modes.begin(),
+ modes.end(), [](auto const &lhs, auto const &rhs) {
+ auto const lhs_extent { lhs.parameters.visibleRegion };
+ auto const rhs_extent { rhs.parameters.visibleRegion };
+ auto const lhs_area { static_cast(
+ lhs_extent.width)
+ * static_cast(lhs_extent.height) };
+ auto const rhs_area { static_cast(
+ rhs_extent.width)
+ * static_cast(rhs_extent.height) };
if (lhs_area == rhs_area) {
return lhs.parameters.refreshRate
< rhs.parameters.refreshRate;
}
return lhs_area < rhs_area;
- });
+ }) };
- auto const planes = device.getDisplayPlanePropertiesKHR();
+ auto const planes { device.getDisplayPlanePropertiesKHR() };
std::optional plane_index;
uint32_t plane_stack_index { 0 };
for (uint32_t i = 0; i < planes.size(); ++i) {
@@ -1009,7 +1014,7 @@ auto VulkanRenderer::setup_kms_surface() -> void
m_logger.info(
" Found suitable display on plane {}", *plane_index);
- auto const extent = best_mode_it->parameters.visibleRegion;
+ auto const extent { best_mode_it->parameters.visibleRegion };
KmsState state {};
state.display = display_props.display;
state.mode = best_mode_it->displayMode;
@@ -1176,7 +1181,7 @@ auto VulkanRenderer::vk_init() -> void
m_logger.warn("KMS display is not on the selected physical device");
}
- auto const props = m_physical_device.getProperties();
+ auto const props { m_physical_device.getProperties() };
m_vk.supported_framebuffer_samples
= props.limits.framebufferColorSampleCounts
& props.limits.framebufferDepthSampleCounts;
@@ -1335,10 +1340,10 @@ auto VulkanRenderer::triangle_pipeline_init() -> void
uint8_t triangle_vert_shader_data[] {
#embed "triangle_vert.spv"
};
- auto triangle_vert_shader = vkutil::load_shader_module(
- std::span(
- triangle_vert_shader_data, sizeof(triangle_vert_shader_data)),
- m_device);
+ auto triangle_vert_shader { vkutil::load_shader_module(
+ std::span(
+ triangle_vert_shader_data, sizeof(triangle_vert_shader_data)),
+ m_device) };
if (!triangle_vert_shader) {
m_logger.err("Failed to load triangle vert shader");
}
@@ -1346,10 +1351,10 @@ auto VulkanRenderer::triangle_pipeline_init() -> void
uint8_t triangle_frag_shader_data[] {
#embed "triangle_frag.spv"
};
- auto triangle_frag_shader = vkutil::load_shader_module(
- std::span(
- triangle_frag_shader_data, sizeof(triangle_frag_shader_data)),
- m_device);
+ auto triangle_frag_shader { vkutil::load_shader_module(
+ std::span(
+ triangle_frag_shader_data, sizeof(triangle_frag_shader_data)),
+ m_device) };
if (!triangle_frag_shader) {
m_logger.err("Failed to load triangle frag shader");
}
@@ -1399,10 +1404,10 @@ auto VulkanRenderer::mesh_pipeline_init() -> void
uint8_t triangle_vert_shader_data[] {
#embed "triangle_mesh_vert.spv"
};
- auto triangle_vert_shader = vkutil::load_shader_module(
- std::span(
- triangle_vert_shader_data, sizeof(triangle_vert_shader_data)),
- m_device);
+ auto triangle_vert_shader { vkutil::load_shader_module(
+ std::span(
+ triangle_vert_shader_data, sizeof(triangle_vert_shader_data)),
+ m_device) };
if (!triangle_vert_shader) {
m_logger.err("Failed to load triangle vert shader");
}
@@ -1410,10 +1415,10 @@ auto VulkanRenderer::mesh_pipeline_init() -> void
uint8_t triangle_frag_shader_data[] {
#embed "tex_image_frag.spv"
};
- auto triangle_frag_shader = vkutil::load_shader_module(
- std::span(
- triangle_frag_shader_data, sizeof(triangle_frag_shader_data)),
- m_device);
+ auto triangle_frag_shader { vkutil::load_shader_module(
+ std::span(
+ triangle_frag_shader_data, sizeof(triangle_frag_shader_data)),
+ m_device) };
if (!triangle_frag_shader) {
m_logger.err("Failed to load triangle frag shader");
}
@@ -1446,6 +1451,24 @@ auto VulkanRenderer::mesh_pipeline_init() -> void
.set_depth_format(
static_cast(m_vk.depth_image.format));
});
+ m_vk.wayland_pipeline
+ = builder.build_graphics([&](GraphicsPipelineBuilder &pipeline_builder)
+ -> GraphicsPipelineBuilder & {
+ return pipeline_builder
+ .set_shaders(
+ triangle_vert_shader.get(), triangle_frag_shader.get())
+ .set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
+ .set_polygon_mode(VK_POLYGON_MODE_FILL)
+ .set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE)
+ .set_multisampling(
+ static_cast(m_vk.msaa_samples))
+ .enable_blending_alpha_blend()
+ .disable_depth_testing()
+ .set_color_attachment_format(
+ static_cast(m_vk.draw_image.format))
+ .set_depth_format(
+ static_cast(m_vk.depth_image.format));
+ });
m_vk.mesh_pipeline_culled
= builder.build_graphics([&](GraphicsPipelineBuilder &pipeline_builder)
-> GraphicsPipelineBuilder & {
@@ -1574,21 +1597,21 @@ auto VulkanRenderer::default_data_init() -> void
{
// Solid color images
- auto const white = smath::pack_unorm4x8(smath::Vec4 { 1, 1, 1, 1 });
+ auto const white { smath::pack_unorm4x8(smath::Vec4 { 1, 1, 1, 1 }) };
m_vk.white_image = create_image(&white, vk::Extent3D { 1, 1, 1 },
vk::Format::eR8G8B8A8Unorm, vk::ImageUsageFlagBits::eSampled);
- auto const black = smath::pack_unorm4x8(smath::Vec4 { 0, 0, 0, 1 });
+ auto const black { smath::pack_unorm4x8(smath::Vec4 { 0, 0, 0, 1 }) };
m_vk.black_image = create_image(&black, vk::Extent3D { 1, 1, 1 },
vk::Format::eR8G8B8A8Unorm, vk::ImageUsageFlagBits::eSampled);
- auto const gray
- = smath::pack_unorm4x8(smath::Vec4 { 0.6f, 0.6f, 0.6f, 1 });
+ auto const gray { smath::pack_unorm4x8(
+ smath::Vec4 { 0.6f, 0.6f, 0.6f, 1 }) };
m_vk.gray_image = create_image(&gray, vk::Extent3D { 1, 1, 1 },
vk::Format::eR8G8B8A8Unorm, vk::ImageUsageFlagBits::eSampled);
// Error checkerboard image
- auto const magenta = smath::pack_unorm4x8(smath::Vec4 { 1, 0, 1, 1 });
+ auto const magenta { smath::pack_unorm4x8(smath::Vec4 { 1, 0, 1, 1 }) };
std::array checkerboard;
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
@@ -1632,7 +1655,7 @@ auto VulkanRenderer::render(std::function const &record) -> void
process_render_commands();
- auto &frame = m_vk.get_current_frame();
+ auto &frame { m_vk.get_current_frame() };
VK_CHECK(m_logger,
m_device.waitForFences(frame.render_fence.get(), true, 1'000'000'000));
frame.deletion_queue.flush();
@@ -1645,8 +1668,8 @@ auto VulkanRenderer::render(std::function const &record) -> void
auto raw_fence { static_cast(frame.render_fence.get()) };
VK_CHECK(m_logger, vkResetFences(m_vkb.dev.device, 1, &raw_fence));
- auto const acquire_result = m_device.acquireNextImageKHR(
- m_vk.swapchain, 1'000'000'000, frame.swapchain_semaphore.get(), {});
+ auto const acquire_result { m_device.acquireNextImageKHR(
+ m_vk.swapchain, 1'000'000'000, frame.swapchain_semaphore.get(), {}) };
if (acquire_result.result == vk::Result::eErrorOutOfDateKHR
|| acquire_result.result == vk::Result::eSuboptimalKHR) {
if (m_use_kms) {
@@ -1806,8 +1829,9 @@ auto VulkanRenderer::render(std::function const &record) -> void
cmd.end();
- auto render_semaphore
- = m_vk.present_semaphores.at(swapchain_image_idx).get();
+ auto render_semaphore {
+ m_vk.present_semaphores.at(swapchain_image_idx).get()
+ };
vk::PipelineStageFlags2 wait_stage
= vk::PipelineStageFlagBits2::eColorAttachmentOutput;
auto wait_info { vkinit::semaphore_submit_info(
@@ -1826,7 +1850,7 @@ auto VulkanRenderer::render(std::function const &record) -> void
present_info.setWaitSemaphores(render_semaphore);
present_info.setImageIndices(swapchain_image_idx);
- auto const present_result = m_vk.graphics_queue.presentKHR(present_info);
+ auto const present_result { m_vk.graphics_queue.presentKHR(present_info) };
if (present_result == vk::Result::eErrorOutOfDateKHR
|| present_result == vk::Result::eSuboptimalKHR) {
if (m_use_kms) {
@@ -1854,7 +1878,7 @@ auto VulkanRenderer::render_to_image(vk::Image target_image,
process_render_commands();
- auto &frame = m_vk.get_current_frame();
+ auto &frame { m_vk.get_current_frame() };
VK_CHECK(m_logger,
m_device.waitForFences(frame.render_fence.get(), true, 1'000'000'000));
frame.deletion_queue.flush();
@@ -2212,7 +2236,7 @@ auto VulkanRenderer::emit_frame_screenshot(FrameData &frame) -> void
destination[i] = source[i + 2];
destination[i + 1] = source[i + 1];
destination[i + 2] = source[i];
- destination[i + 3] = source[i + 3];
+ destination[i + 3] = 0xff;
}
auto const screenshot_flags { vk::ImageUsageFlagBits::eSampled };
@@ -2483,25 +2507,29 @@ auto VulkanRenderer::create_image(void const *data, vk::Extent3D size,
vk::SampleCountFlagBits::e1, mipmapped),
};
- immediate_submit([&](vk::CommandBuffer cmd) {
- vkutil::transition_image(cmd, new_image.image,
- vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal);
+ immediate_submit(
+ [&](vk::CommandBuffer cmd) {
+ vkutil::transition_image(cmd, new_image.image,
+ vk::ImageLayout::eUndefined,
+ vk::ImageLayout::eTransferDstOptimal);
- vk::BufferImageCopy copy_region {};
- copy_region.imageSubresource.aspectMask
- = vk::ImageAspectFlagBits::eColor;
- copy_region.imageSubresource.mipLevel = 0;
- copy_region.imageSubresource.baseArrayLayer = 0;
- copy_region.imageSubresource.layerCount = 1;
- copy_region.imageExtent = size;
+ vk::BufferImageCopy copy_region {};
+ copy_region.imageSubresource.aspectMask
+ = vk::ImageAspectFlagBits::eColor;
+ copy_region.imageSubresource.mipLevel = 0;
+ copy_region.imageSubresource.baseArrayLayer = 0;
+ copy_region.imageSubresource.layerCount = 1;
+ copy_region.imageExtent = size;
- cmd.copyBufferToImage(upload_buffer.buffer, new_image.image,
- vk::ImageLayout::eTransferDstOptimal, copy_region);
+ cmd.copyBufferToImage(upload_buffer.buffer, new_image.image,
+ vk::ImageLayout::eTransferDstOptimal, copy_region);
- vkutil::transition_image(cmd, new_image.image,
- vk::ImageLayout::eTransferDstOptimal,
- vk::ImageLayout::eShaderReadOnlyOptimal);
- });
+ vkutil::transition_image(cmd, new_image.image,
+ vk::ImageLayout::eTransferDstOptimal,
+ vk::ImageLayout::eShaderReadOnlyOptimal);
+ },
+ /*flush_frame_deletion_queue=*/false,
+ /*clear_frame_descriptors=*/false);
if (mapped_here) {
vmaUnmapMemory(m_vk.allocator, upload_buffer.allocation);
@@ -2580,58 +2608,63 @@ auto VulkanRenderer::create_cubemap(std::span pixels,
view_ci.subresourceRange.layerCount = 6;
new_image.image_view = m_device.createImageView(view_ci);
- immediate_submit([&](vk::CommandBuffer cmd) {
- vk::ImageMemoryBarrier to_transfer {};
- to_transfer.srcAccessMask = vk::AccessFlagBits::eNone;
- to_transfer.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
- to_transfer.oldLayout = vk::ImageLayout::eUndefined;
- to_transfer.newLayout = vk::ImageLayout::eTransferDstOptimal;
- to_transfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
- to_transfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
- to_transfer.image = new_image.image;
- to_transfer.subresourceRange.aspectMask
- = vk::ImageAspectFlagBits::eColor;
- to_transfer.subresourceRange.baseMipLevel = 0;
- to_transfer.subresourceRange.levelCount = 1;
- to_transfer.subresourceRange.baseArrayLayer = 0;
- to_transfer.subresourceRange.layerCount = 6;
+ immediate_submit(
+ [&](vk::CommandBuffer cmd) {
+ vk::ImageMemoryBarrier to_transfer {};
+ to_transfer.srcAccessMask = vk::AccessFlagBits::eNone;
+ to_transfer.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
+ to_transfer.oldLayout = vk::ImageLayout::eUndefined;
+ to_transfer.newLayout = vk::ImageLayout::eTransferDstOptimal;
+ to_transfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ to_transfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ to_transfer.image = new_image.image;
+ to_transfer.subresourceRange.aspectMask
+ = vk::ImageAspectFlagBits::eColor;
+ to_transfer.subresourceRange.baseMipLevel = 0;
+ to_transfer.subresourceRange.levelCount = 1;
+ to_transfer.subresourceRange.baseArrayLayer = 0;
+ to_transfer.subresourceRange.layerCount = 6;
- cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
- vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, to_transfer);
+ cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
+ vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, to_transfer);
- std::array regions {};
- for (uint32_t layer = 0; layer < 6; ++layer) {
- vk::BufferImageCopy region {};
- region.bufferOffset = face_bytes * layer;
- region.imageSubresource.aspectMask
- = vk::ImageAspectFlagBits::eColor;
- region.imageSubresource.mipLevel = 0;
- region.imageSubresource.baseArrayLayer = layer;
- region.imageSubresource.layerCount = 1;
- region.imageExtent = new_image.extent;
- regions[layer] = region;
- }
+ std::array regions {};
+ for (uint32_t layer = 0; layer < 6; ++layer) {
+ vk::BufferImageCopy region {};
+ region.bufferOffset = face_bytes * layer;
+ region.imageSubresource.aspectMask
+ = vk::ImageAspectFlagBits::eColor;
+ region.imageSubresource.mipLevel = 0;
+ region.imageSubresource.baseArrayLayer = layer;
+ region.imageSubresource.layerCount = 1;
+ region.imageExtent = new_image.extent;
+ regions[layer] = region;
+ }
- cmd.copyBufferToImage(upload_buffer.buffer, new_image.image,
- vk::ImageLayout::eTransferDstOptimal, regions);
+ cmd.copyBufferToImage(upload_buffer.buffer, new_image.image,
+ vk::ImageLayout::eTransferDstOptimal, regions);
- vk::ImageMemoryBarrier to_read {};
- to_read.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
- to_read.dstAccessMask = vk::AccessFlagBits::eShaderRead;
- to_read.oldLayout = vk::ImageLayout::eTransferDstOptimal;
- to_read.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
- to_read.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
- to_read.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
- to_read.image = new_image.image;
- to_read.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
- to_read.subresourceRange.baseMipLevel = 0;
- to_read.subresourceRange.levelCount = 1;
- to_read.subresourceRange.baseArrayLayer = 0;
- to_read.subresourceRange.layerCount = 6;
+ vk::ImageMemoryBarrier to_read {};
+ to_read.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
+ to_read.dstAccessMask = vk::AccessFlagBits::eShaderRead;
+ to_read.oldLayout = vk::ImageLayout::eTransferDstOptimal;
+ to_read.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
+ to_read.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ to_read.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ to_read.image = new_image.image;
+ to_read.subresourceRange.aspectMask
+ = vk::ImageAspectFlagBits::eColor;
+ to_read.subresourceRange.baseMipLevel = 0;
+ to_read.subresourceRange.levelCount = 1;
+ to_read.subresourceRange.baseArrayLayer = 0;
+ to_read.subresourceRange.layerCount = 6;
- cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
- vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {}, to_read);
- });
+ cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
+ vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {},
+ to_read);
+ },
+ /*flush_frame_deletion_queue=*/false,
+ /*clear_frame_descriptors=*/false);
if (mapped_here) {
vmaUnmapMemory(m_vk.allocator, upload_buffer.allocation);
@@ -2650,6 +2683,12 @@ auto VulkanRenderer::destroy_image(AllocatedImage const &img) -> void
m_vk.allocator, static_cast(img.image), img.allocation);
}
+auto VulkanRenderer::destroy_image_later(AllocatedImage img) -> void
+{
+ m_vk.get_current_frame().deletion_queue.emplace(
+ [this, img]() { destroy_image(img); });
+}
+
auto VulkanRenderer::create_buffer(size_t alloc_size,
vk::BufferUsageFlags usage, VmaMemoryUsage memory_usage) -> AllocatedBuffer
{
diff --git a/src/VulkanRenderer.h b/src/VulkanRenderer.h
index dca1fd6..7167aac 100644
--- a/src/VulkanRenderer.h
+++ b/src/VulkanRenderer.h
@@ -164,6 +164,7 @@ struct VulkanRenderer {
auto create_cubemap(std::span pixels, uint32_t face_size,
vk::Format format, vk::ImageUsageFlags flags) -> AllocatedImage;
auto destroy_image(AllocatedImage const &img) -> void;
+ auto destroy_image_later(AllocatedImage img) -> void;
auto rectangle_mesh() const -> GPUMeshBuffers const &
{
return m_vk.rectangle;
@@ -186,6 +187,7 @@ struct VulkanRenderer {
}
auto draw_extent() const -> vk::Extent2D { return m_vk.draw_extent; }
auto mesh_pipeline() -> Pipeline & { return m_vk.mesh_pipeline; }
+ auto wayland_pipeline() -> Pipeline & { return m_vk.wayland_pipeline; }
auto triangle_pipeline() -> Pipeline & { return m_vk.triangle_pipeline; }
auto device() const -> vk::Device { return m_device; }
auto instance() const -> vk::Instance { return m_instance; }
@@ -359,6 +361,7 @@ private:
Pipeline triangle_pipeline_culled;
Pipeline mesh_pipeline;
Pipeline mesh_pipeline_culled;
+ Pipeline wayland_pipeline;
GPUMeshBuffers rectangle;
diff --git a/src/wayland/Client.h b/src/wayland/Client.h
index 07bdf1d..72b1628 100644
--- a/src/wayland/Client.h
+++ b/src/wayland/Client.h
@@ -9,6 +9,7 @@
#include
#include "Display.h"
+#include "List.h"
namespace Lunar::Wayland {
@@ -46,7 +47,7 @@ struct Client {
auto get_object(uint32_t id) -> std::optional
{
- if (auto *res = wl_client_get_object(m_client, id); res != NULL) {
+ if (auto *res { wl_client_get_object(m_client, id) }; res != NULL) {
return res;
} else {
return {};
@@ -77,7 +78,7 @@ struct Client {
wl_client_add_destroy_late_listener(m_client, listener);
}
- auto get_link() -> List { return wl_client_get_link(m_client); }
+ auto get_link() -> wl_list * { return wl_client_get_link(m_client); }
auto add_resource_created_listener(wl_listener *listener)
{
@@ -92,12 +93,12 @@ struct Client {
(wl_client_for_each_resource_iterator_func_t)[](
wl_resource * res, void *user_data)
->wl_iterator_result {
- auto *f = static_cast<
- std::function *>(
- user_data);
+ auto *f { static_cast<
+ std::function *>(
+ user_data) };
return (*f)(res);
},
- &fn);
+ const_cast(static_cast(&fn)));
}
auto set_max_buffer_size(size_t max_buffer_size)
diff --git a/src/wayland/Display.h b/src/wayland/Display.h
index d9905cc..1faee0c 100644
--- a/src/wayland/Display.h
+++ b/src/wayland/Display.h
@@ -64,8 +64,8 @@ struct Display {
auto add_protocol_logger(wl_protocol_logger_func_t func, void *user_data)
-> wl_protocol_logger *
{
- if (auto *logger
- = wl_display_add_protocol_logger(m_display, func, user_data);
+ if (auto *logger {
+ wl_display_add_protocol_logger(m_display, func, user_data) };
logger != NULL) {
return logger;
} else {
@@ -76,7 +76,7 @@ struct Display {
auto add_shm_format(uint32_t format) -> uint32_t *
{
- if (auto *fmt = wl_display_add_shm_format(m_display, format);
+ if (auto *fmt { wl_display_add_shm_format(m_display, format) };
fmt != NULL) {
return fmt;
} else {
diff --git a/src/wayland/Global.h b/src/wayland/Global.h
index 281062c..a87473f 100644
--- a/src/wayland/Global.h
+++ b/src/wayland/Global.h
@@ -40,7 +40,7 @@ struct Global {
auto get_name(Client &client) const -> std::optional
{
- if (auto const ret = wl_global_get_name(m_global, client.c_ptr());
+ if (auto const ret { wl_global_get_name(m_global, client.c_ptr()) };
ret != 0) {
return ret;
} else {
diff --git a/src/wayland/List.h b/src/wayland/List.h
index 4c97d64..7e2902f 100644
--- a/src/wayland/List.h
+++ b/src/wayland/List.h
@@ -19,15 +19,16 @@ constexpr std::ptrdiff_t member_offset() noexcept
template
inline T *container_of(wl_list *node) noexcept
{
- auto *p = reinterpret_cast(node) - member_offset();
+ auto *p { reinterpret_cast(node)
+ - member_offset() };
return reinterpret_cast(p);
}
template
inline T const *container_of(wl_list const *node) noexcept
{
- auto *p = reinterpret_cast(node)
- - member_offset();
+ auto *p { reinterpret_cast(node)
+ - member_offset() };
return reinterpret_cast(p);
}
@@ -64,7 +65,7 @@ template struct List {
}
auto operator++(int) -> Iterator
{
- auto t = *this;
+ auto t { *this };
++(*this);
return t;
}
@@ -76,7 +77,7 @@ template struct List {
}
auto operator--(int) -> Iterator
{
- auto t = *this;
+ auto t { *this };
--(*this);
return t;
}
@@ -125,7 +126,7 @@ template struct List {
}
auto operator++(int) -> ConstIterator
{
- auto t = *this;
+ auto t { *this };
++(*this);
return t;
}
@@ -137,7 +138,7 @@ template struct List {
}
auto operator--(int) -> ConstIterator
{
- auto t = *this;
+ auto t { *this };
--(*this);
return t;
}
@@ -180,7 +181,7 @@ template struct List {
return;
clear();
- if (auto *h = head_ptr(); h)
+ if (auto *h { head_ptr() }; h)
wl_list_init(h);
}
@@ -211,7 +212,7 @@ template struct List {
auto push_back(T *elem) noexcept -> void
{
- auto *h = head_ptr();
+ auto *h { head_ptr() };
wl_list_insert(h->prev, &(elem->*Member));
}
@@ -223,9 +224,9 @@ template struct List {
auto clear() noexcept -> void
{
- auto *h = head_ptr();
+ auto *h { head_ptr() };
while (!wl_list_empty(h)) {
- auto *node = h->next;
+ auto *node { h->next };
wl_list_remove(node);
wl_list_init(node);
}
@@ -233,25 +234,25 @@ template struct List {
auto begin() noexcept -> Iterator
{
- auto *h = head_ptr();
+ auto *h { head_ptr() };
return Iterator(h->next, h);
}
auto end() noexcept -> Iterator
{
- auto *h = head_ptr();
+ auto *h { head_ptr() };
return Iterator(h, h);
}
auto begin() const noexcept -> ConstIterator
{
- auto const *h = head_ptr();
+ auto const *h { head_ptr() };
return ConstIterator(h->next, h);
}
auto end() const noexcept -> ConstIterator
{
- auto const *h = head_ptr();
+ auto const *h { head_ptr() };
return ConstIterator(h, h);
}
diff --git a/src/wayland/Region.h b/src/wayland/Region.h
new file mode 100644
index 0000000..8498c0e
--- /dev/null
+++ b/src/wayland/Region.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include
+#include
+
+#include
+
+namespace Lunar::Wayland {
+
+struct Region {
+ struct Box {
+ std::int32_t x {};
+ std::int32_t y {};
+ std::int32_t width {};
+ std::int32_t height {};
+ };
+
+ explicit Region(wl_resource *resource)
+ : m_resource(resource)
+ {
+ }
+
+ auto resource() const -> wl_resource * { return m_resource; }
+
+ auto add(std::int32_t x, std::int32_t y, std::int32_t width,
+ std::int32_t height) -> void
+ {
+ m_boxes.push_back(Box { x, y, width, height });
+ }
+
+ auto subtract(std::int32_t x, std::int32_t y, std::int32_t width,
+ std::int32_t height) -> void
+ {
+ m_subtract_boxes.push_back(Box { x, y, width, height });
+ }
+
+private:
+ wl_resource *m_resource {};
+ std::vector m_boxes {};
+ std::vector m_subtract_boxes {};
+};
+
+} // namespace Lunar::Wayland
diff --git a/src/wayland/Shm.cpp b/src/wayland/Shm.cpp
new file mode 100644
index 0000000..6309f45
--- /dev/null
+++ b/src/wayland/Shm.cpp
@@ -0,0 +1,96 @@
+#include "Shm.h"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace Lunar::Wayland {
+
+ShmPool::ShmPool(int fd, std::size_t size, Logger &logger)
+ : m_logger(logger)
+ , m_fd(fd)
+ , m_size(size)
+{
+ m_data = mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
+ if (m_data == MAP_FAILED) {
+ m_data = nullptr;
+ m_logger.err("Failed to mmap shm pool: {}", std::strerror(errno));
+ }
+}
+
+ShmPool::~ShmPool()
+{
+ if (m_data) {
+ munmap(m_data, m_size);
+ }
+ if (m_fd >= 0) {
+ close(m_fd);
+ }
+}
+
+auto ShmPool::resize(std::size_t new_size) -> bool
+{
+ if (!m_data) {
+ return false;
+ }
+ void *new_data = mremap(m_data, m_size, new_size, MREMAP_MAYMOVE);
+ if (new_data == MAP_FAILED) {
+ m_logger.err("Failed to resize shm pool: {}", std::strerror(errno));
+ return false;
+ }
+ m_data = new_data;
+ m_size = new_size;
+ return true;
+}
+
+auto ShmPool::data() const -> std::byte *
+{
+ return static_cast(m_data);
+}
+
+ShmBuffer::ShmBuffer(std::shared_ptr pool, wl_resource *resource,
+ std::int32_t offset, std::int32_t width, std::int32_t height,
+ std::int32_t stride, std::uint32_t format)
+ : pool(std::move(pool))
+ , resource(resource)
+ , offset(offset)
+ , width(width)
+ , height(height)
+ , stride(stride)
+ , format(format)
+{
+}
+
+auto ShmBuffer::data() const -> std::byte *
+{
+ if (!pool || !pool->data()) {
+ return nullptr;
+ }
+ return pool->data() + offset;
+}
+
+auto ShmBuffer::byte_size() const -> std::size_t
+{
+ if (height <= 0 || stride <= 0) {
+ return 0;
+ }
+ return static_cast(height) * static_cast(stride);
+}
+
+auto shm_buffer_from_resource(wl_resource *resource)
+ -> std::shared_ptr
+{
+ if (!resource) {
+ return {};
+ }
+ auto *handle { static_cast *>(
+ wl_resource_get_user_data(resource)) };
+ if (!handle) {
+ return {};
+ }
+ return *handle;
+}
+
+} // namespace Lunar::Wayland
diff --git a/src/wayland/Shm.h b/src/wayland/Shm.h
new file mode 100644
index 0000000..522b2ce
--- /dev/null
+++ b/src/wayland/Shm.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include
+
+#include "../Logger.h"
+
+namespace Lunar::Wayland {
+
+struct ShmPool {
+ explicit ShmPool(int fd, std::size_t size, Logger &logger);
+ ~ShmPool();
+
+ auto resize(std::size_t new_size) -> bool;
+ auto data() const -> std::byte *;
+ auto size() const -> std::size_t { return m_size; }
+
+private:
+ Logger &m_logger;
+ int m_fd { -1 };
+ std::size_t m_size { 0 };
+ void *m_data { nullptr };
+};
+
+struct ShmBuffer {
+ ShmBuffer(std::shared_ptr pool, wl_resource *resource,
+ std::int32_t offset, std::int32_t width, std::int32_t height,
+ std::int32_t stride, std::uint32_t format);
+
+ auto data() const -> std::byte *;
+ auto byte_size() const -> std::size_t;
+
+ std::shared_ptr pool;
+ wl_resource *resource {};
+ std::int32_t offset {};
+ std::int32_t width {};
+ std::int32_t height {};
+ std::int32_t stride {};
+ std::uint32_t format {};
+};
+
+auto shm_buffer_from_resource(wl_resource *resource)
+ -> std::shared_ptr;
+
+} // namespace Lunar::Wayland
diff --git a/src/wayland/Surface.cpp b/src/wayland/Surface.cpp
new file mode 100644
index 0000000..3d5624f
--- /dev/null
+++ b/src/wayland/Surface.cpp
@@ -0,0 +1,98 @@
+#include "Surface.h"
+
+#include
+#include
+
+#include "WaylandServer.h"
+
+#include "wayland-server-protocol.h"
+
+namespace Lunar::Wayland {
+
+Surface::Surface(WaylandServer &server, wl_resource *resource)
+ : m_server(server)
+ , m_resource(resource)
+{
+ m_server.register_surface(this);
+}
+
+Surface::~Surface()
+{
+ m_server.unregister_surface(this);
+ for (auto *callback : m_frame_callbacks) {
+ if (callback) {
+ wl_callback_send_done(callback, m_server.now_ms());
+ wl_resource_destroy(callback);
+ }
+ }
+}
+
+auto Surface::attach(
+ std::shared_ptr buffer, std::int32_t x, std::int32_t y) -> void
+{
+ m_pending_buffer = std::move(buffer);
+ m_pending_offset_x = x;
+ m_pending_offset_y = y;
+}
+
+auto Surface::damage(std::int32_t, std::int32_t, std::int32_t, std::int32_t)
+ -> void
+{
+}
+
+auto Surface::damage_buffer(
+ std::int32_t, std::int32_t, std::int32_t, std::int32_t) -> void
+{
+}
+
+auto Surface::frame(wl_resource *callback) -> void
+{
+ if (!callback) {
+ return;
+ }
+ m_frame_callbacks.push_back(callback);
+}
+
+auto Surface::commit() -> void
+{
+ auto previous { m_current_buffer };
+ m_current_buffer = m_pending_buffer;
+ m_pending_buffer.reset();
+
+ if (previous && previous != m_current_buffer && previous->resource) {
+ wl_buffer_send_release(previous->resource);
+ }
+
+ if (!m_frame_callbacks.empty()) {
+ auto callbacks { std::move(m_frame_callbacks) };
+ auto done_time { m_server.now_ms() };
+ for (auto *callback : callbacks) {
+ if (callback) {
+ wl_callback_send_done(callback, done_time);
+ wl_resource_destroy(callback);
+ }
+ }
+ }
+}
+
+auto Surface::set_opaque_region(std::shared_ptr region) -> void
+{
+ m_opaque_region = std::move(region);
+}
+
+auto Surface::set_input_region(std::shared_ptr region) -> void
+{
+ m_input_region = std::move(region);
+}
+
+auto Surface::set_buffer_transform(std::int32_t transform) -> void
+{
+ m_buffer_transform = transform;
+}
+
+auto Surface::set_buffer_scale(std::int32_t scale) -> void
+{
+ m_buffer_scale = std::max(1, scale);
+}
+
+} // namespace Lunar::Wayland
diff --git a/src/wayland/Surface.h b/src/wayland/Surface.h
new file mode 100644
index 0000000..4463794
--- /dev/null
+++ b/src/wayland/Surface.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include
+
+#include "Region.h"
+#include "Shm.h"
+
+namespace Lunar::Wayland {
+
+struct WaylandServer;
+
+struct Surface {
+ explicit Surface(WaylandServer &server, wl_resource *resource);
+ ~Surface();
+
+ auto resource() const -> wl_resource * { return m_resource; }
+ auto current_buffer() const -> std::shared_ptr const &
+ {
+ return m_current_buffer;
+ }
+
+ auto attach(std::shared_ptr buffer, std::int32_t x,
+ std::int32_t y) -> void;
+ auto damage(std::int32_t x, std::int32_t y, std::int32_t width,
+ std::int32_t height) -> void;
+ auto damage_buffer(std::int32_t x, std::int32_t y, std::int32_t width,
+ std::int32_t height) -> void;
+ auto frame(wl_resource *callback) -> void;
+ auto commit() -> void;
+ auto set_opaque_region(std::shared_ptr region) -> void;
+ auto set_input_region(std::shared_ptr region) -> void;
+ auto set_buffer_transform(std::int32_t transform) -> void;
+ auto set_buffer_scale(std::int32_t scale) -> void;
+
+private:
+ WaylandServer &m_server;
+ wl_resource *m_resource {};
+ std::shared_ptr m_pending_buffer {};
+ std::shared_ptr m_current_buffer {};
+ std::shared_ptr m_opaque_region {};
+ std::shared_ptr m_input_region {};
+ std::vector m_frame_callbacks {};
+ std::int32_t m_buffer_transform { 0 };
+ std::int32_t m_buffer_scale { 1 };
+ std::int32_t m_pending_offset_x { 0 };
+ std::int32_t m_pending_offset_y { 0 };
+};
+
+} // namespace Lunar::Wayland
diff --git a/src/wayland/WaylandServer.cpp b/src/wayland/WaylandServer.cpp
new file mode 100644
index 0000000..421b9e4
--- /dev/null
+++ b/src/wayland/WaylandServer.cpp
@@ -0,0 +1,81 @@
+#include "WaylandServer.h"
+
+#include
+#include
+#include
+#include
+
+namespace Lunar::Wayland {
+
+WaylandServer::WaylandServer(Logger &logger)
+ : m_logger(logger)
+{
+ m_loop = wl_display_get_event_loop(m_display.c_ptr());
+ if (!m_loop) {
+ throw std::runtime_error("Failed to get Wayland event loop");
+ }
+
+ auto *socket_name { wl_display_add_socket_auto(m_display.c_ptr()) };
+ if (!socket_name) {
+ throw std::runtime_error("Failed to create Wayland socket");
+ }
+ if (socket_name) {
+ m_socket_name = socket_name;
+ }
+
+ m_logger.info("Wayland listening on {}", m_socket_name);
+
+ m_compositor_global = create_compositor_global();
+ m_shm_global = create_shm_global();
+ m_xdg_wm_base_global = create_xdg_wm_base_global();
+}
+
+WaylandServer::~WaylandServer() = default;
+
+auto WaylandServer::dispatch() -> void
+{
+ if (!m_loop) {
+ return;
+ }
+ wl_event_loop_dispatch(m_loop, 0);
+}
+
+auto WaylandServer::flush() -> void
+{
+ wl_display_flush_clients(m_display.c_ptr());
+}
+
+auto WaylandServer::now_ms() const -> std::uint32_t
+{
+ using Clock = std::chrono::steady_clock;
+ auto now { Clock::now().time_since_epoch() };
+ auto ms {
+ std::chrono::duration_cast(now).count()
+ };
+ return static_cast(ms);
+}
+
+auto WaylandServer::register_surface(Surface *surface) -> void
+{
+ if (!surface) {
+ return;
+ }
+ m_surfaces.push_back(surface);
+}
+
+auto WaylandServer::unregister_surface(Surface *surface) -> void
+{
+ if (!surface) {
+ return;
+ }
+ auto it { std::remove(m_surfaces.begin(), m_surfaces.end(), surface) };
+ m_surfaces.erase(it, m_surfaces.end());
+}
+
+auto WaylandServer::surfaces() const -> std::span
+{
+ return { m_surfaces.data(), m_surfaces.size() };
+}
+
+} // namespace Lunar::Wayland
+#include "Surface.h"
diff --git a/src/wayland/WaylandServer.h b/src/wayland/WaylandServer.h
new file mode 100644
index 0000000..b800484
--- /dev/null
+++ b/src/wayland/WaylandServer.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "../Logger.h"
+
+#include "Display.h"
+#include "Global.h"
+
+namespace Lunar::Wayland {
+
+struct Surface;
+
+struct WaylandServer {
+ explicit WaylandServer(Logger &logger);
+ ~WaylandServer();
+
+ auto display() -> Display & { return m_display; }
+ auto logger() -> Logger & { return m_logger; }
+ auto socket_name() const -> std::string_view { return m_socket_name; }
+ auto dispatch() -> void;
+ auto flush() -> void;
+ auto now_ms() const -> std::uint32_t;
+ auto register_surface(Surface *surface) -> void;
+ auto unregister_surface(Surface *surface) -> void;
+ auto surfaces() const -> std::span;
+
+private:
+ auto create_compositor_global() -> std::unique_ptr;
+ auto create_shm_global() -> std::unique_ptr;
+ auto create_xdg_wm_base_global() -> std::unique_ptr;
+
+ Logger &m_logger;
+ Display m_display {};
+ wl_event_loop *m_loop { nullptr };
+ std::string m_socket_name {};
+ std::unique_ptr m_compositor_global {};
+ std::unique_ptr m_shm_global {};
+ std::unique_ptr m_xdg_wm_base_global {};
+ std::vector m_surfaces {};
+};
+
+} // namespace Lunar::Wayland
diff --git a/src/wayland/protocols/CompositorProtocol.cpp b/src/wayland/protocols/CompositorProtocol.cpp
new file mode 100644
index 0000000..811b57e
--- /dev/null
+++ b/src/wayland/protocols/CompositorProtocol.cpp
@@ -0,0 +1,254 @@
+#include
+#include
+#include
+#include
+
+#include
+
+#include "wayland-server-protocol.h"
+
+#include "../Region.h"
+#include "../Shm.h"
+#include "../Surface.h"
+#include "../WaylandServer.h"
+
+namespace Lunar::Wayland {
+namespace {
+
+constexpr std::uint32_t COMPOSITOR_VERSION = 4;
+
+auto resource_version(wl_resource *resource) -> std::uint32_t
+{
+ return std::min(
+ wl_resource_get_version(resource), COMPOSITOR_VERSION);
+}
+
+auto region_from_resource(wl_resource *resource) -> std::shared_ptr
+{
+ if (!resource) {
+ return {};
+ }
+ auto *handle { static_cast *>(
+ wl_resource_get_user_data(resource)) };
+ if (!handle) {
+ return {};
+ }
+ return *handle;
+}
+
+void region_destroy(wl_resource *resource)
+{
+ auto *handle { static_cast *>(
+ wl_resource_get_user_data(resource)) };
+ delete handle;
+}
+
+void region_handle_destroy(wl_client *, wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+void region_handle_add(wl_client *, wl_resource *resource, std::int32_t x,
+ std::int32_t y, std::int32_t width, std::int32_t height)
+{
+ if (auto region { region_from_resource(resource) }) {
+ region->add(x, y, width, height);
+ }
+}
+
+void region_handle_subtract(wl_client *, wl_resource *resource, std::int32_t x,
+ std::int32_t y, std::int32_t width, std::int32_t height)
+{
+ if (auto region { region_from_resource(resource) }) {
+ region->subtract(x, y, width, height);
+ }
+}
+
+struct wl_region_interface const REGION_INTERFACE = {
+ .destroy = region_handle_destroy,
+ .add = region_handle_add,
+ .subtract = region_handle_subtract,
+};
+
+auto surface_from_resource(wl_resource *resource) -> Surface *
+{
+ return static_cast(wl_resource_get_user_data(resource));
+}
+
+void surface_destroy_resource(wl_resource *resource)
+{
+ auto *surface { surface_from_resource(resource) };
+ delete surface;
+}
+
+void surface_handle_destroy(wl_client *, wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+void surface_handle_attach(wl_client *, wl_resource *resource,
+ wl_resource *buffer_resource, std::int32_t x, std::int32_t y)
+{
+ auto *surface { surface_from_resource(resource) };
+ if (!surface) {
+ return;
+ }
+ surface->attach(shm_buffer_from_resource(buffer_resource), x, y);
+}
+
+void surface_handle_damage(wl_client *, wl_resource *resource, std::int32_t x,
+ std::int32_t y, std::int32_t width, std::int32_t height)
+{
+ if (auto *surface { surface_from_resource(resource) }) {
+ surface->damage(x, y, width, height);
+ }
+}
+
+void surface_handle_frame(
+ wl_client *client, wl_resource *resource, std::uint32_t callback_id)
+{
+ auto *surface { surface_from_resource(resource) };
+ if (!surface) {
+ return;
+ }
+ auto version { wl_resource_get_version(resource) };
+ auto *callback_resource { wl_resource_create(
+ client, &wl_callback_interface, version, callback_id) };
+ if (!callback_resource) {
+ return;
+ }
+ wl_resource_set_implementation(
+ callback_resource, nullptr, nullptr, nullptr);
+ surface->frame(callback_resource);
+}
+
+void surface_handle_set_opaque_region(
+ wl_client *, wl_resource *resource, wl_resource *region_resource)
+{
+ if (auto *surface { surface_from_resource(resource) }) {
+ surface->set_opaque_region(region_from_resource(region_resource));
+ }
+}
+
+void surface_handle_set_input_region(
+ wl_client *, wl_resource *resource, wl_resource *region_resource)
+{
+ if (auto *surface { surface_from_resource(resource) }) {
+ surface->set_input_region(region_from_resource(region_resource));
+ }
+}
+
+void surface_handle_commit(wl_client *, wl_resource *resource)
+{
+ if (auto *surface { surface_from_resource(resource) }) {
+ surface->commit();
+ }
+}
+
+void surface_handle_set_buffer_transform(
+ wl_client *, wl_resource *resource, std::int32_t transform)
+{
+ if (auto *surface { surface_from_resource(resource) }) {
+ surface->set_buffer_transform(transform);
+ }
+}
+
+void surface_handle_set_buffer_scale(
+ wl_client *, wl_resource *resource, std::int32_t scale)
+{
+ if (auto *surface { surface_from_resource(resource) }) {
+ surface->set_buffer_scale(scale);
+ }
+}
+
+void surface_handle_damage_buffer(wl_client *, wl_resource *resource,
+ std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
+{
+ if (auto *surface { surface_from_resource(resource) }) {
+ surface->damage_buffer(x, y, width, height);
+ }
+}
+
+void surface_handle_offset(
+ wl_client *, wl_resource *resource, std::int32_t x, std::int32_t y)
+{
+ (void)resource;
+ (void)x;
+ (void)y;
+}
+
+struct wl_surface_interface const SURFACE_INTERFACE = {
+ .destroy = surface_handle_destroy,
+ .attach = surface_handle_attach,
+ .damage = surface_handle_damage,
+ .frame = surface_handle_frame,
+ .set_opaque_region = surface_handle_set_opaque_region,
+ .set_input_region = surface_handle_set_input_region,
+ .commit = surface_handle_commit,
+ .set_buffer_transform = surface_handle_set_buffer_transform,
+ .set_buffer_scale = surface_handle_set_buffer_scale,
+ .damage_buffer = surface_handle_damage_buffer,
+ .offset = surface_handle_offset,
+};
+
+void compositor_handle_create_surface(
+ wl_client *client, wl_resource *resource, std::uint32_t id)
+{
+ auto *server { static_cast(
+ wl_resource_get_user_data(resource)) };
+ if (!server) {
+ return;
+ }
+ auto version { resource_version(resource) };
+ auto *surface_resource { wl_resource_create(
+ client, &wl_surface_interface, version, id) };
+ if (!surface_resource) {
+ return;
+ }
+ auto *surface { new Surface(*server, surface_resource) };
+ wl_resource_set_implementation(surface_resource, &SURFACE_INTERFACE,
+ surface, surface_destroy_resource);
+}
+
+void compositor_handle_create_region(
+ wl_client *client, wl_resource *resource, std::uint32_t id)
+{
+ auto version { wl_resource_get_version(resource) };
+ auto *region_resource { wl_resource_create(
+ client, &wl_region_interface, version, id) };
+ if (!region_resource) {
+ return;
+ }
+ auto region { std::make_shared(region_resource) };
+ auto *handle { new std::shared_ptr(std::move(region)) };
+ wl_resource_set_implementation(
+ region_resource, ®ION_INTERFACE, handle, region_destroy);
+}
+
+struct wl_compositor_interface const COMPOSITOR_INTERFACE = {
+ .create_surface = compositor_handle_create_surface,
+ .create_region = compositor_handle_create_region,
+};
+
+void bind_compositor(
+ wl_client *client, void *data, std::uint32_t version, std::uint32_t id)
+{
+ auto *server { static_cast(data) };
+ auto *resource { wl_resource_create(client, &wl_compositor_interface,
+ std::min(version, COMPOSITOR_VERSION), id) };
+ if (!resource) {
+ return;
+ }
+ wl_resource_set_implementation(
+ resource, &COMPOSITOR_INTERFACE, server, nullptr);
+}
+
+} // namespace
+
+auto WaylandServer::create_compositor_global() -> std::unique_ptr
+{
+ return std::make_unique(display(), &wl_compositor_interface,
+ COMPOSITOR_VERSION, this, bind_compositor);
+}
+
+} // namespace Lunar::Wayland
diff --git a/src/wayland/protocols/ShmProtocol.cpp b/src/wayland/protocols/ShmProtocol.cpp
new file mode 100644
index 0000000..82e22e5
--- /dev/null
+++ b/src/wayland/protocols/ShmProtocol.cpp
@@ -0,0 +1,171 @@
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "wayland-server-protocol.h"
+
+#include "../Shm.h"
+#include "../WaylandServer.h"
+
+namespace Lunar::Wayland {
+namespace {
+
+constexpr std::uint32_t SHM_VERSION = 2;
+
+auto shm_pool_from_resource(wl_resource *resource) -> std::shared_ptr
+{
+ auto *handle { static_cast *>(
+ wl_resource_get_user_data(resource)) };
+ if (!handle) {
+ return {};
+ }
+ return *handle;
+}
+
+void shm_pool_destroy_resource(wl_resource *resource)
+{
+ auto *handle { static_cast *>(
+ wl_resource_get_user_data(resource)) };
+ delete handle;
+}
+
+void shm_buffer_destroy_resource(wl_resource *resource)
+{
+ auto *handle { static_cast *>(
+ wl_resource_get_user_data(resource)) };
+ delete handle;
+}
+
+void shm_pool_handle_destroy(wl_client *, wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+void shm_pool_handle_resize(
+ wl_client *, wl_resource *resource, std::int32_t size)
+{
+ auto pool { shm_pool_from_resource(resource) };
+ if (!pool) {
+ return;
+ }
+ if (size <= 0) {
+ return;
+ }
+ pool->resize(static_cast(size));
+}
+
+void shm_handle_release(wl_client *, wl_resource *) { }
+
+void shm_pool_handle_create_buffer(wl_client *client, wl_resource *resource,
+ std::uint32_t id, std::int32_t offset, std::int32_t width,
+ std::int32_t height, std::int32_t stride, std::uint32_t format)
+{
+ auto pool { shm_pool_from_resource(resource) };
+ if (!pool) {
+ return;
+ }
+
+ if (width <= 0 || height <= 0 || stride <= 0 || offset < 0) {
+ wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_STRIDE,
+ "Invalid shm buffer geometry");
+ return;
+ }
+
+ if (format != WL_SHM_FORMAT_XRGB8888 && format != WL_SHM_FORMAT_ARGB8888) {
+ wl_resource_post_error(
+ resource, WL_SHM_ERROR_INVALID_FORMAT, "Unsupported shm format");
+ return;
+ }
+
+ auto required { static_cast(offset)
+ + static_cast(height) * static_cast(stride) };
+ if (required > pool->size()) {
+ wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_STRIDE,
+ "Shm buffer size out of bounds");
+ return;
+ }
+
+ auto *buffer_resource { wl_resource_create(
+ client, &wl_buffer_interface, 1, id) };
+ if (!buffer_resource) {
+ return;
+ }
+
+ auto buffer { std::make_shared(
+ pool, buffer_resource, offset, width, height, stride, format) };
+ auto *handle { new std::shared_ptr(std::move(buffer)) };
+ wl_resource_set_implementation(
+ buffer_resource, nullptr, handle, shm_buffer_destroy_resource);
+}
+
+struct wl_shm_pool_interface const SHM_POOL_INTERFACE = {
+ .create_buffer = shm_pool_handle_create_buffer,
+ .destroy = shm_pool_handle_destroy,
+ .resize = shm_pool_handle_resize,
+};
+
+void shm_handle_create_pool(wl_client *client, wl_resource *resource,
+ std::uint32_t id, int fd, std::int32_t size)
+{
+ auto *server
+ = static_cast(wl_resource_get_user_data(resource));
+ if (!server) {
+ return;
+ }
+ if (size <= 0) {
+ wl_resource_post_error(
+ resource, WL_SHM_ERROR_INVALID_STRIDE, "Invalid shm pool size");
+ close(fd);
+ return;
+ }
+
+ auto pool { std::make_shared(
+ fd, static_cast(size), server->logger()) };
+ if (!pool->data()) {
+ wl_resource_post_error(
+ resource, WL_SHM_ERROR_INVALID_FD, "Failed to mmap shm pool");
+ return;
+ }
+
+ auto *pool_resource
+ = wl_resource_create(client, &wl_shm_pool_interface, 1, id);
+ if (!pool_resource) {
+ return;
+ }
+ auto *handle { new std::shared_ptr(std::move(pool)) };
+ wl_resource_set_implementation(
+ pool_resource, &SHM_POOL_INTERFACE, handle, shm_pool_destroy_resource);
+}
+
+struct wl_shm_interface const SHM_INTERFACE = {
+ .create_pool = shm_handle_create_pool,
+ .release = shm_handle_release,
+};
+
+void bind_shm(
+ wl_client *client, void *data, std::uint32_t version, std::uint32_t id)
+{
+ auto *server { static_cast(data) };
+ auto *resource { wl_resource_create(
+ client, &wl_shm_interface, std::min(version, SHM_VERSION), id) };
+ if (!resource) {
+ return;
+ }
+ wl_resource_set_implementation(resource, &SHM_INTERFACE, server, nullptr);
+ wl_shm_send_format(resource, WL_SHM_FORMAT_XRGB8888);
+ wl_shm_send_format(resource, WL_SHM_FORMAT_ARGB8888);
+}
+
+} // namespace
+
+auto WaylandServer::create_shm_global() -> std::unique_ptr
+{
+ return std::make_unique(
+ display(), &wl_shm_interface, SHM_VERSION, this, bind_shm);
+}
+
+} // namespace Lunar::Wayland
diff --git a/src/wayland/protocols/XdgShellProtocol.cpp b/src/wayland/protocols/XdgShellProtocol.cpp
new file mode 100644
index 0000000..679ff37
--- /dev/null
+++ b/src/wayland/protocols/XdgShellProtocol.cpp
@@ -0,0 +1,327 @@
+#include
+#include
+
+#include
+
+#include "xdg-shell-server-protocol.h"
+
+#include "../WaylandServer.h"
+
+namespace Lunar::Wayland {
+namespace {
+
+constexpr std::uint32_t XDG_WM_BASE_VERSION = 7;
+
+struct XdgSurface {
+ WaylandServer &server;
+ wl_resource *resource {};
+ wl_resource *surface_resource {};
+ wl_resource *toplevel_resource {};
+ std::uint32_t last_serial { 0 };
+};
+
+struct XdgToplevel {
+ WaylandServer &server;
+ wl_resource *resource {};
+ XdgSurface *surface {};
+};
+
+extern struct xdg_toplevel_interface const XDG_TOPLEVEL_INTERFACE_IMPL;
+
+auto xdg_surface_from_resource(wl_resource *resource) -> XdgSurface *
+{
+ return static_cast(wl_resource_get_user_data(resource));
+}
+
+auto xdg_toplevel_from_resource(wl_resource *resource) -> XdgToplevel *
+{
+ return static_cast(wl_resource_get_user_data(resource));
+}
+
+auto send_configure(XdgSurface &surface) -> void
+{
+ std::uint32_t serial = surface.server.display().next_serial();
+ if (surface.toplevel_resource) {
+ wl_array states;
+ wl_array_init(&states);
+ xdg_toplevel_send_configure(surface.toplevel_resource, 0, 0, &states);
+ wl_array_release(&states);
+ }
+ surface.last_serial = serial;
+ xdg_surface_send_configure(surface.resource, serial);
+}
+
+auto xdg_positioner_handle_destroy(wl_client *, wl_resource *resource) -> void
+{
+ wl_resource_destroy(resource);
+}
+
+auto xdg_positioner_handle_set_size(
+ wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
+{
+}
+
+auto xdg_positioner_handle_set_anchor_rect(wl_client *, wl_resource *,
+ std::int32_t, std::int32_t, std::int32_t, std::int32_t) -> void
+{
+}
+
+auto xdg_positioner_handle_set_anchor(wl_client *, wl_resource *, std::uint32_t)
+ -> void
+{
+}
+
+auto xdg_positioner_handle_set_gravity(
+ wl_client *, wl_resource *, std::uint32_t) -> void
+{
+}
+
+auto xdg_positioner_handle_set_constraint_adjustment(
+ wl_client *, wl_resource *, std::uint32_t) -> void
+{
+}
+
+auto xdg_positioner_handle_set_offset(
+ wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
+{
+}
+
+auto xdg_positioner_handle_set_reactive(wl_client *, wl_resource *) -> void { }
+
+auto xdg_positioner_handle_set_parent_size(
+ wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
+{
+}
+
+auto xdg_positioner_handle_set_parent_configure(
+ wl_client *, wl_resource *, std::uint32_t) -> void
+{
+}
+
+struct xdg_positioner_interface const XDG_POSITIONER_INTERFACE_IMPL = {
+ .destroy = xdg_positioner_handle_destroy,
+ .set_size = xdg_positioner_handle_set_size,
+ .set_anchor_rect = xdg_positioner_handle_set_anchor_rect,
+ .set_anchor = xdg_positioner_handle_set_anchor,
+ .set_gravity = xdg_positioner_handle_set_gravity,
+ .set_constraint_adjustment
+ = xdg_positioner_handle_set_constraint_adjustment,
+ .set_offset = xdg_positioner_handle_set_offset,
+ .set_reactive = xdg_positioner_handle_set_reactive,
+ .set_parent_size = xdg_positioner_handle_set_parent_size,
+ .set_parent_configure = xdg_positioner_handle_set_parent_configure,
+};
+
+auto xdg_surface_destroy_resource(wl_resource *resource) -> void
+{
+ auto *surface { xdg_surface_from_resource(resource) };
+ delete surface;
+}
+
+auto xdg_surface_handle_destroy(wl_client *, wl_resource *resource) -> void
+{
+ wl_resource_destroy(resource);
+}
+
+auto xdg_surface_handle_get_toplevel(
+ wl_client *client, wl_resource *resource, std::uint32_t id) -> void
+{
+ auto *surface { xdg_surface_from_resource(resource) };
+ if (!surface) {
+ return;
+ }
+ auto version { wl_resource_get_version(resource) };
+ auto *toplevel_resource { wl_resource_create(
+ client, &::xdg_toplevel_interface, version, id) };
+ if (!toplevel_resource) {
+ return;
+ }
+ auto *toplevel { new XdgToplevel {
+ surface->server, toplevel_resource, surface } };
+ surface->toplevel_resource = toplevel_resource;
+ wl_resource_set_implementation(toplevel_resource,
+ &XDG_TOPLEVEL_INTERFACE_IMPL, toplevel, [](wl_resource *res) {
+ auto *tl { xdg_toplevel_from_resource(res) };
+ if (tl && tl->surface && tl->surface->toplevel_resource == res) {
+ tl->surface->toplevel_resource = nullptr;
+ }
+ delete tl;
+ });
+
+ send_configure(*surface);
+}
+
+auto xdg_surface_handle_get_popup(wl_client *, wl_resource *, std::uint32_t,
+ wl_resource *, wl_resource *) -> void
+{
+}
+
+auto xdg_surface_handle_set_window_geometry(wl_client *, wl_resource *,
+ std::int32_t, std::int32_t, std::int32_t, std::int32_t) -> void
+{
+}
+
+auto xdg_surface_handle_ack_configure(
+ wl_client *, wl_resource *resource, std::uint32_t serial) -> void
+{
+ if (auto *surface { xdg_surface_from_resource(resource) }) {
+ surface->last_serial = serial;
+ }
+}
+
+struct xdg_surface_interface const XDG_SURFACE_INTERFACE_IMPL = {
+ .destroy = xdg_surface_handle_destroy,
+ .get_toplevel = xdg_surface_handle_get_toplevel,
+ .get_popup = xdg_surface_handle_get_popup,
+ .set_window_geometry = xdg_surface_handle_set_window_geometry,
+ .ack_configure = xdg_surface_handle_ack_configure,
+};
+
+auto xdg_toplevel_handle_destroy(wl_client *, wl_resource *resource) -> void
+{
+ wl_resource_destroy(resource);
+}
+
+auto xdg_toplevel_handle_set_parent(wl_client *, wl_resource *, wl_resource *)
+ -> void
+{
+}
+
+auto xdg_toplevel_handle_set_title(wl_client *, wl_resource *, char const *)
+ -> void
+{
+}
+
+auto xdg_toplevel_handle_set_app_id(wl_client *, wl_resource *, char const *)
+ -> void
+{
+}
+
+auto xdg_toplevel_handle_show_window_menu(wl_client *, wl_resource *,
+ wl_resource *, std::uint32_t, std::int32_t, std::int32_t) -> void
+{
+}
+
+auto xdg_toplevel_handle_move(
+ wl_client *, wl_resource *, wl_resource *, std::uint32_t) -> void
+{
+}
+
+auto xdg_toplevel_handle_resize(wl_client *, wl_resource *, wl_resource *,
+ std::uint32_t, std::uint32_t) -> void
+{
+}
+
+auto xdg_toplevel_handle_set_max_size(
+ wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
+{
+}
+
+auto xdg_toplevel_handle_set_min_size(
+ wl_client *, wl_resource *, std::int32_t, std::int32_t) -> void
+{
+}
+
+auto xdg_toplevel_handle_set_maximized(wl_client *, wl_resource *) -> void { }
+
+auto xdg_toplevel_handle_unset_maximized(wl_client *, wl_resource *) -> void { }
+
+auto xdg_toplevel_handle_set_fullscreen(
+ wl_client *, wl_resource *, wl_resource *) -> void
+{
+}
+
+auto xdg_toplevel_handle_unset_fullscreen(wl_client *, wl_resource *) -> void {
+}
+
+auto xdg_toplevel_handle_set_minimized(wl_client *, wl_resource *) -> void { }
+
+struct xdg_toplevel_interface const XDG_TOPLEVEL_INTERFACE_IMPL = {
+ .destroy = xdg_toplevel_handle_destroy,
+ .set_parent = xdg_toplevel_handle_set_parent,
+ .set_title = xdg_toplevel_handle_set_title,
+ .set_app_id = xdg_toplevel_handle_set_app_id,
+ .show_window_menu = xdg_toplevel_handle_show_window_menu,
+ .move = xdg_toplevel_handle_move,
+ .resize = xdg_toplevel_handle_resize,
+ .set_max_size = xdg_toplevel_handle_set_max_size,
+ .set_min_size = xdg_toplevel_handle_set_min_size,
+ .set_maximized = xdg_toplevel_handle_set_maximized,
+ .unset_maximized = xdg_toplevel_handle_unset_maximized,
+ .set_fullscreen = xdg_toplevel_handle_set_fullscreen,
+ .unset_fullscreen = xdg_toplevel_handle_unset_fullscreen,
+ .set_minimized = xdg_toplevel_handle_set_minimized,
+};
+
+auto xdg_wm_base_handle_destroy(wl_client *, wl_resource *resource) -> void
+{
+ wl_resource_destroy(resource);
+}
+
+auto xdg_wm_base_handle_create_positioner(
+ wl_client *client, wl_resource *resource, std::uint32_t id) -> void
+{
+ auto version { wl_resource_get_version(resource) };
+ auto *positioner { wl_resource_create(
+ client, &::xdg_positioner_interface, version, id) };
+ if (!positioner) {
+ return;
+ }
+ wl_resource_set_implementation(
+ positioner, &XDG_POSITIONER_INTERFACE_IMPL, nullptr, nullptr);
+}
+
+auto xdg_wm_base_handle_get_xdg_surface(wl_client *client,
+ wl_resource *resource, std::uint32_t id, wl_resource *surface_resource)
+ -> void
+{
+ auto *server
+ = static_cast(wl_resource_get_user_data(resource));
+ if (!server) {
+ return;
+ }
+ auto version { wl_resource_get_version(resource) };
+ auto *xdg_surface_resource { wl_resource_create(
+ client, &::xdg_surface_interface, version, id) };
+ if (!xdg_surface_resource) {
+ return;
+ }
+ auto *surface { new XdgSurface {
+ *server, xdg_surface_resource, surface_resource, nullptr, 0 } };
+ wl_resource_set_implementation(xdg_surface_resource,
+ &XDG_SURFACE_INTERFACE_IMPL, surface, xdg_surface_destroy_resource);
+}
+
+auto xdg_wm_base_handle_pong(wl_client *, wl_resource *, std::uint32_t) -> void
+{
+}
+
+struct xdg_wm_base_interface const XDG_WM_BASE_INTERFACE_IMPL = {
+ .destroy = xdg_wm_base_handle_destroy,
+ .create_positioner = xdg_wm_base_handle_create_positioner,
+ .get_xdg_surface = xdg_wm_base_handle_get_xdg_surface,
+ .pong = xdg_wm_base_handle_pong,
+};
+
+auto bind_xdg_wm_base(wl_client *client, void *data, std::uint32_t version,
+ std::uint32_t id) -> void
+{
+ auto *server { static_cast(data) };
+ auto *resource { wl_resource_create(client, &::xdg_wm_base_interface,
+ std::min(version, XDG_WM_BASE_VERSION), id) };
+ if (!resource) {
+ return;
+ }
+ wl_resource_set_implementation(
+ resource, &XDG_WM_BASE_INTERFACE_IMPL, server, nullptr);
+}
+
+} // namespace
+
+auto WaylandServer::create_xdg_wm_base_global() -> std::unique_ptr
+{
+ return std::make_unique(display(), &::xdg_wm_base_interface,
+ XDG_WM_BASE_VERSION, this, bind_xdg_wm_base);
+}
+
+} // namespace Lunar::Wayland
diff --git a/tools/shm_life.cpp b/tools/shm_life.cpp
new file mode 100644
index 0000000..7ab68f7
--- /dev/null
+++ b/tools/shm_life.cpp
@@ -0,0 +1,417 @@
+#define _GNU_SOURCE
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "xdg-shell-client-protocol.h"
+#include
+
+static wl_compositor *g_compositor;
+static wl_shm *g_shm;
+static xdg_wm_base *g_xdg_wm_base;
+static xdg_surface *g_xdg_surface;
+static xdg_toplevel *g_xdg_toplevel;
+static bool g_configured;
+static std::sig_atomic_t volatile g_running { 1 };
+
+static auto handle_signal(int) -> void { g_running = 0; }
+
+static auto registry_global(void *data, wl_registry *registry, uint32_t name,
+ char const *interface, uint32_t version) -> void
+{
+ (void)data;
+ if (std::strcmp(interface, "wl_compositor") == 0) {
+ g_compositor = static_cast(wl_registry_bind(registry,
+ name, &wl_compositor_interface, version < 4 ? version : 4));
+ } else if (std::strcmp(interface, "wl_shm") == 0) {
+ g_shm = static_cast(wl_registry_bind(
+ registry, name, &wl_shm_interface, version < 1 ? version : 1));
+ } else if (std::strcmp(interface, "xdg_wm_base") == 0) {
+ g_xdg_wm_base = static_cast(wl_registry_bind(
+ registry, name, &xdg_wm_base_interface, version < 7 ? version : 7));
+ }
+}
+
+static auto registry_global_remove(
+ void *data, wl_registry *registry, uint32_t name) -> void
+{
+ (void)data;
+ (void)registry;
+ (void)name;
+}
+
+static wl_registry_listener const registry_listener = {
+ .global = registry_global,
+ .global_remove = registry_global_remove,
+};
+
+static auto xdg_wm_base_handle_ping(
+ void *, xdg_wm_base *wm_base, uint32_t serial) -> void
+{
+ xdg_wm_base_pong(wm_base, serial);
+}
+
+static xdg_wm_base_listener const xdg_wm_base_listener = {
+ .ping = xdg_wm_base_handle_ping,
+};
+
+static auto xdg_surface_handle_configure(
+ void *, xdg_surface *surface, uint32_t serial) -> void
+{
+ xdg_surface_ack_configure(surface, serial);
+ g_configured = true;
+}
+
+static xdg_surface_listener const xdg_surface_listener = {
+ .configure = xdg_surface_handle_configure,
+};
+
+static auto xdg_toplevel_handle_configure(
+ void *, xdg_toplevel *, int32_t, int32_t, wl_array *) -> void
+{
+}
+
+static auto xdg_toplevel_handle_close(void *, xdg_toplevel *) -> void { }
+
+static auto xdg_toplevel_handle_configure_bounds(
+ void *, xdg_toplevel *, int32_t, int32_t) -> void
+{
+}
+
+static auto xdg_toplevel_handle_wm_capabilities(
+ void *, xdg_toplevel *, wl_array *) -> void
+{
+}
+
+static xdg_toplevel_listener const xdg_toplevel_listener = {
+ .configure = xdg_toplevel_handle_configure,
+ .close = xdg_toplevel_handle_close,
+ .configure_bounds = xdg_toplevel_handle_configure_bounds,
+ .wm_capabilities = xdg_toplevel_handle_wm_capabilities,
+};
+
+static auto create_shm_file(size_t size) -> int
+{
+ int fd { memfd_create("shm_life", 0) };
+ if (fd < 0) {
+ return -1;
+ }
+ if (ftruncate(fd, static_cast(size)) != 0) {
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+auto main(int argc, char **argv) -> int
+{
+ auto *display { wl_display_connect(nullptr) };
+ if (!display) {
+ std::println(std::cerr, "Failed to connect to Wayland display.");
+ return 1;
+ }
+
+ auto *registry { wl_display_get_registry(display) };
+ if (!registry) {
+ std::println(std::cerr, "Failed to fetch Wayland registry.");
+ wl_display_disconnect(display);
+ return 1;
+ }
+
+ if (wl_registry_add_listener(registry, ®istry_listener, nullptr) < 0) {
+ std::println(std::cerr, "Failed to add registry listener.");
+ wl_display_disconnect(display);
+ return 1;
+ }
+ if (wl_display_roundtrip(display) < 0) {
+ std::println(std::cerr, "Failed to sync Wayland registry.");
+ wl_display_disconnect(display);
+ return 1;
+ }
+
+ if (!g_compositor || !g_shm || !g_xdg_wm_base) {
+ std::println(std::cerr, "Missing compositor or xdg globals.");
+ wl_display_disconnect(display);
+ return 1;
+ }
+
+ std::signal(SIGINT, handle_signal);
+ std::signal(SIGTERM, handle_signal);
+
+ if (xdg_wm_base_add_listener(g_xdg_wm_base, &xdg_wm_base_listener, nullptr)
+ < 0) {
+ std::println(std::cerr, "Failed to add xdg_wm_base listener.");
+ wl_display_disconnect(display);
+ return 1;
+ }
+
+ constexpr int width { 640 };
+ constexpr int height { 480 };
+ int cell_size { 5 };
+ if (argc > 1) {
+ long requested { std::strtol(argv[1], nullptr, 10) };
+ if (requested >= 2 && requested <= 80) {
+ cell_size = static_cast(requested);
+ }
+ }
+ int grid_width { width / cell_size };
+ int grid_height { height / cell_size };
+ constexpr int stride { width * 4 };
+ constexpr size_t size { static_cast(stride) * height };
+ int fd { create_shm_file(size) };
+ if (fd < 0) {
+ std::println(std::cerr, "Failed to create shm file.");
+ wl_display_disconnect(display);
+ return 1;
+ }
+
+ auto *data { static_cast(
+ mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) };
+ if (data == MAP_FAILED) {
+ std::println(std::cerr, "Failed to mmap shm.");
+ close(fd);
+ wl_display_disconnect(display);
+ return 1;
+ }
+
+ std::vector grid(static_cast(grid_width * grid_height), 0);
+ std::vector next(static_cast(grid_width * grid_height), 0);
+ std::minstd_rand rng(std::random_device {}());
+ std::bernoulli_distribution alive_dist(0.45);
+ std::vector history;
+ float hue { 0.0f };
+ float hue_step { 1.5f };
+
+ auto hue_to_rgb { [](float hue_degrees) -> uint32_t {
+ float h { std::fmod(hue_degrees, 360.0f) / 360.0f };
+ float s { 1.0f };
+ float l { 0.5f };
+ auto hue_to_channel { [](float p, float q, float t) -> float {
+ if (t < 0.0f) {
+ t += 1.0f;
+ }
+ if (t > 1.0f) {
+ t -= 1.0f;
+ }
+ if (t < 1.0f / 6.0f) {
+ return p + (q - p) * 6.0f * t;
+ }
+ if (t < 1.0f / 2.0f) {
+ return q;
+ }
+ if (t < 2.0f / 3.0f) {
+ return p + (q - p) * (2.0f / 3.0f - t) * 6.0f;
+ }
+ return p;
+ } };
+ float r { l };
+ float g { l };
+ float b { l };
+ if (s > 0.0f) {
+ float q { l < 0.5f ? l * (1.0f + s) : l + s - l * s };
+ float p { 2.0f * l - q };
+ r = hue_to_channel(p, q, h + 1.0f / 3.0f);
+ g = hue_to_channel(p, q, h);
+ b = hue_to_channel(p, q, h - 1.0f / 3.0f);
+ }
+ uint8_t rr { static_cast(r * 255.0f) };
+ uint8_t gg { static_cast(g * 255.0f) };
+ uint8_t bb { static_cast(b * 255.0f) };
+ return 0xff000000u | (uint32_t)rr << 16 | (uint32_t)gg << 8
+ | (uint32_t)bb;
+ } };
+
+ auto randomize_grid { [&]() -> void {
+ for (int y = 0; y < grid_height; ++y) {
+ for (int x = 0; x < grid_width; ++x) {
+ grid[y * grid_width + x] = alive_dist(rng) ? 1 : 0;
+ }
+ }
+ std::fill(next.begin(), next.end(), 0);
+ history.clear();
+ } };
+
+ auto hash_grid { [&]() -> uint64_t {
+ uint64_t hash { 1469598103934665603ull };
+ for (uint8_t cell : grid) {
+ hash ^= static_cast(cell);
+ hash *= 1099511628211ull;
+ }
+ return hash;
+ } };
+
+ randomize_grid();
+
+ auto *pool { wl_shm_create_pool(g_shm, fd, static_cast(size)) };
+ auto *buffer { wl_shm_pool_create_buffer(
+ pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888) };
+ auto *surface { wl_compositor_create_surface(g_compositor) };
+ if (!pool || !buffer || !surface) {
+ std::println(std::cerr, "Failed to create shm objects.");
+ if (surface) {
+ wl_surface_destroy(surface);
+ }
+ if (buffer) {
+ wl_buffer_destroy(buffer);
+ }
+ if (pool) {
+ wl_shm_pool_destroy(pool);
+ }
+ munmap(data, size);
+ close(fd);
+ wl_display_disconnect(display);
+ return 1;
+ }
+
+ g_xdg_surface = xdg_wm_base_get_xdg_surface(g_xdg_wm_base, surface);
+ if (!g_xdg_surface) {
+ std::println(std::cerr, "Failed to create xdg_surface.");
+ wl_surface_destroy(surface);
+ wl_buffer_destroy(buffer);
+ wl_shm_pool_destroy(pool);
+ munmap(data, size);
+ close(fd);
+ wl_display_disconnect(display);
+ return 1;
+ }
+ if (xdg_surface_add_listener(g_xdg_surface, &xdg_surface_listener, nullptr)
+ < 0) {
+ std::println(std::cerr, "Failed to add xdg_surface listener.");
+ xdg_surface_destroy(g_xdg_surface);
+ wl_surface_destroy(surface);
+ wl_buffer_destroy(buffer);
+ wl_shm_pool_destroy(pool);
+ munmap(data, size);
+ close(fd);
+ wl_display_disconnect(display);
+ return 1;
+ }
+
+ g_xdg_toplevel = xdg_surface_get_toplevel(g_xdg_surface);
+ if (g_xdg_toplevel) {
+ xdg_toplevel_add_listener(
+ g_xdg_toplevel, &xdg_toplevel_listener, nullptr);
+ xdg_toplevel_set_title(g_xdg_toplevel, "life");
+ xdg_toplevel_set_app_id(g_xdg_toplevel, "lunar.life");
+ }
+
+ wl_surface_commit(surface);
+ if (wl_display_roundtrip(display) < 0 || !g_configured) {
+ std::println(std::cerr, "Failed to receive initial configure.");
+ if (g_xdg_toplevel) {
+ xdg_toplevel_destroy(g_xdg_toplevel);
+ }
+ xdg_surface_destroy(g_xdg_surface);
+ wl_surface_destroy(surface);
+ wl_buffer_destroy(buffer);
+ wl_shm_pool_destroy(pool);
+ munmap(data, size);
+ close(fd);
+ wl_display_disconnect(display);
+ return 1;
+ }
+
+ wl_surface_attach(surface, buffer, 0, 0);
+ wl_surface_commit(surface);
+ wl_display_flush(display);
+
+ auto last_tick { std::chrono::steady_clock::now() };
+ while (g_running) {
+ wl_display_dispatch_pending(display);
+
+ auto now { std::chrono::steady_clock::now() };
+ auto dt { now - last_tick };
+ if (dt < std::chrono::milliseconds(16)) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(4));
+ continue;
+ }
+ last_tick = now;
+
+ for (int y = 0; y < grid_height; ++y) {
+ for (int x = 0; x < grid_width; ++x) {
+ int neighbors { 0 };
+ for (int oy = -1; oy <= 1; ++oy) {
+ for (int ox = -1; ox <= 1; ++ox) {
+ if (ox == 0 && oy == 0) {
+ continue;
+ }
+ int ny { (y + oy + grid_height) % grid_height };
+ int nx { (x + ox + grid_width) % grid_width };
+ neighbors += grid[ny * grid_width + nx];
+ }
+ }
+ uint8_t alive { grid[y * grid_width + x] };
+ if (alive) {
+ next[y * grid_width + x]
+ = (neighbors == 2 || neighbors == 3) ? 1 : 0;
+ } else {
+ next[y * grid_width + x] = (neighbors == 3) ? 1 : 0;
+ }
+ }
+ }
+
+ grid.swap(next);
+ std::fill(next.begin(), next.end(), 0);
+
+ uint64_t current_hash { hash_grid() };
+ bool repeating { std::find(history.begin(), history.end(), current_hash)
+ != history.end() };
+ if (repeating) {
+ randomize_grid();
+ current_hash = hash_grid();
+ }
+ history.push_back(current_hash);
+ if (history.size() > 5) {
+ history.erase(history.begin());
+ }
+
+ hue += hue_step;
+ if (hue >= 360.0f) {
+ hue -= 360.0f;
+ }
+ uint32_t alive_color { hue_to_rgb(hue) };
+ for (int y = 0; y < height; ++y) {
+ int gy { y / cell_size };
+ for (int x = 0; x < width; ++x) {
+ int gx { x / cell_size };
+ uint8_t alive { grid[gy * grid_width + gx] };
+ if (alive) {
+ data[y * width + x] = alive_color;
+ } else {
+ data[y * width + x] = 0x00000000u;
+ }
+ }
+ }
+
+ wl_surface_attach(surface, buffer, 0, 0);
+ wl_surface_damage_buffer(surface, 0, 0, width, height);
+ wl_surface_commit(surface);
+ wl_display_flush(display);
+ }
+
+ if (g_xdg_toplevel) {
+ xdg_toplevel_destroy(g_xdg_toplevel);
+ }
+ if (g_xdg_surface) {
+ xdg_surface_destroy(g_xdg_surface);
+ }
+ wl_buffer_destroy(buffer);
+ wl_shm_pool_destroy(pool);
+ wl_surface_destroy(surface);
+ munmap(data, size);
+ close(fd);
+ wl_display_disconnect(display);
+}