diff --git a/README.md b/README.md
index 56714b8e2..aa877a5a4 100644
--- a/README.md
+++ b/README.md
@@ -29,9 +29,9 @@ See [NEWS](https://gitlab.xfce.org/xfce/xfwm4/-/blob/master/NEWS) for details on
From source:
% cd xfwm4
- % ./autogen.sh
- % make
- % make install
+ % meson build
+ % ninja -C build
+ % ninja -C build install
From release tarball:
diff --git a/common/meson.build b/common/meson.build
new file mode 100644
index 000000000..9d684d4ff
--- /dev/null
+++ b/common/meson.build
@@ -0,0 +1,3 @@
+xfwm_common = static_library('xfwm-common',
+ 'xfwm-common.c',
+ dependencies: gtk)
diff --git a/meson.build b/meson.build
new file mode 100644
index 000000000..3bf25c3ff
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,165 @@
+project('xfwm4', 'c',
+ version: '4.20.0',
+ meson_version: '>=0.54',
+ default_options: 'c_std=gnu11',)
+
+xfwm4_version = meson.project_version()
+
+ver_arr = xfwm4_version.split('.')
+
+xfwm4_major_version = ver_arr[0]
+xfwm4_minor_version = ver_arr[1]
+xfwm4_micro_version = ver_arr[2]
+
+glib_minimum_version = '2.72.0'
+gtk_minimum_version = '3.24.0'
+xfce_minimum_version = '4.8.0'
+libxfce4ui_minimum_version = '4.12.0'
+libxfce4kbd_private_minimum_version = '4.12.0'
+xfconf_minimum_version = '4.13.0'
+xfconf_legacy_version = '4.12.0'
+xcomposite_minimum_version = '0.2'
+wnck_minimum_version = '3.14'
+startup_notification_minimum_version = '0.5'
+intltool_minimum_version = '0.35'
+libepoxy_minimum_version = '1.0'
+xpresent_minimum_version = '1.0'
+presentproto_minimum_version = '1.1'
+
+prefix = get_option('prefix')
+
+bindir = join_paths(prefix, get_option('bindir'))
+datadir = join_paths(prefix, get_option('datadir'))
+libdir = join_paths(prefix, get_option('libdir'))
+libexecdir = join_paths(prefix, get_option('libexecdir'))
+includedir = join_paths(prefix, get_option('includedir'))
+sysconfdir = get_option('sysconfdir')
+
+pkgname = meson.project_name()
+
+pkgdatadir = join_paths(datadir, pkgname)
+pkglibdir = join_paths(libdir, pkgname)
+pkgincludedir = join_paths(includedir, pkgname)
+
+pcdir = join_paths(libdir, 'pkgconfig')
+
+gettext_package = meson.project_name()
+localedir = join_paths(datadir, 'locale')
+
+top_srcdir = meson.current_source_dir()
+builddir = meson.current_build_dir()
+
+#localedir = '"' + get_option('localedir') + '"'
+
+
+add_project_arguments('-DWLR_USE_UNSTABLE', language: 'c')
+
+cc = meson.get_compiler('c')
+
+wayland_server = dependency('wayland-server')
+wayland_client = dependency('wayland-client')
+wayland_cursor = dependency('wayland-cursor')
+wayland_egl = dependency('wayland-egl')
+wayland_protos = dependency('wayland-protocols', version: '>=1.14')
+pixman = dependency('pixman-1')
+cairo = dependency('cairo')
+pango = dependency('pango')
+pangocairo = dependency('pangocairo')
+glesv2 = dependency('glesv2')
+wlroots = dependency('wlroots', version: ['>=0.17.0', '<0.18.0'])
+xkbcommon = dependency('xkbcommon')
+glib = dependency('glib-2.0', version: '>=' + glib_minimum_version)
+x11 = dependency('x11')
+compositor = [
+ dependency('xcomposite'),
+ dependency('xfixes'),
+ dependency('xdamage'),
+ ]
+xrender = dependency('xrender')
+xext = dependency('xext')
+xrandr = dependency('xrandr')
+libinput = dependency('libinput', version: '>=1.10.0')
+egl = dependency('egl', version: '>=1.0.0')
+evdev = dependency('libevdev', version: '>=1.5.8')
+libxml2 = dependency('libxml-2.0')
+gtk = dependency('gtk+-3.0', version: '>=' + gtk_minimum_version)
+libxfce4util = dependency('libxfce4util-1.0',
+ version: '>=' + xfce_minimum_version)
+libxfce4ui = dependency('libxfce4ui-2',
+ version: '>=' + libxfce4ui_minimum_version)
+libxfce4kbd_private = dependency('libxfce4kbd-private-3',
+ version: '>='
+ + libxfce4kbd_private_minimum_version)
+libxfconf = dependency('libxfconf-0', version: '>=' + xfconf_minimum_version)
+libwnck = dependency('libwnck-3.0',
+ version: '>=' + wnck_minimum_version)
+xinerama = dependency('xinerama')
+
+m_dep = cc.find_library('m')
+
+wlroots_features = {
+ 'xwayland': false,
+ 'libinput_backend': false,
+ 'session': false,
+}
+foreach name, _: wlroots_features
+ var_name = 'have_' + name.underscorify()
+ have = wlroots.get_variable(pkgconfig: var_name, internal: var_name) == 'true'
+ wlroots_features += {name: have}
+endforeach
+
+# glibc lacks strl*. if we can't detect them, assume we need libbsd
+lacking_libc = false
+if cc.has_function('strlcpy') == false or cc.has_function('strlcat') == false
+ lacking_libc = true
+ bsd_overlay = dependency('libbsd-overlay')
+endif
+
+if wlroots_features['xwayland']
+ xcb = dependency('xcb', required: true)
+
+ if xcb.found()
+ add_project_arguments('-DUSE_XWAYLAND', language: 'c')
+ endif
+endif
+
+
+
+conf_data = configuration_data()
+conf_data.set('PACKAGE', '"' + pkgname + '"')
+conf_data.set('VERSION', '"' + xfwm4_version + '"')
+conf_data.set('REVISION', '"bdb2b4f88"')
+conf_data.set('GETTEXT_PACKAGE', '"' + gettext_package + '"')
+conf_data.set('PACKAGE_NAME', '"' + pkgname + '"')
+conf_data.set('PACKAGE_LOCALE_DIR', '"' + localedir + '"')
+conf_data.set('HAVE_LIBX11', 1)
+conf_data.set('HAVE_COMPOSITOR', 1)
+
+exec_prefix = get_option ('prefix')
+helper_path_prefix = exec_prefix + '/lib'
+
+add_global_arguments('-DHAVE_CONFIG_H', language: 'c')
+add_global_arguments('-DDATADIR=' + '"' + datadir + '"', language: 'c')
+add_global_arguments('-DPACKAGE_DATADIR=' + '"' + pkgdatadir + '"', language: 'c')
+add_global_arguments('-DHELPERDIR=' + '"' + helper_path_prefix + '"', language: 'c')
+add_global_arguments('-DBINDIR=' + '"' + bindir + '"', language: 'c')
+
+add_project_arguments(
+ '-Wno-unused-parameter',
+ '-Wno-unused-variable',
+ '-D_DEFAULT_SOURCE',
+ '-D_POSIX_C_SOURCE=200112L',
+ '-DWLR_USE_UNSTABLE',
+ '-DPACKAGE_NAME="' + meson.project_name() + '"',
+ '-DPACKAGE_VERSION="' + meson.project_version() + '"',
+ language: 'c')
+
+configure_file(output: 'config.h',
+ configuration: conf_data
+)
+
+incdir = include_directories('.')
+
+subdir('protocol')
+subdir('common')
+subdir('src')
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 000000000..88ac80b4f
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,2 @@
+option('xwayland', type: 'boolean', value: true,
+ description: 'Whether Xwayland support is enabled')
diff --git a/protocol/Makefile.am b/protocol/Makefile.am
new file mode 100644
index 000000000..bfbb4cd18
--- /dev/null
+++ b/protocol/Makefile.am
@@ -0,0 +1,36 @@
+ACLOCAL_AMFLAGS = -I m4
+
+unstable_protocols_SOURCES = \
+xfway-shell.xml \
+ $(NULL)
+
+unstable_protocols_CFLAGS = \
+$(WAYLAND_PROTOCOLS_CFLAGS) \
+ $(NULL)
+
+unstable_protocols_LDADD = \
+$(WAYLAND_PROTOCOLS_LIBS) \
+ $(NULL)
+
+stable_protocols = \
+ \
+ $(NULL)
+
+nobase_dist_pkgdata_DATA = \
+ $(unstable_protocols) \
+ $(stable_protocols) \
+ $(NULL)
+
+#dist_noinst_DATA = \
+ #$(sort $(foreach p,$(unstable_protocols),$(dir $p)README)) \
+ #$(sort $(foreach p,$(stable_protocols),$(dir $p)README)) \
+# $(NULL)
+
+#noarch_pkgconfig_DATA = wayland-protocols.pc
+
+dist_check_SCRIPTS = scan.sh
+
+TESTS = $(unstable_protocols) $(stable_protocols)
+TEST_EXTENSIONS = .xml
+AM_TESTS_ENVIRONMENT = SCANNER='$(wayland_scanner)'; export SCANNER;
+XML_LOG_COMPILER = $(srcdir)/scan.sh
diff --git a/protocol/meson.build b/protocol/meson.build
new file mode 100644
index 000000000..5735fc492
--- /dev/null
+++ b/protocol/meson.build
@@ -0,0 +1,85 @@
+wayland_protos_inc = include_directories('.')
+
+
+wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
+
+wayland_scanner_dep = dependency('wayland-scanner', required: false, native: true)
+if wayland_scanner_dep.found()
+ wayland_scanner = find_program(
+ wayland_scanner_dep.get_pkgconfig_variable('wayland_scanner'),
+ native: true,
+ )
+else
+ wayland_scanner = find_program('wayland-scanner', native: true)
+endif
+
+protocols = [
+ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
+ ['wlr-layer-shell-unstable-v1.xml'],
+ ['wlr-foreign-toplevel-management-unstable-v1.xml'],
+ ['xfway-shell.xml'],
+ ['wlr-gamma-control-unstable-v1.xml'],
+ ['wlr-screencopy-unstable-v1.xml'],
+]
+
+client_protocols = [
+ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
+ ['wlr-layer-shell-unstable-v1.xml'],
+ ['wlr-foreign-toplevel-management-unstable-v1.xml'],
+ ['xfway-shell.xml'],
+ ['wlr-gamma-control-unstable-v1.xml'],
+ ['wlr-screencopy-unstable-v1.xml'],
+]
+
+wl_protos_src = []
+wl_protos_headers = []
+
+foreach p : protocols
+ xml = join_paths(p)
+ wl_protos_src += custom_target(
+ xml.underscorify() + '_server_c',
+ input: xml,
+ output: '@BASENAME@-protocol.c',
+ command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
+ )
+ wl_protos_headers += custom_target(
+ xml.underscorify() + '_server_h',
+ input: xml,
+ output: '@BASENAME@-protocol.h',
+ command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
+ )
+endforeach
+
+
+foreach p : client_protocols
+ xml = join_paths(p)
+ wl_protos_headers += custom_target(
+ xml.underscorify() + '_client_h',
+ input: xml,
+ output: '@BASENAME@-client-protocol.h',
+ command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
+ )
+endforeach
+
+
+xfwm_lib_client_protos = static_library(
+ 'xfwm_client_protos',
+ wl_protos_src + wl_protos_headers,
+ dependencies: wayland_client.partial_dependency(compile_args: true),
+)
+
+xfwm_client_protos = declare_dependency(
+ link_with: xfwm_lib_client_protos,
+ sources: wl_protos_headers,
+)
+
+xfwm_lib_server_protos = static_library(
+ 'xfwm_server_protos',
+ wl_protos_src + wl_protos_headers,
+ dependencies: wayland_server.partial_dependency(compile_args: true),
+)
+
+xfwm_server_protos = declare_dependency(
+ link_with: xfwm_lib_server_protos,
+ sources: wl_protos_headers,
+)
diff --git a/protocol/scan.sh b/protocol/scan.sh
new file mode 100755
index 000000000..cad688775
--- /dev/null
+++ b/protocol/scan.sh
@@ -0,0 +1,11 @@
+#!/bin/sh -e
+
+if [ "x$SCANNER" = "x" ] ; then
+ echo "No scanner present, test skipped." 1>&2
+ exit 77
+fi
+
+$SCANNER client-header --strict $1 /dev/null
+$SCANNER server-header --strict $1 /dev/null
+$SCANNER private-code --strict $1 /dev/null
+$SCANNER public-code --strict $1 /dev/null
diff --git a/protocol/window-switcher-unstable-v1.xml b/protocol/window-switcher-unstable-v1.xml
new file mode 100644
index 000000000..35e901222
--- /dev/null
+++ b/protocol/window-switcher-unstable-v1.xml
@@ -0,0 +1,140 @@
+
+
+
+ Copyright © 2017 Quentin "Sardem FF7" Glidic
+
+ 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 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 object is a singleton global.
+
+ This interface can only be bound once at the same time.
+ Any binding of this interface while already bound results
+ in a protocol error (bound).
+ Compositors are expected to restrict this interface to trusted clients.
+
+ This interface is intended for window switchers of any kind.
+
+
+
+
+
+
+ These errors can be emitted in response to
+ ww_window_switcher requests.
+
+
+
+
+
+
+
+
+
+ This event will be sent whenever the implicit grab is broken.
+ When receiving this event, the client must destroy the wl_surface.
+
+
+
+
+
+
+
+ The object is a singleton global.
+
+ This interface can only be bound once at the same time.
+ Any binding of this interface while already bound results
+ in a protocol error (bound).
+
+ Compositors are expected to restrict this interface to trusted clients.
+
+
+
+
+
+
+ These errors can be emitted in response to
+ ww_window_switcher_window requests.
+
+
+
+
+
+
+ Tell the compositor to switch to this window.
+
+
+
+
+
+
+
+ Tell the compositor to close this window.
+
+
+
+
+
+
+
+ This tells the compositor to draw a thumbnail of the window
+ in the given rectangle.
+
+ All of x, y, width and height must be positive. And width and
+ height must be strictly positive. Otherwise, a protocol error
+ (invalid_rectangle) is raised.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This event will be sent whenever all data for this window has
+ been transmitted.
+
+ If some data event was not received, it means the data was
+ unavailable.
+
+
+
+
diff --git a/protocol/wlr-foreign-toplevel-management-unstable-v1.xml b/protocol/wlr-foreign-toplevel-management-unstable-v1.xml
new file mode 100644
index 000000000..a97738f8a
--- /dev/null
+++ b/protocol/wlr-foreign-toplevel-management-unstable-v1.xml
@@ -0,0 +1,259 @@
+
+
+
+ Copyright © 2018 Ilia Bozhinov
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+
+
+
+
+ The purpose of this protocol is to enable the creation of taskbars
+ and docks by providing them with a list of opened applications and
+ letting them request certain actions on them, like maximizing, etc.
+
+ After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
+ toplevel window will be sent via the toplevel event
+
+
+
+
+ This event is emitted whenever a new toplevel window is created. It
+ is emitted for all toplevels, regardless of the app that has created
+ them.
+
+ All initial details of the toplevel(title, app_id, states, etc.) will
+ be sent immediately after this event via the corresponding events in
+ zwlr_foreign_toplevel_handle_v1.
+
+
+
+
+
+
+ Indicates the client no longer wishes to receive events for new toplevels.
+ However the compositor may emit further toplevel_created events, until
+ the finished event is emitted.
+
+ The client must not send any more requests after this one.
+
+
+
+
+
+ This event indicates that the compositor is done sending events to the
+ zwlr_foreign_toplevel_manager_v1. The server will destroy the object
+ immediately after sending this request, so it will become invalid and
+ the client should free any resources associated with it.
+
+
+
+
+
+
+ A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
+ window. Each app may have multiple opened toplevels.
+
+ Each toplevel has a list of outputs it is visible on, conveyed to the
+ client with the output_enter and output_leave events.
+
+
+
+
+ This event is emitted whenever the title of the toplevel changes.
+
+
+
+
+
+
+ This event is emitted whenever the app-id of the toplevel changes.
+
+
+
+
+
+
+ This event is emitted whenever the toplevel becomes visible on
+ the given output. A toplevel may be visible on multiple outputs.
+
+
+
+
+
+
+ This event is emitted whenever the toplevel stops being visible on
+ the given output. It is guaranteed that an entered-output event
+ with the same output has been emitted before this event.
+
+
+
+
+
+
+ Requests that the toplevel be maximized. If the maximized state actually
+ changes, this will be indicated by the state event.
+
+
+
+
+
+ Requests that the toplevel be unmaximized. If the maximized state actually
+ changes, this will be indicated by the state event.
+
+
+
+
+
+ Requests that the toplevel be minimized. If the minimized state actually
+ changes, this will be indicated by the state event.
+
+
+
+
+
+ Requests that the toplevel be unminimized. If the minimized state actually
+ changes, this will be indicated by the state event.
+
+
+
+
+
+ Request that this toplevel be activated on the given seat.
+ There is no guarantee the toplevel will be actually activated.
+
+
+
+
+
+
+ The different states that a toplevel can have. These have the same meaning
+ as the states with the same names defined in xdg-toplevel
+
+
+
+
+
+
+
+
+
+
+ This event is emitted immediately after the zlw_foreign_toplevel_handle_v1
+ is created and each time the toplevel state changes, either because of a
+ compositor action or because of a request in this protocol.
+
+
+
+
+
+
+
+ This event is sent after all changes in the toplevel state have been
+ sent.
+
+ This allows changes to the zwlr_foreign_toplevel_handle_v1 properties
+ to be seen as atomic, even if they happen via multiple events.
+
+
+
+
+
+ Send a request to the toplevel to close itself. The compositor would
+ typically use a shell-specific method to carry out this request, for
+ example by sending the xdg_toplevel.close event. However, this gives
+ no guarantees the toplevel will actually be destroyed. If and when
+ this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
+ be emitted.
+
+
+
+
+
+ The rectangle of the surface specified in this request corresponds to
+ the place where the app using this protocol represents the given toplevel.
+ It can be used by the compositor as a hint for some operations, e.g
+ minimizing. The client is however not required to set this, in which
+ case the compositor is free to decide some default value.
+
+ If the client specifies more than one rectangle, only the last one is
+ considered.
+
+ The dimensions are given in surface-local coordinates.
+ Setting width=height=0 removes the already-set rectangle.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This event means the toplevel has been destroyed. It is guaranteed there
+ won't be any more events for this zwlr_foreign_toplevel_handle_v1. The
+ toplevel itself becomes inert so any requests will be ignored except the
+ destroy request.
+
+
+
+
+
+ Destroys the zwlr_foreign_toplevel_handle_v1 object.
+
+ This request should be called either when the client does not want to
+ use the toplevel anymore or after the closed event to finalize the
+ destruction of the object.
+
+
+
+
+
+
+
+ Requests that the toplevel be fullscreened on the given output. If the
+ fullscreen state and/or the outputs the toplevel is visible on actually
+ change, this will be indicated by the state and output_enter/leave
+ events.
+
+ The output parameter is only a hint to the compositor. Also, if output
+ is NULL, the compositor should decide which output the toplevel will be
+ fullscreened on, if at all.
+
+
+
+
+
+
+ Requests that the toplevel be unfullscreened. If the fullscreen state
+ actually changes, this will be indicated by the state event.
+
+
+
+
diff --git a/protocol/wlr-gamma-control-unstable-v1.xml b/protocol/wlr-gamma-control-unstable-v1.xml
new file mode 100644
index 000000000..a9db76240
--- /dev/null
+++ b/protocol/wlr-gamma-control-unstable-v1.xml
@@ -0,0 +1,126 @@
+
+
+
+ Copyright © 2015 Giulio camuffo
+ Copyright © 2018 Simon Ser
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+
+
+
+ This protocol allows a privileged client to set the gamma tables for
+ outputs.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+
+
+
+
+ This interface is a manager that allows creating per-output gamma
+ controls.
+
+
+
+
+ Create a gamma control that can be used to adjust gamma tables for the
+ provided output.
+
+
+
+
+
+
+
+ All objects created by the manager will still remain valid, until their
+ appropriate destroy request has been called.
+
+
+
+
+
+
+ This interface allows a client to adjust gamma tables for a particular
+ output.
+
+ The client will receive the gamma size, and will then be able to set gamma
+ tables. At any time the compositor can send a failed event indicating that
+ this object is no longer valid.
+
+ There must always be at most one gamma control object per output, which
+ has exclusive access to this particular output. When the gamma control
+ object is destroyed, the gamma table is restored to its original value.
+
+
+
+
+ Advertise the size of each gamma ramp.
+
+ This event is sent immediately when the gamma control object is created.
+
+
+
+
+
+
+
+
+
+
+ Set the gamma table. The file descriptor can be memory-mapped to provide
+ the raw gamma table, which contains successive gamma ramps for the red,
+ green and blue channels. Each gamma ramp is an array of 16-byte unsigned
+ integers which has the same length as the gamma size.
+
+ The file descriptor data must have the same length as three times the
+ gamma size.
+
+
+
+
+
+
+ This event indicates that the gamma control is no longer valid. This
+ can happen for a number of reasons, including:
+ - The output doesn't support gamma tables
+ - Setting the gamma tables failed
+ - Another client already has exclusive gamma control for this output
+ - The compositor has transferred gamma control to another client
+
+ Upon receiving this event, the client should destroy this object.
+
+
+
+
+
+ Destroys the gamma control object. If the object is still valid, this
+ restores the original gamma tables.
+
+
+
+
diff --git a/protocol/wlr-layer-shell-unstable-v1.xml b/protocol/wlr-layer-shell-unstable-v1.xml
new file mode 100644
index 000000000..d62fd51e9
--- /dev/null
+++ b/protocol/wlr-layer-shell-unstable-v1.xml
@@ -0,0 +1,390 @@
+
+
+
+ Copyright © 2017 Drew DeVault
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+
+
+
+
+ Clients can use this interface to assign the surface_layer role to
+ wl_surfaces. Such surfaces are assigned to a "layer" of the output and
+ rendered with a defined z-depth respective to each other. They may also be
+ anchored to the edges and corners of a screen and specify input handling
+ semantics. This interface should be suitable for the implementation of
+ many desktop shell components, and a broad number of other applications
+ that interact with the desktop.
+
+
+
+
+ Create a layer surface for an existing surface. This assigns the role of
+ layer_surface, or raises a protocol error if another role is already
+ assigned.
+
+ Creating a layer 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 layer_surface.configure call
+ must also be treated as errors.
+
+ After creating a layer_surface object and setting it up, the client
+ must perform an initial commit without any buffer attached.
+ The compositor will reply with a layer_surface.configure event.
+ The client must acknowledge it and is then allowed to attach a buffer
+ to map the surface.
+
+ You may pass NULL for output to allow the compositor to decide which
+ output to use. Generally this will be the one that the user most
+ recently interacted with.
+
+ Clients can specify a namespace that defines the purpose of the layer
+ surface.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ These values indicate which layers a surface can be rendered in. They
+ are ordered by z depth, bottom-most first. Traditional shell surfaces
+ will typically be rendered between the bottom and top layers.
+ Fullscreen shell surfaces are typically rendered at the top layer.
+ Multiple surfaces can share a single layer, and ordering within a
+ single layer is undefined.
+
+
+
+
+
+
+
+
+
+
+
+
+ This request indicates that the client will not use the layer_shell
+ object any more. Objects that have been created through this instance
+ are not affected.
+
+
+
+
+
+
+ An interface that may be implemented by a wl_surface, for surfaces that
+ are designed to be rendered as a layer of a stacked desktop-like
+ environment.
+
+ Layer surface state (layer, size, anchor, exclusive zone,
+ margin, interactivity) is double-buffered, and will be applied at the
+ time wl_surface.commit of the corresponding wl_surface is called.
+
+ Attaching a null buffer to a layer surface unmaps it.
+
+ Unmapping a layer_surface means that the surface cannot be shown by the
+ compositor until it is explicitly mapped again. The layer_surface
+ returns to the state it had right after layer_shell.get_layer_surface.
+ The client can re-map the surface by performing a commit without any
+ buffer attached, waiting for a configure event and handling it as usual.
+
+
+
+
+ Sets the size of the surface in surface-local coordinates. The
+ compositor will display the surface centered with respect to its
+ anchors.
+
+ If you pass 0 for either value, the compositor will assign it and
+ inform you of the assignment in the configure event. You must set your
+ anchor to opposite edges in the dimensions you omit; not doing so is a
+ protocol error. Both values are 0 by default.
+
+ Size is double-buffered, see wl_surface.commit.
+
+
+
+
+
+
+
+ Requests that the compositor anchor the surface to the specified edges
+ and corners. If two orthogonal edges are specified (e.g. 'top' and
+ 'left'), then the anchor point will be the intersection of the edges
+ (e.g. the top left corner of the output); otherwise the anchor point
+ will be centered on that edge, or in the center if none is specified.
+
+ Anchor is double-buffered, see wl_surface.commit.
+
+
+
+
+
+
+ Requests that the compositor avoids occluding an area with other
+ surfaces. The compositor's use of this information is
+ implementation-dependent - do not assume that this region will not
+ actually be occluded.
+
+ A positive value is only meaningful if the surface is anchored to one
+ edge or an edge and both perpendicular edges. If the surface is not
+ anchored, anchored to only two perpendicular edges (a corner), anchored
+ to only two parallel edges or anchored to all edges, a positive value
+ will be treated the same as zero.
+
+ A positive zone is the distance from the edge in surface-local
+ coordinates to consider exclusive.
+
+ Surfaces that do not wish to have an exclusive zone may instead specify
+ how they should interact with surfaces that do. If set to zero, the
+ surface indicates that it would like to be moved to avoid occluding
+ surfaces with a positive exclusive zone. If set to -1, the surface
+ indicates that it would not like to be moved to accommodate for other
+ surfaces, and the compositor should extend it all the way to the edges
+ it is anchored to.
+
+ For example, a panel might set its exclusive zone to 10, so that
+ maximized shell surfaces are not shown on top of it. A notification
+ might set its exclusive zone to 0, so that it is moved to avoid
+ occluding the panel, but shell surfaces are shown underneath it. A
+ wallpaper or lock screen might set their exclusive zone to -1, so that
+ they stretch below or over the panel.
+
+ The default value is 0.
+
+ Exclusive zone is double-buffered, see wl_surface.commit.
+
+
+
+
+
+
+ Requests that the surface be placed some distance away from the anchor
+ point on the output, in surface-local coordinates. Setting this value
+ for edges you are not anchored to has no effect.
+
+ The exclusive zone includes the margin.
+
+ Margin is double-buffered, see wl_surface.commit.
+
+
+
+
+
+
+
+
+
+ Types of keyboard interaction possible for layer shell surfaces. The
+ rationale for this is twofold: (1) some applications are not interested
+ in keyboard events and not allowing them to be focused can improve the
+ desktop experience; (2) some applications will want to take exclusive
+ keyboard focus.
+
+
+
+
+ This value indicates that this surface is not interested in keyboard
+ events and the compositor should never assign it the keyboard focus.
+
+ This is the default value, set for newly created layer shell surfaces.
+
+ This is useful for e.g. desktop widgets that display information or
+ only have interaction with non-keyboard input devices.
+
+
+
+
+ Request exclusive keyboard focus if this surface is above the shell surface layer.
+
+ For the top and overlay layers, the seat will always give
+ exclusive keyboard focus to the top-most layer which has keyboard
+ interactivity set to exclusive. If this layer contains multiple
+ surfaces with keyboard interactivity set to exclusive, the compositor
+ determines the one receiving keyboard events in an implementation-
+ defined manner. In this case, no guarantee is made when this surface
+ will receive keyboard focus (if ever).
+
+ For the bottom and background layers, the compositor is allowed to use
+ normal focus semantics.
+
+ This setting is mainly intended for applications that need to ensure
+ they receive all keyboard events, such as a lock screen or a password
+ prompt.
+
+
+
+
+ This requests the compositor to allow this surface to be focused and
+ unfocused by the user in an implementation-defined manner. The user
+ should be able to unfocus this surface even regardless of the layer
+ it is on.
+
+ Typically, the compositor will want to use its normal mechanism to
+ manage keyboard focus between layer shell surfaces with this setting
+ and regular toplevels on the desktop layer (e.g. click to focus).
+ Nevertheless, it is possible for a compositor to require a special
+ interaction to focus or unfocus layer shell surfaces (e.g. requiring
+ a click even if focus follows the mouse normally, or providing a
+ keybinding to switch focus between layers).
+
+ This setting is mainly intended for desktop shell components (e.g.
+ panels) that allow keyboard interaction. Using this option can allow
+ implementing a desktop shell that can be fully usable without the
+ mouse.
+
+
+
+
+
+
+ Set how keyboard events are delivered to this surface. By default,
+ layer shell surfaces do not receive keyboard events; this request can
+ be used to change this.
+
+ This setting is inherited by child surfaces set by the get_popup
+ request.
+
+ Layer surfaces receive pointer, touch, and tablet events normally. If
+ you do not want to receive them, set the input region on your surface
+ to an empty region.
+
+ Keyboard interactivity is double-buffered, see wl_surface.commit.
+
+
+
+
+
+
+ This assigns an xdg_popup's parent to this layer_surface. This popup
+ should have been created via xdg_surface::get_popup with the parent set
+ to NULL, and this request must be invoked before committing the popup's
+ initial state.
+
+ See the documentation of xdg_popup for more details about what an
+ xdg_popup is and how it is used.
+
+
+
+
+
+
+ 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.
+
+ If the client receives multiple configure events before it
+ can respond to one, it only has to ack the last configure event.
+
+ 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.
+
+
+
+
+
+
+ This request destroys the layer surface.
+
+
+
+
+
+ The configure event asks the client to resize its surface.
+
+ 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.
+
+ 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 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). If the client picks a smaller size and
+ is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
+ surface will be centered on this axis.
+
+ If the width or height arguments are zero, it means the client should
+ decide its own window dimension.
+
+
+
+
+
+
+
+
+ The closed event is sent by the compositor when the surface will no
+ longer be shown. The output may have been destroyed or the user may
+ have asked for it to be removed. Further changes to the surface will be
+ ignored. The client should destroy the resource after receiving this
+ event, and create a new surface if they so choose.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Change the layer that the surface is rendered on.
+
+ Layer is double-buffered, see wl_surface.commit.
+
+
+
+
+
diff --git a/protocol/wlr-screencopy-unstable-v1.xml b/protocol/wlr-screencopy-unstable-v1.xml
new file mode 100644
index 000000000..50b1b7d2a
--- /dev/null
+++ b/protocol/wlr-screencopy-unstable-v1.xml
@@ -0,0 +1,232 @@
+
+
+
+ Copyright © 2018 Simon Ser
+ Copyright © 2019 Andri Yngvason
+
+ 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.
+
+
+
+ This protocol allows clients to ask the compositor to copy part of the
+ screen content to a client buffer.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+
+
+
+
+ This object is a manager which offers requests to start capturing from a
+ source.
+
+
+
+
+ Capture the next frame of an entire output.
+
+
+
+
+
+
+
+
+ Capture the next frame of an output's region.
+
+ The region is given in output logical coordinates, see
+ xdg_output.logical_size. The region will be clipped to the output's
+ extents.
+
+
+
+
+
+
+
+
+
+
+
+
+ All objects created by the manager will still remain valid, until their
+ appropriate destroy request has been called.
+
+
+
+
+
+
+ This object represents a single frame.
+
+ When created, a series of buffer events will be sent, each representing a
+ supported buffer type. The "buffer_done" event is sent afterwards to
+ indicate that all supported buffer types have been enumerated. The client
+ will then be able to send a "copy" request. If the capture is successful,
+ the compositor will send a "flags" followed by a "ready" event.
+
+ For objects version 2 or lower, wl_shm buffers are always supported, ie.
+ the "buffer" event is guaranteed to be sent.
+
+ If the capture failed, the "failed" event is sent. This can happen anytime
+ before the "ready" event.
+
+ Once either a "ready" or a "failed" event is received, the client should
+ destroy the frame.
+
+
+
+
+ Provides information about wl_shm buffer parameters that need to be
+ used for this frame. This event is sent once after the frame is created
+ if wl_shm buffers are supported.
+
+
+
+
+
+
+
+
+
+ Copy the frame to the supplied buffer. The buffer must have a the
+ correct size, see zwlr_screencopy_frame_v1.buffer and
+ zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a
+ supported format.
+
+ If the frame is successfully copied, a "flags" and a "ready" events are
+ sent. Otherwise, a "failed" event is sent.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Provides flags about the frame. This event is sent once before the
+ "ready" event.
+
+
+
+
+
+
+ Called as soon as the frame is copied, indicating it is available
+ for reading. This event includes the time at which presentation happened
+ at.
+
+ The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples,
+ each component being an unsigned 32-bit value. Whole seconds are in
+ tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo,
+ and the additional fractional part in tv_nsec as nanoseconds. Hence,
+ for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part
+ may have an arbitrary offset at start.
+
+ After receiving this event, the client should destroy the object.
+
+
+
+
+
+
+
+
+ This event indicates that the attempted frame copy has failed.
+
+ After receiving this event, the client should destroy the object.
+
+
+
+
+
+ Destroys the frame. This request can be sent at any time by the client.
+
+
+
+
+
+
+ Same as copy, except it waits until there is damage to copy.
+
+
+
+
+
+
+ This event is sent right before the ready event when copy_with_damage is
+ requested. It may be generated multiple times for each copy_with_damage
+ request.
+
+ The arguments describe a box around an area that has changed since the
+ last copy request that was derived from the current screencopy manager
+ instance.
+
+ The union of all regions received between the call to copy_with_damage
+ and a ready event is the total damage since the prior ready event.
+
+
+
+
+
+
+
+
+
+
+ Provides information about linux-dmabuf buffer parameters that need to
+ be used for this frame. This event is sent once after the frame is
+ created if linux-dmabuf buffers are supported.
+
+
+
+
+
+
+
+
+ This event is sent once after all buffer events have been sent.
+
+ The client should proceed to create a buffer of one of the supported
+ types, and send a "copy" request.
+
+
+
+
diff --git a/protocol/xdg-shell-unstable-v6.xml b/protocol/xdg-shell-unstable-v6.xml
new file mode 100644
index 000000000..5e25e668e
--- /dev/null
+++ b/protocol/xdg-shell-unstable-v6.xml
@@ -0,0 +1,1044 @@
+
+
+
+
+ Copyright © 2008-2013 Kristian Høgsberg
+ Copyright © 2013 Rafael Antognolli
+ Copyright © 2013 Jasper St. Pierre
+ Copyright © 2010-2013 Intel Corporation
+
+ 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.
+
+
+
+
+ xdg_shell allows clients to turn a wl_surface into a "real window"
+ which can be dragged, resized, stacked, and moved around by the
+ user. Everything about this interface is suited towards traditional
+ desktop environments.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Destroy this xdg_shell object.
+
+ Destroying a bound xdg_shell object while there are surfaces
+ still alive created by this xdg_shell object instance is illegal
+ and will result in a protocol 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.
+
+ 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_shell.ping.
+
+
+
+
+
+
+ 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_shell.ping.
+
+ 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.
+
+ A compositor is free to ping in any way it wants, but a client must
+ always respond to any xdg_shell 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 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. The rectangle must be at least 1x1 large.
+
+ 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 zero or negative size is set the invalid_input error is raised.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Defines a set of edges for the anchor rectangle. These are used to
+ derive an anchor point that the child surface will be positioned
+ relative to. If two orthogonal edges are specified (e.g. 'top' and
+ 'left'), then the anchor point will be the intersection of the edges
+ (e.g. the top left position of the rectangle); 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.
+
+ If two parallel anchor edges are specified (e.g. 'left' and 'right'),
+ the invalid_input error is raised.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Defines in what direction a surface should be positioned, relative to
+ the anchor point of the parent surface. If two orthogonal gravities are
+ specified (e.g. 'bottom' and 'right'), then the child surface will be
+ placed in the specified direction; otherwise, the child surface will be
+ centered over the anchor point on any axis that had no gravity
+ specified.
+
+ If two parallel gravities are specified (e.g. 'left' and 'right'), the
+ 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 a monitor.
+
+
+
+
+ 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'.
+
+ 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.
+
+
+
+
+
+
+
+
+ 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.
+
+ For a 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 and (3) the client has committed a
+ buffer to the surface.
+
+
+
+
+
+
+
+
+
+
+ Destroy the xdg_surface object. An xdg_surface must only be destroyed
+ after its role object has been destroyed.
+
+
+
+
+
+ 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.
+
+ 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.
+
+ The window geometry is double buffered, and will be applied at the
+ time wl_surface.commit of the corresponding wl_surface is called.
+
+ 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.
+
+ The width and height must be greater than zero. Setting an invalid size
+ will raise an error. 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.
+
+
+
+
+
+
+
+
+
+ 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.
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+
+
+
+ Unmap and destroy the window. The window will be effectively
+ hidden from the user's point of view, and all state like
+ maximization, fullscreen, and so on, will be lost.
+
+
+
+
+
+ Set the "parent" of this surface. This window should be stacked
+ above a parent. The parent surface must be mapped as long as this
+ surface is mapped.
+
+ Parent windows should be set on dialogs, toolboxes, or other
+ "auxiliary" surfaces, so that the parent is raised when the dialog
+ 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".
+
+ 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] http://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.
+
+ 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. 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. They will get applied on
+ the next commit.
+
+
+
+ The surface is maximized. The window geometry specified in the configure
+ event must be obeyed by the client.
+
+
+
+
+ The surface is fullscreen. The window geometry specified in the configure
+ event must be obeyed by the client.
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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. They will get applied
+ on the next 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 a protocol 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
+ protocol 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. They will get applied
+ on the next 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 a protocol 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
+ protocol error.
+
+
+
+
+
+
+
+ Maximize the surface.
+
+ After requesting that the surface should be maximized, the compositor
+ will respond by emitting a configure event with the "maximized" state
+ and the required window geometry. The client should then update its
+ content, drawing it in a maximized state, i.e. without shadow or other
+ decoration outside of the window geometry. 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.
+
+
+
+
+
+ Unmaximize the surface.
+
+ After requesting that the surface should be unmaximized, the compositor
+ will respond by emitting a configure event without the "maximized"
+ state. If available, the compositor will include the window geometry
+ dimensions the window had prior to being maximized in the configure
+ request. The client must then update its content, drawing it in a
+ regular state, i.e. potentially with shadow, etc. 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.
+
+
+
+
+
+ Make the surface fullscreen.
+
+ You can specify an output that you would prefer to be fullscreen.
+ 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
+ black borders filling the rest of the output.
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+ The parent surface must have either the xdg_toplevel or xdg_popup surface
+ role.
+
+ 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 x and y arguments passed when creating the popup object specify
+ where the top left of the popup should be placed, relative to the
+ local surface coordinates of the parent surface. See
+ xdg_surface.get_popup. An xdg_popup must intersect with or be at least
+ partially adjacent to its parent surface.
+
+ 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, a 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.
+
+ The parent of a grabbing popup must either be another xdg_popup with an
+ active explicit grab, or an xdg_popup or xdg_toplevel, if there are no
+ explicit grabs already taken.
+
+ 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.
+
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
diff --git a/protocol/xdg-shell.xml b/protocol/xdg-shell.xml
new file mode 100644
index 000000000..cc44ad09e
--- /dev/null
+++ b/protocol/xdg-shell.xml
@@ -0,0 +1,1251 @@
+
+
+
+
+ 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 protocol 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 protocol 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.
+
+
+
+
+
+
+ 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.
+
+ 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 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.
+
+
+
+
+
+
+ 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, the client must
+ perform an initial commit without any buffer attached. The compositor
+ will reply with 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.
+
+
+
+
+
+
+
+
+
+
+ Destroy the xdg_surface object. An xdg_surface must only be destroyed
+ after its role object has been destroyed.
+
+
+
+
+
+ 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.
+
+ The window geometry is double buffered, and will be applied at the
+ time wl_surface.commit of the corresponding wl_surface is called.
+
+ 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.
+
+ The width and height must be greater than zero. Setting an invalid size
+ will raise an error. 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.
+
+
+
+
+
+
+
+
+
+ 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.
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+ 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 perfoming 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 windows 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 window removes any parent-child
+ relationship for the child. Setting a null parent for a window which
+ currently has no parent is a no-op.
+
+ If the parent is unmapped then its children are managed as
+ though the parent of the now-unmapped parent has become the
+ parent of this surface. If no parent exists for the now-unmapped
+ parent then the children are managed as though they have no
+ parent surface.
+
+
+
+
+
+
+ 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] http://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.
+
+ 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. 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. They will get applied on
+ the next commit.
+
+
+
+ The surface is maximized. The window geometry specified in the configure
+ event must be obeyed by the client.
+
+ 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 window is currently in a tiled layout and the right edge is
+ considered to be adjacent to another part of the tiling grid.
+
+
+
+
+ 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 window is currently in a tiled layout and the bottom edge is
+ considered to be adjacent to another part of the tiling grid.
+
+
+
+
+
+
+ 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. They will get applied
+ on the next 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 a protocol 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
+ protocol 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. They will get applied
+ on the next 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 a protocol 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
+ protocol 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.
+
+
+
+
+
+
+ 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, a 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.
+
+ The parent of a grabbing popup must either be another xdg_popup with an
+ active explicit grab, or an xdg_popup or xdg_toplevel, if there are no
+ explicit grabs already taken.
+
+ 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/protocol/xfway-shell.xml b/protocol/xfway-shell.xml
new file mode 100644
index 000000000..7394e156e
--- /dev/null
+++ b/protocol/xfway-shell.xml
@@ -0,0 +1,294 @@
+
+
+ Copyright (C) 2018 - 2022 adlo
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+
+
+ This is a private protocol for communication between xfwm4's Wayland compositor and its helper client.
+
+
+
+
+ This event is emitted whenever a new toplevel window is created. It
+ is emitted for all toplevels, regardless of the app that has created
+ them.
+
+ All initial details of the toplevel(title, app_id, states, etc.) will
+ be sent immediately after this event via the corresponding events in
+ zwlr_foreign_toplevel_handle_v1.
+
+
+
+
+
+
+ Indicates the client no longer wishes to receive events for new toplevels.
+ However the compositor may emit further toplevel_created events, until
+ the finished event is emitted.
+
+ The client must not send any more requests after this one.
+
+
+
+
+
+ This event indicates that the compositor is done sending events to the
+ zwlr_foreign_toplevel_manager_v1. The server will destroy the object
+ immediately after sending this request, so it will become invalid and
+ the client should free any resources associated with it.
+
+
+
+
+
+ Key press or key release event.
+
+
+
+
+
+
+
+
+ Tell the client to select the next window in the switcher.
+
+
+
+
+
+ Tell the client to destroy the alt-tab switcher.
+
+
+
+
+
+
+ Set the window as an alt-tab window switcher.
+
+
+
+
+
+
+
+
+
+ An xfwm_shell_window object represents an opened toplevel
+ window. Each app may have multiple opened toplevels.
+
+
+
+
+ This event is emitted whenever the title of the toplevel changes.
+
+
+
+
+
+
+ This event is emitted whenever the app-id of the toplevel changes.
+
+
+
+
+
+
+ This event is emitted whenever the toplevel becomes visible on
+ the given output. A toplevel may be visible on multiple outputs.
+
+
+
+
+
+
+ This event is emitted whenever the toplevel stops being visible on
+ the given output. It is guaranteed that an entered-output event
+ with the same output has been emitted before this event.
+
+
+
+
+
+
+ Requests that the toplevel be maximized. If the maximized state actually
+ changes, this will be indicated by the state event.
+
+
+
+
+
+ Requests that the toplevel be unmaximized. If the maximized state actually
+ changes, this will be indicated by the state event.
+
+
+
+
+
+ Requests that the toplevel be minimized. If the minimized state actually
+ changes, this will be indicated by the state event.
+
+
+
+
+
+ Requests that the toplevel be unminimized. If the minimized state actually
+ changes, this will be indicated by the state event.
+
+
+
+
+
+ Focus the window
+
+
+
+
+
+
+ Raise the window
+
+
+
+
+
+
+ Request that this toplevel be activated on the given seat.
+ There is no guarantee the toplevel will be actually activated.
+
+
+
+
+
+
+ The different states that a toplevel can have. These have the same meaning
+ as the states with the same names defined in xdg-toplevel
+
+
+
+
+
+
+
+
+
+
+ This event is emitted immediately after the zlw_foreign_toplevel_handle_v1
+ is created and each time the toplevel state changes, either because of a
+ compositor action or because of a request in this protocol.
+
+
+
+
+
+
+
+ This event is sent after all changes in the toplevel state have been
+ sent.
+
+ This allows changes to the zwlr_foreign_toplevel_handle_v1 properties
+ to be seen as atomic, even if they happen via multiple events.
+
+
+
+
+
+ Send a request to the toplevel to close itself. The compositor would
+ typically use a shell-specific method to carry out this request, for
+ example by sending the xdg_toplevel.close event. However, this gives
+ no guarantees the toplevel will actually be destroyed. If and when
+ this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
+ be emitted.
+
+
+
+
+
+ The rectangle of the surface specified in this request corresponds to
+ the place where the app using this protocol represents the given toplevel.
+ It can be used by the compositor as a hint for some operations, e.g
+ minimizing. The client is however not required to set this, in which
+ case the compositor is free to decide some default value.
+
+ If the client specifies more than one rectangle, only the last one is
+ considered.
+
+ The dimensions are given in surface-local coordinates.
+ Setting width=height=0 removes the already-set rectangle.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This event means the toplevel has been destroyed. It is guaranteed there
+ won't be any more events for this zwlr_foreign_toplevel_handle_v1. The
+ toplevel itself becomes inert so any requests will be ignored except the
+ destroy request.
+
+
+
+
+
+ Destroys the zwlr_foreign_toplevel_handle_v1 object.
+
+ This request should be called either when the client does not want to
+ use the toplevel anymore or after the closed event to finalize the
+ destruction of the object.
+
+
+
+
+
+
+
+ Requests that the toplevel be fullscreened on the given output. If the
+ fullscreen state and/or the outputs the toplevel is visible on actually
+ change, this will be indicated by the state and output_enter/leave
+ events.
+
+ The output parameter is only a hint to the compositor. Also, if output
+ is NULL, the compositor should decide which output the toplevel will be
+ fullscreened on, if at all.
+
+
+
+
+
+
+ Requests that the toplevel be unfullscreened. If the fullscreen state
+ actually changes, this will be indicated by the state event.
+
+
+
+
+
+
+ Alt-tab window switcher.
+
+
+
diff --git a/src/client.c b/src/client.c
index 4f47b0eb8..8a9056244 100644
--- a/src/client.c
+++ b/src/client.c
@@ -1581,7 +1581,7 @@ clientRestoreSizePos (Client *c)
}
Client *
-clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
+clientFrameX11 (DisplayInfo *display_info, Window w, gboolean recapture)
{
ScreenInfo *screen_info;
XWindowAttributes attr;
@@ -1625,6 +1625,40 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
goto out;
}
+ c = _clientFrame (display_info,
+ screen_info,
+ XFWM_CLIENT_TYPE_X11,
+ NULL,
+ w,
+ &attr,
+ FALSE);
+
+ out:
+ /* Window is reparented now, so we can safely release the grab
+ * on the server
+ */
+ myDisplayErrorTrapPopIgnored (display_info);
+ myDisplayUngrabServer (display_info);
+
+ return c;
+}
+
+Client *
+_clientFrame (DisplayInfo *display_info,
+ ScreenInfo *screen_info,
+ xfwmClientType client_type,
+ struct wlr_xdg_surface *xdg_surface,
+ Window w,
+ XWindowAttributes *attr,
+ gboolean recapture)
+{
+ XSetWindowAttributes attributes;
+ Client *c = NULL;
+ gboolean shaped;
+ gchar *wm_name;
+ unsigned long valuemask;
+ int i;
+
c = g_new0 (Client, 1);
if (!c)
{
@@ -1632,7 +1666,12 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
goto out;
}
+ c->client_type = client_type;
c->window = w;
+ if (client_type == XFWM_CLIENT_TYPE_WAYLAND)
+ {
+ c->xdg_toplevel = xdg_surface->toplevel;
+ }
c->screen_info = screen_info;
c->serial = screen_info->client_serial++;
@@ -1640,10 +1679,13 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
c->dialog_pid = 0;
c->dialog_fd = -1;
+ if (client_type == XFWM_CLIENT_TYPE_X11)
+ {
getWindowName (display_info, c->window, &wm_name);
getWindowHostname (display_info, c->window, &c->hostname);
c->name = clientCreateTitleName (c, wm_name, c->hostname);
g_free (wm_name);
+ }
getTransientFor (display_info, screen_info->xroot, c->window, &c->transient_for);
XChangeSaveSet(display_info->dpy, c->window, SetModeInsert);
@@ -1653,10 +1695,10 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
c->flags = 0L;
c->wm_flags = 0L;
c->xfwm_flags = XFWM_FLAG_INITIAL_VALUES;
- c->x = attr.x;
- c->y = attr.y;
- c->width = attr.width;
- c->height = attr.height;
+ c->x = attr->x;
+ c->y = attr->y;
+ c->width = attr->width;
+ c->height = attr->height;
c->applied_geometry.x = c->x;
c->applied_geometry.y = c->y;
@@ -1669,14 +1711,16 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
clientGetWMNormalHints (c, FALSE);
clientGetMWMHints (c);
+ if (client_type == XFWM_CLIENT_TYPE_X11)
+ {
c->size->x = c->x;
c->size->y = c->y;
c->size->width = c->width;
c->size->height = c->height;
c->frame_cache_width = -1;
c->frame_cache_height = -1;
- c->border_width = attr.border_width;
- c->cmap = attr.colormap;
+ c->border_width = attr->border_width;
+ c->cmap = attr->colormap;
shaped = clientCheckShape(c);
if (shaped)
@@ -1693,6 +1737,7 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
{
FLAG_SET (c->xfwm_flags, XFWM_FLAG_IS_RESIZABLE);
}
+
for (i = 0; i < BUTTON_COUNT; i++)
{
@@ -1734,6 +1779,8 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
/* Ping timeout */
c->ping_time = 0;
+ } /* Wayland guard */
+
c->class.res_name = NULL;
c->class.res_class = NULL;
XGetClassHint (display_info->dpy, w, &c->class);
@@ -1750,14 +1797,14 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
TRACE ("\"%s\" (0x%lx) initial map_state = %s",
c->name, c->window,
- (attr.map_state == IsUnmapped) ?
+ (attr->map_state == IsUnmapped) ?
"IsUnmapped" :
- (attr.map_state == IsViewable) ?
+ (attr->map_state == IsViewable) ?
"IsViewable" :
- (attr.map_state == IsUnviewable) ?
+ (attr->map_state == IsUnviewable) ?
"IsUnviewable" :
"(unknown)");
- if (attr.map_state != IsUnmapped)
+ if (attr->map_state != IsUnmapped)
{
/* Reparent will send us unmap/map events */
FLAG_SET (c->xfwm_flags, XFWM_FLAG_MAP_PENDING);
@@ -1766,13 +1813,22 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
c->type = UNSET;
c->type_atom = None;
+
+
FLAG_SET (c->flags, START_ICONIC (c) ? CLIENT_FLAG_ICONIFIED : 0);
+
+ if (client_type == XFWM_CLIENT_TYPE_X11)
+ {
FLAG_SET (c->wm_flags, HINTS_ACCEPT_INPUT (c->wmhints) ? WM_FLAG_INPUT : 0);
+ }
+
clientGetWMProtocols (c);
c->win_layer = WIN_LAYER_NORMAL;
c->pre_fullscreen_layer = c->win_layer;
+ if (client_type == XFWM_CLIENT_TYPE_X11)
+ {
/* net_wm_user_time standard */
c->user_time = 0;
c->user_time_win = getNetWMUserTimeWindow(display_info, c->window);
@@ -1807,11 +1863,12 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
if (!FLAG_TEST (c->xfwm_flags, XFWM_FLAG_SESSION_MANAGED))
{
clientCoordGravitate (c, c->gravity, APPLY, &c->x, &c->y);
- if (attr.map_state == IsUnmapped)
+ if (attr->map_state == IsUnmapped)
{
clientInitPosition (c);
}
}
+ } /* Wayland guard */
/*
Initialize "old" fields once the position is ensured, to avoid
@@ -1828,6 +1885,8 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
c->pre_fullscreen_geometry.width = c->width;
c->pre_fullscreen_geometry.height = c->height;
+ if (client_type == XFWM_CLIENT_TYPE_X11)
+ {
/*
We must call clientApplyInitialState() after having placed the
window so that the inital position values are correctly set if the
@@ -1841,12 +1900,12 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
attributes.bit_gravity = StaticGravity;
#ifdef HAVE_RENDER
- if ((attr.depth == 32) && (display_info->have_render))
+ if ((attr->depth == 32) && (display_info->have_render))
{
- c->visual = attr.visual;
- c->depth = attr.depth;
+ c->visual = attr->visual;
+ c->depth = attr->depth;
- attributes.colormap = attr.colormap;
+ attributes.colormap = attr->colormap;
attributes.background_pixmap = None;
attributes.border_pixel = 0;
attributes.background_pixel = 0;
@@ -1865,9 +1924,12 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
c->depth = screen_info->depth;
#endif /* HAVE_RENDER */
+if (!xfwmIsWaylandCompositor ())
+{
c->frame =
XCreateWindow (display_info->dpy, screen_info->xroot, 0, 0, 1, 1, 0,
c->depth, InputOutput, c->visual, valuemask, &attributes);
+}
#ifdef HAVE_XI2
xfwm_device_configure_xi2_event_mask (display_info->devices, display_info->dpy,
c->frame, attributes.event_mask);
@@ -1900,10 +1962,12 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
{
XShapeSelectInput (display_info->dpy, c->window, ShapeNotifyMask);
}
+ } /* Wayland guard */
clientAddToList (c);
clientGrabButtons(c);
-
+if (!xfwmIsWaylandCompositor ())
+{
/* Initialize per client menu button pixmap */
for (i = 0; i < STATE_TOGGLED; i++)
@@ -1954,6 +2018,7 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
/* Notify the compositor about this new window */
compositorAddWindow (display_info, c->frame, c);
+} /* Wayland guard */
if (!FLAG_TEST (c->flags, CLIENT_FLAG_ICONIFIED))
{
@@ -1984,11 +2049,14 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
setWMState (display_info, c->window, IconicState);
clientSetNetActions (c);
}
+ if (client_type == XFWM_CLIENT_TYPE_X11)
+ {
clientUpdateOpacity (c);
clientGrabMouseButton (c);
setNetFrameExtents (display_info, c->window, frameTop (c), frameLeft (c),
frameRight (c), frameBottom (c));
clientSetNetState (c);
+ }
#ifdef HAVE_XSYNC
c->xsync_counter = None;
@@ -2009,12 +2077,7 @@ clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
DBG ("client_count=%d", screen_info->client_count);
out:
- /* Window is reparented now, so we can safely release the grab
- * on the server
- */
- myDisplayErrorTrapPopIgnored (display_info);
- myDisplayUngrabServer (display_info);
-
+
return c;
}
@@ -2136,7 +2199,7 @@ clientFrameAll (ScreenInfo *screen_info)
XGetWindowAttributes (display_info->dpy, wins[i], &attr);
if ((attr.map_state == IsViewable) && (attr.root == screen_info->xroot))
{
- Client *c = clientFrame (display_info, wins[i], TRUE);
+ Client *c = clientFrameX11 (display_info, wins[i], TRUE);
if ((c) && ((screen_info->params->raise_on_click) || (screen_info->params->click_to_focus)))
{
clientGrabMouseButton (c);
diff --git a/src/client.h b/src/client.h
index bfe4d9c19..5e313c2a2 100644
--- a/src/client.h
+++ b/src/client.h
@@ -270,12 +270,28 @@ typedef enum
}
tilePositionType;
+typedef enum
+{
+ XFWM_CLIENT_TYPE_WAYLAND,
+ XFWM_CLIENT_TYPE_X11
+}
+xfwmClientType;
+
struct _Client
{
/* Reference to our screen structure */
ScreenInfo *screen_info;
+
+ xfwmWaylandCompositor *server;
+
+ xfwmClientType client_type;
Window window;
+
+ struct wlr_xdg_toplevel *xdg_toplevel;
+ struct wlr_xwayland_surface *xwayland_surface;
+ struct wlr_scene_tree *scene_tree;
+
Window frame;
Window transient_for;
Window user_time_win;
@@ -348,6 +364,20 @@ struct _Client
guint32 opacity;
guint32 opacity_applied;
guint opacity_flags;
+
+ struct wl_list link;
+
+ struct wlr_xdg_toplevel_decoration_v1 *decoration;
+
+ struct wl_listener map;
+ struct wl_listener unmap;
+ struct wl_listener destroy;
+ struct wl_listener new_popup;
+ struct wl_listener request_fullscreen;
+ struct wl_listener request_maximize;
+ struct wl_listener request_minimize;
+ struct wl_listener request_move;
+ struct wl_listener request_resize;
#ifdef HAVE_LIBSTARTUP_NOTIFICATION
/* Startup notification */
@@ -396,9 +426,19 @@ void clientGetWMProtocols (Client *);
void clientUpdateIcon (Client *);
void clientSaveSizePos (Client *);
gboolean clientRestoreSizePos (Client *);
-Client *clientFrame (DisplayInfo *,
+Client * _clientFrame (DisplayInfo *,
+ ScreenInfo *,
+ xfwmClientType,
+ struct wlr_xdg_surface *,
Window,
+ XWindowAttributes *,
gboolean);
+Client * clientFrameWayland (DisplayInfo *display_info,
+ struct wlr_xdg_surface *xdg_surface,
+ gboolean recapture);
+Client * clientFrameX11 (DisplayInfo *display_info,
+ Window w,
+ gboolean recapture);
void clientUnframe (Client *,
gboolean);
void clientFrameAll (ScreenInfo *);
diff --git a/src/display.c b/src/display.c
index ad43a2671..b34a27fd7 100644
--- a/src/display.c
+++ b/src/display.c
@@ -61,6 +61,8 @@
static DisplayInfo *default_display;
+static gboolean is_wayland_compositor = FALSE;
+
static gboolean
myDisplayInitAtoms (DisplayInfo *display_info)
{
@@ -329,7 +331,7 @@ myDisplayInit (GdkDisplay *gdisplay)
myDisplayCreateCursor (display);
- myDisplayCreateTimestampWin (display);
+ myDisplayCreateTimestampWin (display);
display->xfilter = NULL;
display->screens = NULL;
@@ -340,6 +342,8 @@ myDisplayInit (GdkDisplay *gdisplay)
display->nb_screens = 0;
display->current_time = CurrentTime;
+ if (!xfwmIsWaylandCompositor ())
+ {
hostnametmp = g_new0 (gchar, (size_t) MAX_HOSTNAME_LENGTH + 1);
if (gethostname ((char *) hostnametmp, MAX_HOSTNAME_LENGTH))
{
@@ -352,6 +356,7 @@ myDisplayInit (GdkDisplay *gdisplay)
g_free (hostnametmp);
compositorInitDisplay (display);
+ }
return display;
}
@@ -846,3 +851,15 @@ myDisplayGetKeymap (DisplayInfo *display_info)
{
return gdk_keymap_get_for_display (display_info->gdisplay);
}
+
+gboolean
+xfwmIsWaylandCompositor (void)
+{
+ return is_wayland_compositor;
+}
+
+void
+xfwmSetIsWaylandCompositor (gboolean value)
+{
+ is_wayland_compositor = value;
+}
diff --git a/src/display.h b/src/display.h
index b44ed384c..801ecb398 100644
--- a/src/display.h
+++ b/src/display.h
@@ -33,6 +33,22 @@
#include
#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
#ifndef ShapeInput
#define ShapeInput 2
#endif
@@ -67,6 +83,8 @@
#include "event_filter.h"
+#include "wayland/server.h"
+
/*
* The following macro is taken straight from metacity,
* if that needs some explanation, please refer to metacity's
@@ -416,4 +434,6 @@ gint myDisplayErrorTrapPop (DisplayInfo *);
void myDisplayErrorTrapPopIgnored (DisplayInfo *);
void myDisplayBeep (DisplayInfo *);
GdkKeymap *myDisplayGetKeymap (DisplayInfo *);
+gboolean xfwmIsWaylandCompositor (void);
+void xfwmSetIsWaylandCompositor (gboolean value);
#endif /* INC_DISPLAY_H */
diff --git a/src/events.c b/src/events.c
index c8da46640..a0dc5e87b 100644
--- a/src/events.c
+++ b/src/events.c
@@ -1168,7 +1168,7 @@ handleMapRequest (DisplayInfo *display_info, XMapRequestEvent * ev)
}
else
{
- clientFrame (display_info, ev->window, FALSE);
+ clientFrameX11 (display_info, ev->window, FALSE);
}
status = EVENT_FILTER_REMOVE;
diff --git a/src/hints.c b/src/hints.c
index 308493413..253ea172b 100644
--- a/src/hints.c
+++ b/src/hints.c
@@ -657,6 +657,12 @@ getTransientFor (DisplayInfo *display_info, Window root, Window w, Window * tran
TRACE ("window 0x%lx", w);
+ if (xfwmIsWaylandCompositor ())
+ {
+ *transient_for = None;
+ return;
+ }
+
myDisplayErrorTrapPush (display_info);
status = XGetTransientForHint (display_info->dpy, w, transient_for);
result = myDisplayErrorTrapPop (display_info);
diff --git a/src/main.c b/src/main.c
index 0f87df1f3..cb3a19cb2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -16,8 +16,8 @@
MA 02110-1301, USA.
- oroborus - (c) 2001 Ken Lynch
- xfwm4 - (c) 2002-2011 Olivier Fourdan
+ oroborus - (c) 2001 Ken Lynch
+ xfwm4 - (c) 2002-2011 Olivier Fourdan
*/
@@ -49,6 +49,7 @@
#include "display.h"
#include "screen.h"
+#include "wayland/server.h"
#include "events.h"
#include "event_filter.h"
#include "frame.h"
@@ -64,6 +65,8 @@
#include "compositor.h"
#include "spinning_cursor.h"
+static GMainLoop *xfwm_main_loop = NULL;
+
#define BASE_EVENT_MASK \
SubstructureNotifyMask|\
StructureNotifyMask|\
@@ -474,8 +477,8 @@ static int
initialize (gboolean replace_wm)
{
DisplayInfo *display_info;
- gint i, nscreens, default_screen;
-
+ gint i, nscreens, default_screen;
+
DBG ("xfwm4 starting, using GTK+-%d.%d.%d", gtk_major_version,
gtk_minor_version, gtk_micro_version);
@@ -490,13 +493,14 @@ initialize (gboolean replace_wm)
#else
display_info->enable_compositor = FALSE;
#endif /* HAVE_COMPOSITOR */
-
+
initModifiers (display_info->dpy);
setupHandler (TRUE);
- nscreens = ScreenCount (display_info->dpy);
- default_screen = DefaultScreen (display_info->dpy);
+ nscreens = ScreenCount (display_info->dpy);
+ default_screen = DefaultScreen (display_info->dpy);
+
for(i = 0; i < nscreens; i++)
{
ScreenInfo *screen_info;
@@ -506,7 +510,7 @@ initialize (gboolean replace_wm)
if (i == default_screen)
{
- gscr = gdk_display_get_default_screen (display_info->gdisplay);
+ gscr = gdk_display_get_default_screen (display_info->gdisplay);
}
else
{
@@ -534,15 +538,20 @@ initialize (gboolean replace_wm)
}
screen_info = myScreenInit (display_info, gscr, MAIN_EVENT_MASK, replace_wm);
+ wlr_log (WLR_INFO, "create screen\n");
+
if (!screen_info)
{
continue;
}
+ if (!xfwmIsWaylandCompositor ())
+ {
if (!initSettings (screen_info))
{
return -2;
}
+ }
#ifdef HAVE_COMPOSITOR
if (display_info->enable_compositor)
{
@@ -550,7 +559,13 @@ initialize (gboolean replace_wm)
}
#endif /* HAVE_COMPOSITOR */
sn_init_display (screen_info);
+
myDisplayAddScreen (display_info, screen_info);
+
+ if (xfwmIsWaylandCompositor ())
+ {
+ return 0;
+ }
screen_info->current_ws = getNetCurrentDesktop (display_info, screen_info->xroot);
setUTF8StringHint (display_info, screen_info->xfwm4_win, NET_WM_NAME, "Xfwm4");
setNetSupportedHint (display_info, screen_info->xroot, screen_info->xfwm4_win);
@@ -603,6 +618,7 @@ main (int argc, char **argv)
{
gboolean version = FALSE;
gboolean replace_wm = FALSE;
+ gboolean opt_wayland = FALSE;
int status;
GOptionContext *context;
GError *error = NULL;
@@ -626,6 +642,10 @@ main (int argc, char **argv)
#endif /* HAVE_COMPOSITOR */
{ "replace", 'r', 0, G_OPTION_ARG_NONE,
&replace_wm, N_("Replace the existing window manager"), NULL },
+ { "wayland", 0, 0, G_OPTION_ARG_NONE,
+ &opt_wayland,
+ N_("Run as a Wayland compositor"),
+ NULL,},
{ "version", 'V', 0, G_OPTION_ARG_NONE,
&version, N_("Print version information and exit"), NULL },
#ifdef DEBUG
@@ -684,11 +704,21 @@ main (int argc, char **argv)
return EXIT_FAILURE;
}
g_option_context_free (context);
+
+ xfwmSetIsWaylandCompositor (opt_wayland);
#ifdef DEBUG
setupLog (debug);
#endif /* DEBUG */
- DBG ("xfwm4 starting");
+ DBG ("xfwm4 starting");
+
+ if (xfwmIsWaylandCompositor ())
+ {
+ wlr_log_init (WLR_INFO, NULL);
+
+ xfwmWaylandInit ();
+
+ }
gtk_init (&argc, &argv);
@@ -700,6 +730,15 @@ main (int argc, char **argv)
init_pango_cache ();
status = initialize (replace_wm);
+
+ xfwmWaylandCompositor *compositor = xfwmWaylandGetDefault ();
+
+ if (xfwmIsWaylandCompositor ())
+ {
+ wl_event_loop_dispatch (wl_display_get_event_loop (compositor->wl_display), -1);
+ wl_event_loop_dispatch (wl_display_get_event_loop (compositor->wl_display), -1);
+ wl_event_loop_dispatch (wl_display_get_event_loop (compositor->wl_display), -1);
+ }
/*
status < 0 => Error, cancel execution
status == 0 => Run w/out session manager
@@ -725,7 +764,10 @@ main (int argc, char **argv)
exit (1);
break;
}
+ if (!xfwmIsWaylandCompositor ())
+ {
cleanUp ();
+ }
DBG ("xfwm4 terminated");
return 0;
}
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 000000000..87cb1b940
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,76 @@
+xfwm4_sources = [
+ 'client.c',
+ 'compositor.c',
+ 'cycle.c',
+ 'device.c',
+ 'display.c',
+ 'event_filter.c',
+ 'events.c',
+ 'focus.c',
+ 'frame.c',
+ 'hints.c',
+ 'icons.c',
+ 'keyboard.c',
+ 'main.c',
+ 'menu.c',
+ 'misc.c',
+ 'moveresize.c',
+ 'mypixmap.c',
+ 'mywindow.c',
+ 'netwm.c',
+ 'parserc.c',
+ 'placement.c',
+ 'poswin.c',
+ 'screen.c',
+ 'session.c',
+ 'settings.c',
+ 'spinning_cursor.c',
+ 'stacking.c',
+ 'startup_notification.c',
+ 'tabwin.c',
+ 'terminate.c',
+ 'transients.c',
+ 'ui_style.c',
+ 'wireframe.c',
+ 'workspaces.c',
+ 'xsync.c',
+ 'wayland/cursor.c',
+ 'wayland/decoration.c',
+ 'wayland/layer_shell.c',
+ 'wayland/output.c',
+ 'wayland/seat.c',
+ 'wayland/server.c',
+ 'wayland/xdg_shell.c',
+ 'wayland/xwayland.c',
+]
+
+xfwm4_dependencies = [
+ glib,
+ gtk,
+ compositor,
+ xrender,
+ xext,
+ xrandr,
+ x11,
+ libxfce4kbd_private,
+ libxfce4ui,
+ libxfce4util,
+ libxfconf,
+ xinerama,
+ m_dep,
+ xfwm_server_protos,
+ evdev,
+ libinput,
+ libxml2,
+ wayland_server,
+ wlroots,
+ xkbcommon,
+]
+
+xfwm4_exe = executable('xfwm4',
+ xfwm4_sources,
+ dependencies: xfwm4_dependencies,
+ include_directories: incdir,
+ link_with: xfwm_common,
+ install: true
+)
diff --git a/src/screen.c b/src/screen.c
index 8a7c176ba..642ae1206 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -170,7 +170,9 @@ myScreenInit (DisplayInfo *display_info, GdkScreen *gscr, unsigned long event_ma
int i, j;
g_return_val_if_fail (display_info, NULL);
+
g_return_val_if_fail (GDK_IS_SCREEN (gscr), NULL);
+
TRACE ("entering");
screen_info = g_new0 (ScreenInfo, 1);
@@ -180,7 +182,7 @@ myScreenInit (DisplayInfo *display_info, GdkScreen *gscr, unsigned long event_ma
screen_info->gscr = gscr;
desktop_visible = 0;
layout = NULL;
-
+
/* Create a GTK window so that we are just like any other GTK application */
screen_info->gtk_win = gtk_window_new (GTK_WINDOW_POPUP);
gtk_window_set_screen (GTK_WINDOW (screen_info->gtk_win), gscr);
@@ -208,6 +210,8 @@ myScreenInit (DisplayInfo *display_info, GdkScreen *gscr, unsigned long event_ma
screen_info->shape_win = (Window) None;
myScreenComputeSize (screen_info);
+ if (!xfwmIsWaylandCompositor ())
+ {
screen_info->xfwm4_win = gdk_x11_window_get_xid (gtk_widget_get_window (screen_info->gtk_win));
if (!myScreenSetWMAtom (screen_info, replace_wm))
{
@@ -224,6 +228,7 @@ myScreenInit (DisplayInfo *display_info, GdkScreen *gscr, unsigned long event_ma
return NULL;
}
gdk_window_set_user_data (event_win, screen_info->gtk_win);
+ }
screen_info->current_ws = 0;
screen_info->previous_ws = 0;
@@ -250,6 +255,8 @@ myScreenInit (DisplayInfo *display_info, GdkScreen *gscr, unsigned long event_ma
screen_info->key_grabs = 0;
screen_info->pointer_grabs = 0;
+ if (!xfwmIsWaylandCompositor ())
+ {
getHint (display_info, screen_info->xroot, NET_SHOWING_DESKTOP, &desktop_visible);
screen_info->show_desktop = (desktop_visible != 0);
@@ -327,6 +334,7 @@ myScreenInit (DisplayInfo *display_info, GdkScreen *gscr, unsigned long event_ma
screen_info->monitors_index = NULL;
myScreenInvalidateMonitorCache (screen_info);
myScreenRebuildMonitorIndex (screen_info);
+ }
return (screen_info);
}
diff --git a/src/stacking.c b/src/stacking.c
index ec714db9b..50e1b0322 100644
--- a/src/stacking.c
+++ b/src/stacking.c
@@ -369,6 +369,11 @@ clientRaise (Client * c, Window wsibling)
TRACE ("client \"%s\" (0x%lx) above (0x%lx)", c->name, c->window, wsibling);
+ if (xfwmIsWaylandCompositor () && !c->scene_tree)
+ {
+ return;
+ }
+
if (!FLAG_TEST (c->xfwm_flags, XFWM_FLAG_MANAGED))
{
return;
@@ -427,8 +432,20 @@ clientRaise (Client * c, Window wsibling)
/* Now, screen_info->windows_stack contains the correct window stack
We still need to tell the X Server to reflect the changes
*/
+ if (xfwmIsWaylandCompositor ())
+ {
+ xfwmWaylandCompositor *compositor = xfwmWaylandGetDefault ();
+ /* Move the view to the front */
+ if (!compositor->seat->focused_layer)
+ {
+ wlr_scene_node_raise_to_top(&c->scene_tree->node);
+ }
+ }
+ else
+ {
clientApplyStackList (screen_info);
clientSetNetClientList (screen_info, display_info->atoms[NET_CLIENT_LIST_STACKING], screen_info->windows_stack);
+ }
screen_info->last_raise = c;
}
diff --git a/src/wayland/cursor.c b/src/wayland/cursor.c
new file mode 100644
index 000000000..318aa0fb5
--- /dev/null
+++ b/src/wayland/cursor.c
@@ -0,0 +1,269 @@
+#include "cursor.h"
+#include "client.h"
+#include "xdg_shell.h"
+
+void reset_cursor_mode(xfwmWaylandCompositor *server) {
+ /* Reset the cursor mode to passthrough. */
+ server->cursor->cursor_mode = XFWM_CURSOR_PASSTHROUGH;
+ server->grabbed_view = NULL;
+}
+
+static void process_cursor_move(xfwmWaylandCompositor *server, uint32_t time) {
+ /* Move the grabbed view to the new position. */
+ Client *view = server->grabbed_view;
+ if (view->scene_tree->node.type == WLR_SCENE_NODE_TREE) {
+ view->x = server->cursor->cursor->x - server->grab_x;
+ view->y = server->cursor->cursor->y - server->grab_y;
+ wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y);
+ }
+}
+
+static void process_cursor_resize(xfwmWaylandCompositor *server,
+ uint32_t time) {
+ /*
+ * Resizing the grabbed view can be a little bit complicated, because we
+ * could be resizing from any corner or edge. This not only resizes the view
+ * on one or two axes, but can also move the view if you resize from the top
+ * or left edges (or top-left corner).
+ *
+ * Note that I took some shortcuts here. In a more fleshed-out compositor,
+ * you'd wait for the client to prepare a buffer at the new size, then
+ * commit any movement that was prepared.
+ */
+ Client *view = server->grabbed_view;
+ double border_x = server->cursor->cursor->x - server->grab_x;
+ double border_y = server->cursor->cursor->y - server->grab_y;
+ int new_left = server->grab_geobox.x;
+ int new_right = server->grab_geobox.x + server->grab_geobox.width;
+ int new_top = server->grab_geobox.y;
+ int new_bottom = server->grab_geobox.y + server->grab_geobox.height;
+
+ if (server->resize_edges & WLR_EDGE_TOP) {
+ new_top = border_y;
+ if (new_top >= new_bottom) {
+ new_top = new_bottom - 1;
+ }
+ } else if (server->resize_edges & WLR_EDGE_BOTTOM) {
+ new_bottom = border_y;
+ if (new_bottom <= new_top) {
+ new_bottom = new_top + 1;
+ }
+ }
+ if (server->resize_edges & WLR_EDGE_LEFT) {
+ new_left = border_x;
+ if (new_left >= new_right) {
+ new_left = new_right - 1;
+ }
+ } else if (server->resize_edges & WLR_EDGE_RIGHT) {
+ new_right = border_x;
+ if (new_right <= new_left) {
+ new_right = new_left + 1;
+ }
+ }
+
+ struct wlr_box geo_box;
+ wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box);
+ view->x = new_left - geo_box.x;
+ view->y = new_top - geo_box.y;
+ wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y);
+
+ int new_width = new_right - new_left;
+ int new_height = new_bottom - new_top;
+ wlr_xdg_toplevel_set_size(view->xdg_toplevel, new_width, new_height);
+}
+
+static void process_cursor_motion(xfwmWaylandCompositor *server,
+ uint32_t time) {
+ /* If the mode is non-passthrough, delegate to those functions. */
+ if (server->cursor->cursor_mode == XFWM_CURSOR_MOVE) {
+ process_cursor_move(server, time);
+ return;
+ } else if (server->cursor->cursor_mode == XFWM_CURSOR_RESIZE) {
+ process_cursor_resize(server, time);
+ return;
+ }
+
+ /* Otherwise, find the view under the pointer and send the event along. */
+ double sx, sy;
+ struct wlr_seat *seat = server->seat->seat;
+ struct wlr_surface *surface = NULL;
+ Client *view = get_view_at(server, server->cursor->cursor->x,
+ server->cursor->cursor->y, &surface, &sx, &sy);
+ if (!view) {
+ /* If there's no view under the cursor, set the cursor image to a
+ * default. This is what makes the cursor image appear when you move it
+ * around the screen, not over any views. */
+ wlr_cursor_set_xcursor(server->cursor->cursor,
+ server->cursor->xcursor_manager, "left_ptr");
+ }
+ if (surface) {
+ /*
+ * Send pointer enter and motion events.
+ *
+ * The enter event gives the surface "pointer focus", which is distinct
+ * from keyboard focus. You get pointer focus by moving the pointer over
+ * a window.
+ *
+ * Note that wlroots will avoid sending duplicate enter/motion events if
+ * the surface has already has pointer focus or if the client is already
+ * aware of the coordinates passed.
+ */
+ wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
+ wlr_seat_pointer_notify_motion(seat, time, sx, sy);
+ } else {
+ /* Clear pointer focus so future button events and such are not sent to
+ * the last client to have the cursor over it. */
+ wlr_seat_pointer_clear_focus(seat);
+ }
+
+ wlr_idle_notifier_v1_notify_activity(server->idle_notifier, seat);
+}
+
+static void handle_cursor_motion(struct wl_listener *listener, void *data) {
+ /* This event is forwarded by the cursor when a pointer emits a _relative_
+ * pointer motion event (i.e. a delta) */
+ xfwmCursor *cursor = wl_container_of(listener, cursor, cursor_motion);
+ struct wlr_pointer_motion_event *event = data;
+ /* The cursor doesn't move unless we tell it to. The cursor automatically
+ * handles constraining the motion to the output layout, as well as any
+ * special configuration applied for the specific input device which
+ * generated the event. You can pass NULL for the device if you want to move
+ * the cursor around without any input. */
+ wlr_cursor_move(cursor->cursor, &event->pointer->base, event->delta_x,
+ event->delta_y);
+ process_cursor_motion(cursor->server, event->time_msec);
+}
+
+static void handle_cursor_motion_absolute(struct wl_listener *listener,
+ void *data) {
+ /* This event is forwarded by the cursor when a pointer emits an _absolute_
+ * motion event, from 0..1 on each axis. This happens, for example, when
+ * wlroots is running under a Wayland window rather than KMS+DRM, and you
+ * move the mouse over the window. You could enter the window from any edge,
+ * so we have to warp the mouse there. There is also some hardware which
+ * emits these events. */
+ xfwmCursor *cursor =
+ wl_container_of(listener, cursor, cursor_motion_absolute);
+ struct wlr_pointer_motion_absolute_event *event = data;
+ wlr_cursor_warp_absolute(cursor->cursor, &event->pointer->base, event->x,
+ event->y);
+ process_cursor_motion(cursor->server, event->time_msec);
+}
+
+static void handle_cursor_button(struct wl_listener *listener, void *data) {
+ /* This event is forwarded by the cursor when a pointer emits a button
+ * event. */
+ xfwmCursor *cursor = wl_container_of(listener, cursor, cursor_button);
+ struct wlr_pointer_button_event *event = data;
+ /* Notify the client with pointer focus that a button press has occurred */
+ wlr_seat_pointer_notify_button(cursor->server->seat->seat, event->time_msec,
+ event->button, event->state);
+ double sx, sy;
+ struct wlr_surface *surface = NULL;
+ Client *view =
+ get_view_at(cursor->server, cursor->server->cursor->cursor->x,
+ cursor->server->cursor->cursor->y, &surface, &sx, &sy);
+ if (event->state == WLR_BUTTON_RELEASED) {
+ /* If you released any buttons, we exit interactive move/resize mode. */
+ reset_cursor_mode(cursor->server);
+ } else {
+ /* Focus that client if the button was _pressed_ */
+ focus_view(view, surface);
+ }
+
+ wlr_idle_notifier_v1_notify_activity(cursor->server->idle_notifier,
+ cursor->server->seat->seat);
+}
+
+static void handle_cursor_axis(struct wl_listener *listener, void *data) {
+ /* This event is forwarded by the cursor when a pointer emits an axis event,
+ * for example when you move the scroll wheel. */
+ xfwmCursor *cursor = wl_container_of(listener, cursor, cursor_axis);
+ struct wlr_pointer_axis_event *event = data;
+ /* Notify the client with pointer focus of the axis event. */
+ wlr_seat_pointer_notify_axis(cursor->server->seat->seat, event->time_msec,
+ event->orientation, event->delta,
+ event->delta_discrete, event->source);
+}
+
+static void handle_cursor_frame(struct wl_listener *listener, void *data) {
+ /* This event is forwarded by the cursor when a pointer emits an frame
+ * event. Frame events are sent after regular pointer events to group
+ * multiple events together. For instance, two axis events may happen at the
+ * same time, in which case a frame event won't be sent in between. */
+ xfwmCursor *cursor = wl_container_of(listener, cursor, cursor_frame);
+ /* Notify the client with pointer focus of the frame event. */
+ wlr_seat_pointer_notify_frame(cursor->server->seat->seat);
+}
+
+static void handle_cursor_request(struct wl_listener *listener, void *data) {
+ xfwmCursor *cursor = wl_container_of(listener, cursor, request_cursor);
+ /* This event is raised by the seat when a client provides a cursor image */
+ struct wlr_seat_pointer_request_set_cursor_event *event = data;
+ struct wlr_seat_client *focused_client =
+ cursor->server->seat->seat->pointer_state.focused_client;
+ /* This can be sent by any client, so we check to make sure this one is
+ * actually has pointer focus first. */
+ if (focused_client == event->seat_client) {
+ /* Once we've vetted the client, we can tell the cursor to use the
+ * provided surface as the cursor image. It will set the hardware cursor
+ * on the output that it's currently on and continue to do so as the
+ * cursor moves between outputs. */
+ wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x,
+ event->hotspot_y);
+ }
+}
+
+xfwmCursor *cursor_create(xfwmWaylandCompositor *server) {
+ xfwmCursor *cursor = malloc(sizeof(xfwmCursor));
+ cursor->cursor = wlr_cursor_create();
+ cursor->server = server;
+ wlr_cursor_attach_output_layout(cursor->cursor, server->output_layout);
+
+ /* Creates an xcursor manager, another wlroots utility which loads up
+ * Xcursor themes to source cursor images from and makes sure that cursor
+ * images are available at all scale factors on the screen (necessary for
+ * HiDPI support). We add a cursor theme at scale factor 1 to begin with. */
+ cursor->xcursor_manager = wlr_xcursor_manager_create(NULL, 24);
+ wlr_xcursor_manager_load(cursor->xcursor_manager, 1);
+
+ /*
+ * wlr_cursor *only* displays an image on screen. It does not move around
+ * when the pointer moves. However, we can attach input devices to it, and
+ * it will generate aggregate events for all of them. In these events, we
+ * can choose how we want to process them, forwarding them to clients and
+ * moving the cursor around. More detail on this process is described in
+ * Drew DeVault's input handling blog post:
+ *
+ * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html
+ *
+ * And more comments are sprinkled throughout the notify functions above.
+ */
+ cursor->cursor_mode = XFWM_CURSOR_PASSTHROUGH;
+ cursor->cursor_motion.notify = handle_cursor_motion;
+ wl_signal_add(&cursor->cursor->events.motion, &cursor->cursor_motion);
+ cursor->cursor_motion_absolute.notify = handle_cursor_motion_absolute;
+ wl_signal_add(&cursor->cursor->events.motion_absolute,
+ &cursor->cursor_motion_absolute);
+ cursor->cursor_button.notify = handle_cursor_button;
+ wl_signal_add(&cursor->cursor->events.button, &cursor->cursor_button);
+ cursor->cursor_axis.notify = handle_cursor_axis;
+ wl_signal_add(&cursor->cursor->events.axis, &cursor->cursor_axis);
+ cursor->cursor_frame.notify = handle_cursor_frame;
+ wl_signal_add(&cursor->cursor->events.frame, &cursor->cursor_frame);
+ cursor->request_cursor.notify = handle_cursor_request;
+ wl_signal_add(&server->seat->seat->events.request_set_cursor,
+ &cursor->request_cursor);
+
+ return cursor;
+}
+
+void cursor_destroy(xfwmCursor *cursor) {
+ if (!cursor) {
+ return;
+ }
+
+ wlr_xcursor_manager_destroy(cursor->xcursor_manager);
+ wlr_cursor_destroy(cursor->cursor);
+ free(cursor);
+}
\ No newline at end of file
diff --git a/src/wayland/cursor.h b/src/wayland/cursor.h
new file mode 100644
index 000000000..ca50e7f5e
--- /dev/null
+++ b/src/wayland/cursor.h
@@ -0,0 +1,38 @@
+#ifndef _XFWM_CURSOR_H
+#define _XFWM_CURSOR_H
+#include
+#include
+
+struct _xfwmWaylandCompositor;
+typedef struct _xfwmWaylandCompositor xfwmWaylandCompositor;
+
+enum CursorMode {
+ XFWM_CURSOR_PASSTHROUGH,
+ XFWM_CURSOR_MOVE,
+ XFWM_CURSOR_RESIZE,
+};
+
+struct _xfwmCursor {
+ struct wlr_cursor *cursor;
+ struct wlr_xcursor_manager *xcursor_manager;
+
+ xfwmWaylandCompositor *server;
+
+ enum CursorMode cursor_mode;
+ struct wl_listener cursor_motion;
+ struct wl_listener cursor_motion_absolute;
+
+ struct wl_listener cursor_button;
+ struct wl_listener cursor_axis;
+ struct wl_listener cursor_frame;
+
+ struct wl_listener request_cursor;
+};
+
+typedef struct _xfwmCursor xfwmCursor;
+
+xfwmCursor *cursor_create(xfwmWaylandCompositor *server);
+void cursor_destroy(xfwmCursor *cursor);
+void reset_cursor_mode(xfwmWaylandCompositor *server);
+
+#endif /* cursor.h */
diff --git a/src/wayland/decoration.c b/src/wayland/decoration.c
new file mode 100644
index 000000000..957f17f88
--- /dev/null
+++ b/src/wayland/decoration.c
@@ -0,0 +1,43 @@
+#include "decoration.h"
+#include "client.h"
+
+static void destroy_xdg_toplevel_decoration(struct wl_listener *listener, void *data) {
+ xfwmDecoration *decoration = wl_container_of(listener, decoration, toplevel_decoration_destroy);
+ wl_list_remove(&decoration->toplevel_decoration_destroy.link);
+ free(decoration);
+}
+
+static void free_xdg_decoration_mode(struct wl_listener *listener, void *data) {
+ xfwmDecoration *decoration = wl_container_of(listener, decoration, mode_destroy);
+ wl_list_remove(&decoration->mode_destroy.link);
+ wl_list_remove(&decoration->request_mode.link);
+}
+
+static void handle_xdg_decoration_mode(struct wl_listener *listener, void *data) {
+ struct wlr_xdg_toplevel_decoration_v1 *toplevel_decoration = data;
+ xfwmDecoration *decoration = wl_container_of(listener, decoration, request_mode);
+ Client *view = wl_container_of(decoration->server->views.next, view, link);
+ wlr_xdg_toplevel_decoration_v1_set_mode(toplevel_decoration, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
+ view->decoration = toplevel_decoration;
+}
+
+static void handle_new_xdg_toplevel_decoration(struct wl_listener *listener, void *data) {
+ xfwmDecoration *decoration = (xfwmDecoration *) calloc(1, sizeof(xfwmDecoration));
+ xfwmWaylandCompositor *server = wl_container_of(listener, server, new_xdg_decoration);
+ decoration->server = server;
+ struct wlr_xdg_toplevel_decoration_v1 *toplevel_decoration = data;
+ decoration->toplevel_decoration_destroy.notify = destroy_xdg_toplevel_decoration;
+ wl_signal_add(&toplevel_decoration->manager->events.destroy, &decoration->toplevel_decoration_destroy);
+ decoration->request_mode.notify = handle_xdg_decoration_mode;
+ wl_signal_add(&toplevel_decoration->events.request_mode, &decoration->request_mode);
+ decoration->mode_destroy.notify = free_xdg_decoration_mode;
+ wl_signal_add(&toplevel_decoration->events.destroy, &decoration->mode_destroy);
+ /* For some reason, a lot of clients don't emit the request_mode signal. */
+ handle_xdg_decoration_mode(&decoration->request_mode, toplevel_decoration);
+}
+
+void init_xdg_decoration(xfwmWaylandCompositor *server) {
+ struct wlr_xdg_decoration_manager_v1 *decoration = wlr_xdg_decoration_manager_v1_create(server->wl_display);
+ server->new_xdg_decoration.notify = handle_new_xdg_toplevel_decoration;
+ wl_signal_add(&decoration->events.new_toplevel_decoration, &server->new_xdg_decoration);
+}
diff --git a/src/wayland/decoration.h b/src/wayland/decoration.h
new file mode 100644
index 000000000..38a9731d9
--- /dev/null
+++ b/src/wayland/decoration.h
@@ -0,0 +1,19 @@
+#ifndef _XFWM_DECORATION_H
+#define _XFWM_DECORATION_H
+
+#include
+
+#include "server.h"
+
+struct _xfwmDecoration {
+ xfwmWaylandCompositor *server;
+
+ struct wl_listener toplevel_decoration_destroy;
+ struct wl_listener request_mode;
+ struct wl_listener mode_destroy;
+};
+
+typedef struct _xfwmDecoration xfwmDecoration;
+
+void init_xdg_decoration(xfwmWaylandCompositor *server);
+#endif
diff --git a/src/wayland/layer_shell.c b/src/wayland/layer_shell.c
new file mode 100644
index 000000000..16f55638d
--- /dev/null
+++ b/src/wayland/layer_shell.c
@@ -0,0 +1,358 @@
+/*
+ * More or less taken verbatim from wio .
+ * Additional material taken from sway .
+ *
+ * Copyright 2019 Drew DeVault
+ * Copyright 2022 Sway Developers
+ */
+#include
+#include
+
+#include "client.h"
+#include "xdg_shell.h"
+
+static void descriptor_destroy(xfwmSceneDescriptor *desc) {
+ if (!desc) {
+ return;
+ }
+
+ wl_list_remove(&desc->destroy.link);
+
+ free(desc);
+}
+
+static void handle_descriptor_destroy(struct wl_listener *listener,
+ void *data) {
+ xfwmSceneDescriptor *desc = wl_container_of(listener, desc, destroy);
+
+ descriptor_destroy(desc);
+}
+
+void assign_scene_descriptor(struct wlr_scene_node *node,
+ enum SceneDescriptorType type, void *data) {
+ xfwmSceneDescriptor *desc = calloc(1, sizeof(xfwmSceneDescriptor));
+
+ if (!desc) {
+ return;
+ }
+
+ desc->type = type;
+ desc->data = data;
+
+ desc->destroy.notify = handle_descriptor_destroy;
+ wl_signal_add(&node->events.destroy, &desc->destroy);
+
+ node->data = desc;
+}
+
+static void arrange_surface(Output *output, struct wlr_box *full_area,
+ struct wlr_box *usable_area,
+ struct wlr_scene_tree *scene_tree) {
+ struct wlr_scene_node *node;
+ wl_list_for_each(node, &scene_tree->children, link) {
+ xfwmSceneDescriptor *desc = node->data;
+
+ if (desc->type == XFWM_SCENE_DESC_LAYER_SHELL) {
+ xfwmLayerSurface *surface = desc->data;
+ wlr_scene_layer_surface_v1_configure(surface->scene, full_area,
+ usable_area);
+ }
+ }
+}
+
+void arrange_layers(Output *output) {
+ struct wlr_box usable_area = {0};
+ struct wlr_box full_area;
+ wlr_output_effective_resolution(output->wlr_output, &usable_area.width,
+ &usable_area.height);
+
+ memcpy(&full_area, &usable_area, sizeof(struct wlr_box));
+
+ arrange_surface(output, &full_area, &usable_area,
+ output->layers.shell_background);
+ arrange_surface(output, &full_area, &usable_area,
+ output->layers.shell_bottom);
+ arrange_surface(output, &full_area, &usable_area, output->layers.shell_top);
+ arrange_surface(output, &full_area, &usable_area,
+ output->layers.shell_overlay);
+}
+
+static struct wlr_scene_tree *
+xfwm_layer_get_scene(Output *output, enum zwlr_layer_shell_v1_layer type) {
+ switch (type) {
+ case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND:
+ return output->layers.shell_background;
+ case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM:
+ return output->layers.shell_bottom;
+ case ZWLR_LAYER_SHELL_V1_LAYER_TOP:
+ return output->layers.shell_top;
+ case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY:
+ return output->layers.shell_overlay;
+ }
+
+ /* Unreachable */
+ return NULL;
+}
+
+static xfwmLayerSurface *
+xfwm_layer_surface_create(struct wlr_scene_layer_surface_v1 *scene) {
+ xfwmLayerSurface *surface = calloc(1, sizeof(xfwmLayerSurface));
+ if (!surface) {
+ return NULL;
+ }
+
+ surface->scene = scene;
+
+ return surface;
+}
+
+static void handle_surface_commit(struct wl_listener *listener, void *data) {
+ xfwmLayerSurface *surface =
+ wl_container_of(listener, surface, surface_commit);
+
+ if (!surface->output) {
+ return;
+ }
+
+ struct wlr_layer_surface_v1 *layer_surface = surface->scene->layer_surface;
+ uint32_t committed = layer_surface->current.committed;
+
+ enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer;
+ struct wlr_scene_tree *output_layer =
+ xfwm_layer_get_scene(surface->output, layer_type);
+
+ if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) {
+ wlr_scene_node_reparent(&surface->scene->tree->node, output_layer);
+ }
+
+ if (committed || layer_surface->surface->mapped != surface->mapped) {
+ surface->mapped = layer_surface->surface->mapped;
+ arrange_layers(surface->output);
+
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ wlr_surface_send_frame_done(layer_surface->surface, &now);
+ }
+
+ if (layer_surface->current.layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
+ wlr_scene_node_raise_to_top(&output_layer->node);
+ }
+
+ if (layer_surface == surface->server->seat->focused_layer) {
+ seat_focus_surface(surface->server->seat, layer_surface->surface);
+ }
+}
+
+static void handle_map(struct wl_listener *listener, void *data) {
+ xfwmLayerSurface *surface = wl_container_of(listener, surface, map);
+
+ struct wlr_layer_surface_v1 *layer_surface = surface->scene->layer_surface;
+
+ /* focus on new surface */
+ if (layer_surface->current.keyboard_interactive &&
+ (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY ||
+ layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) {
+
+ Seat *seat = surface->server->seat;
+ /* but only if the currently focused layer has a lower precedence */
+ if (!seat->focused_layer ||
+ seat->focused_layer->current.layer >= layer_surface->current.layer) {
+ seat_set_focus_layer(seat, layer_surface);
+ }
+ arrange_layers(surface->output);
+ }
+}
+
+static void handle_unmap(struct wl_listener *listener, void *data) {
+ xfwmLayerSurface *surface = wl_container_of(listener, surface, unmap);
+
+ Seat *seat = surface->server->seat;
+ if (seat->focused_layer == surface->scene->layer_surface) {
+ seat_set_focus_layer(seat, NULL);
+ }
+}
+
+static void xfwm_layer_surface_destroy(xfwmLayerSurface *surface) {
+ if (surface == NULL) {
+ return;
+ }
+
+ wl_list_remove(&surface->map.link);
+ wl_list_remove(&surface->unmap.link);
+ wl_list_remove(&surface->surface_commit.link);
+ wl_list_remove(&surface->destroy.link);
+
+ free(surface);
+}
+
+static void handle_destroy(struct wl_listener *listener, void *data) {
+ xfwmLayerSurface *surface = wl_container_of(listener, surface, destroy);
+
+ if (surface->output) {
+ arrange_layers(surface->output);
+ }
+
+ xfwm_layer_surface_destroy(surface);
+}
+
+static void popup_handle_destroy(struct wl_listener *listener, void *data) {
+ xfwmLayerPopup *popup = wl_container_of(listener, popup, destroy);
+
+ wl_list_remove(&popup->destroy.link);
+ wl_list_remove(&popup->new_popup.link);
+ free(popup);
+}
+
+static xfwmLayerSurface *popup_get_layer(xfwmLayerPopup *popup) {
+ struct wlr_scene_node *current = &popup->scene->node;
+ while (current) {
+ if (current->data) {
+ xfwmSceneDescriptor *desc = current->data;
+ if (desc->type == XFWM_SCENE_DESC_LAYER_SHELL) {
+ return desc->data;
+ }
+ }
+
+ current = ¤t->parent->node;
+ }
+
+ return NULL;
+}
+
+static void popup_unconstrain(xfwmLayerPopup *popup) {
+ xfwmLayerSurface *surface = popup_get_layer(popup);
+ if (!surface) {
+ return;
+ }
+
+ struct wlr_xdg_popup *wlr_popup = popup->wlr_popup;
+ Output *output = surface->output;
+
+ int lx, ly;
+ wlr_scene_node_coords(&popup->scene->node, &lx, &ly);
+
+ /* The output box expressed in the coordinate system of the toplevel
+ * parent of the popup. */
+ struct wlr_box output_toplevel_sx_box = {
+ .x = output->geometry.x - MIN(lx, 0),
+ .y = output->geometry.y - MAX(ly, 0),
+ .width = output->geometry.width,
+ .height = output->geometry.height,
+ };
+
+ wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
+}
+
+static void popup_handle_new_popup(struct wl_listener *listener, void *data);
+
+static xfwmLayerPopup *create_popup(struct wlr_xdg_popup *wlr_popup,
+ struct wlr_scene_tree *parent) {
+ xfwmLayerPopup *popup = calloc(1, sizeof(xfwmLayerPopup));
+ if (popup == NULL) {
+ return NULL;
+ }
+
+ popup->wlr_popup = wlr_popup;
+
+ popup->scene = wlr_scene_xdg_surface_create(parent, wlr_popup->base);
+
+ if (!popup->scene) {
+ free(popup);
+ return NULL;
+ }
+
+ assign_scene_descriptor(&popup->scene->node,
+ XFWM_SCENE_DESC_LAYER_SHELL_POPUP, popup);
+
+ popup->destroy.notify = popup_handle_destroy;
+ wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
+
+ popup->new_popup.notify = popup_handle_new_popup;
+ wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
+
+ popup_unconstrain(popup);
+
+ return popup;
+}
+
+static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
+ xfwmLayerPopup *layer_popup =
+ wl_container_of(listener, layer_popup, new_popup);
+ struct wlr_xdg_popup *wlr_popup = data;
+ create_popup(wlr_popup, layer_popup->scene);
+}
+
+static void handle_new_popup(struct wl_listener *listener, void *data) {
+ xfwmLayerSurface *xfwm_layer_surface =
+ wl_container_of(listener, xfwm_layer_surface, new_popup);
+ struct wlr_xdg_popup *wlr_popup = data;
+
+ create_popup(wlr_popup, xfwm_layer_surface->scene->tree);
+}
+
+void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
+ struct wlr_layer_surface_v1 *layer_surface = data;
+
+ if (layer_surface->output == NULL) {
+ xfwmWaylandCompositor *server =
+ wl_container_of(listener, server, new_layer_surface);
+ layer_surface->output = wlr_output_layout_output_at(
+ server->output_layout, server->cursor->cursor->x,
+ server->cursor->cursor->y);
+ }
+ Output *output = layer_surface->output->data;
+
+ if (!layer_surface->output) {
+ /* Assign last active output */
+ layer_surface->output = output->wlr_output;
+ }
+
+ enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer;
+ struct wlr_scene_tree *output_layer =
+ xfwm_layer_get_scene(output, layer_type);
+ struct wlr_scene_layer_surface_v1 *scene_surface =
+ wlr_scene_layer_surface_v1_create(output_layer, layer_surface);
+ if (!scene_surface) {
+ return;
+ }
+
+ xfwmLayerSurface *surface = xfwm_layer_surface_create(scene_surface);
+
+ assign_scene_descriptor(&scene_surface->tree->node,
+ XFWM_SCENE_DESC_LAYER_SHELL, surface);
+ if (!scene_surface->tree->node.data) {
+ wlr_layer_surface_v1_destroy(layer_surface);
+ return;
+ }
+
+ surface->output = output;
+ surface->server = output->server;
+
+ surface->surface_commit.notify = handle_surface_commit;
+ wl_signal_add(&layer_surface->surface->events.commit,
+ &surface->surface_commit);
+ surface->map.notify = handle_map;
+ wl_signal_add(&layer_surface->surface->events.map, &surface->map);
+ surface->unmap.notify = handle_unmap;
+ wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap);
+ surface->destroy.notify = handle_destroy;
+ wl_signal_add(&layer_surface->events.destroy, &surface->destroy);
+ surface->new_popup.notify = handle_new_popup;
+ wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup);
+
+ /* Temporarily set the layer's current state to pending
+ * So that we can easily arrange it */
+ struct wlr_layer_surface_v1_state old_state = layer_surface->current;
+ layer_surface->current = layer_surface->pending;
+ arrange_layers(output);
+ layer_surface->current = old_state;
+}
+
+void init_layer_shell(xfwmWaylandCompositor *server) {
+
+ server->layer_shell = wlr_layer_shell_v1_create(server->wl_display, 4);
+
+ server->new_layer_surface.notify = handle_layer_shell_surface;
+ wl_signal_add(&server->layer_shell->events.new_surface,
+ &server->new_layer_surface);
+}
diff --git a/src/wayland/layer_shell.h b/src/wayland/layer_shell.h
new file mode 100644
index 000000000..a7b585dc5
--- /dev/null
+++ b/src/wayland/layer_shell.h
@@ -0,0 +1,67 @@
+#ifndef _XFWM_LAYERS_H
+#define _XFWM_LAYERS_H
+#include
+
+struct _xfwmWaylandCompositor;
+typedef struct _xfwmWaylandCompositor xfwmWaylandCompositor;
+
+struct _Output;
+typedef struct _Output Output;
+
+typedef struct _Client Client;
+
+struct _xfwmLayerSurface {
+ Output *output;
+ xfwmWaylandCompositor *server;
+
+ struct wlr_scene_layer_surface_v1 *scene;
+
+ bool mapped;
+
+ struct wl_listener destroy;
+ struct wl_listener map;
+ struct wl_listener unmap;
+ struct wl_listener surface_commit;
+ struct wl_listener new_popup;
+};
+
+typedef struct _xfwmLayerSurface xfwmLayerSurface;
+
+struct _xfwmLayerPopup {
+ struct wlr_xdg_popup *wlr_popup;
+ struct wlr_scene_tree *scene;
+
+ struct wl_listener destroy;
+ struct wl_listener new_popup;
+};
+
+typedef struct _xfwmLayerPopup xfwmLayerPopup;
+
+struct _xfwmLayerSubsurface {
+ struct wlr_scene_tree *scene;
+
+ struct wl_listener destroy;
+};
+
+typedef struct _xfwmLayerSubsurface xfwmLayerSubsurface;
+
+enum SceneDescriptorType {
+ XFWM_SCENE_DESC_NODE,
+ XFWM_SCENE_DESC_LAYER_SHELL,
+ XFWM_SCENE_DESC_LAYER_SHELL_POPUP,
+};
+
+struct _xfwmSceneDescriptor {
+ enum SceneDescriptorType type;
+ void *data;
+ struct wl_listener destroy;
+};
+
+typedef struct _xfwmSceneDescriptor xfwmSceneDescriptor;
+
+void init_layer_shell(xfwmWaylandCompositor *server);
+void assign_scene_descriptor(struct wlr_scene_node *node,
+ enum SceneDescriptorType type, void *data);
+
+#endif
+
diff --git a/src/wayland/meson.build b/src/wayland/meson.build
new file mode 100644
index 000000000..18c4970ef
--- /dev/null
+++ b/src/wayland/meson.build
@@ -0,0 +1,30 @@
+xfwm4_wayland_src = files(
+ 'cursor.c',
+ 'decoration.c',
+ 'layer_shell.c',
+ 'main.c',
+ 'output.c',
+ 'seat.c',
+ 'server.c',
+ 'xdg_shell.c',
+ 'xwayland.c',
+ )
+
+xfwm4_wayland_dep = [
+ xfwm_server_protos,
+ evdev,
+ libinput,
+ libxml2,
+ wayland_server,
+ wlroots,
+ xkbcommon,
+ gtk,
+ ]
+
+executable(
+ 'xfwm4-wayland',
+ xfwm4_wayland_src,
+ include_directories: [incdir],
+ dependencies: [xfwm4_wayland_dep],
+ install: true,
+ )
diff --git a/src/wayland/output.c b/src/wayland/output.c
new file mode 100644
index 000000000..d4c818023
--- /dev/null
+++ b/src/wayland/output.c
@@ -0,0 +1,125 @@
+/* Copyright (c) 2018 - 2023 adlo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "output.h"
+#include
+
+void output_frame_notify(struct wl_listener *listener, void *data) {
+ /* This function is called every time an output is ready to display a frame,
+ * generally at the output's refresh rate (e.g. 60Hz). */
+ Output *output = wl_container_of(listener, output, frame);
+ struct wlr_scene *scene = output->server->scene;
+
+ struct wlr_scene_output *scene_output =
+ wlr_scene_get_scene_output(scene, output->wlr_output);
+
+ wlr_output_layout_get_box(output->server->output_layout, output->wlr_output,
+ &output->geometry);
+
+ /* Update the background for the current output size. */
+ wlr_scene_rect_set_size(output->background, output->geometry.width,
+ output->geometry.height);
+
+ /* Render the scene if needed and commit the output */
+ wlr_scene_output_commit(scene_output, NULL);
+
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ wlr_scene_output_send_frame_done(scene_output, &now);
+}
+
+void output_destroy_notify(struct wl_listener *listener, void *data) {
+ Output *output = wl_container_of(listener, output, destroy);
+
+ wl_list_remove(&output->frame.link);
+ wl_list_remove(&output->destroy.link);
+
+ wl_list_remove(&output->request_state.link);
+
+ /* Frees the layers */
+ size_t num_layers = sizeof(output->layers) / sizeof(struct wlr_scene_node *);
+ for (size_t i = 0; i < num_layers; i++) {
+ struct wlr_scene_node *node =
+ ((struct wlr_scene_node **)&output->layers)[i];
+ wlr_scene_node_destroy(node);
+ }
+
+ wl_list_remove(&output->link);
+ g_free(output);
+}
+
+void new_output_notify(struct wl_listener *listener, void *data) {
+ /* This event is raised by the backend when a new output (aka a display or
+ * monitor) becomes available. */
+ xfwmWaylandCompositor *server = wl_container_of(listener, server, new_output);
+ struct wlr_output *wlr_output = data;
+
+ /* Configures the output created by the backend to use our allocator
+ * and our renderer. Must be done once, before commiting the output */
+ wlr_output_init_render(wlr_output, server->allocator, server->renderer);
+
+ /* Some backends don't have modes. DRM+KMS does, and we need to set a mode
+ * before we can use the output. The mode is a tuple of (width, height,
+ * refresh rate), and each monitor supports only a specific set of modes. We
+ * just pick the monitor's preferred mode, a more sophisticated compositor
+ * would let the user configure it. */
+ if (!wl_list_empty(&wlr_output->modes)) {
+ struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
+ wlr_output_set_mode(wlr_output, mode);
+ wlr_output_enable(wlr_output, true);
+ if (!wlr_output_commit(wlr_output)) {
+ return;
+ }
+ }
+
+ /* Allocates and configures our state for this output */
+ Output *output = g_new0(Output, 1);
+ output->wlr_output = wlr_output;
+ output->server = server;
+
+ /* Set the background color */
+ float color[4] = {0.1875, 0.1875, 0.1875, 1.0};
+ output->background = wlr_scene_rect_create(&server->scene->tree, 0, 0, color);
+ wlr_scene_node_lower_to_bottom(&output->background->node);
+
+ /* Initializes the layers */
+ size_t num_layers = sizeof(output->layers) / sizeof(struct wlr_scene_node *);
+ for (size_t i = 0; i < num_layers; i++) {
+ ((struct wlr_scene_node **)&output->layers)[i] =
+ &wlr_scene_tree_create(&server->scene->tree)->node;
+ }
+
+ /* Sets up a listener for the frame notify event. */
+ output->frame.notify = output_frame_notify;
+ wl_signal_add(&wlr_output->events.frame, &output->frame);
+
+ /* Sets up a listener for the destroy notify event. */
+ output->destroy.notify = output_destroy_notify;
+ wl_signal_add(&wlr_output->events.destroy, &output->destroy);
+
+ wl_list_insert(&server->outputs, &output->link);
+
+ /* Adds this to the output layout. The add_auto function arranges outputs
+ * from left-to-right in the order they appear. A more sophisticated
+ * compositor would let the user configure the arrangement of outputs in the
+ * layout.
+ *
+ * The output layout utility automatically adds a wl_output global to the
+ * display, which Wayland clients can see to find out information about the
+ * output (such as DPI, scale factor, manufacturer, etc).
+ */
+ wlr_output_layout_add_auto(server->output_layout, wlr_output);
+}
\ No newline at end of file
diff --git a/src/wayland/output.h b/src/wayland/output.h
new file mode 100644
index 000000000..a66c187ac
--- /dev/null
+++ b/src/wayland/output.h
@@ -0,0 +1,55 @@
+/* Copyright (c) 2018 - 2023 adlo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _XFWM_OUTPUT_H
+#define _XFWM_OUTPUT_H
+
+#include
+#include
+
+#include "server.h"
+
+struct _Output
+{
+ struct wlr_output *wlr_output;
+ xfwmWaylandCompositor *server;
+ struct wl_list link;
+
+ struct
+ {
+ struct wlr_scene_tree *shell_background;
+ struct wlr_scene_tree *shell_bottom;
+ struct wlr_scene_tree *shell_fullscreen;
+ struct wlr_scene_tree *shell_overlay;
+ struct wlr_scene_tree *shell_top;
+ } layers;
+
+ struct wlr_scene_rect *background;
+
+ struct wlr_box geometry;
+
+ struct wl_listener frame;
+ struct wl_listener destroy;
+ struct wl_listener request_state;
+};
+
+typedef struct _Output Output;
+
+void output_frame_notify(struct wl_listener* listener, void *data);
+void output_destroy_notify(struct wl_listener* listener, void *data);
+void new_output_notify(struct wl_listener* listener, void *data);
+
+#endif /* output.h */
\ No newline at end of file
diff --git a/src/wayland/seat.c b/src/wayland/seat.c
new file mode 100644
index 000000000..12efac65f
--- /dev/null
+++ b/src/wayland/seat.c
@@ -0,0 +1,250 @@
+#include
+
+#include
+#include
+#include
+
+#include "client.h"
+#include "seat.h"
+#include "wayland/server.h"
+#include "xdg_shell.h"
+
+static void deiconify_view(Client *view) {
+ if (view->xdg_toplevel->requested.minimized) {
+ view->xdg_toplevel->requested.minimized = false;
+ wl_signal_emit(&view->xdg_toplevel->events.request_minimize, NULL);
+ }
+}
+
+static void keyboard_handle_destroy(struct wl_listener *listener, void *data) {
+ /* This event is raised by the keyboard base wlr_input_device to signal
+ * the destruction of the wlr_keyboard. It will no longer receive events
+ * and should be destroyed.
+ */
+ Keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
+ wl_list_remove(&keyboard->modifiers.link);
+ wl_list_remove(&keyboard->key.link);
+ wl_list_remove(&keyboard->destroy.link);
+ wl_list_remove(&keyboard->link);
+ free(keyboard);
+}
+
+static void keyboard_handle_modifiers(struct wl_listener *listener,
+ void *data) {
+ /* This event is raised when a modifier key, such as shift or alt, is
+ * pressed. We simply communicate this to the client. */
+ Keyboard *keyboard = wl_container_of(listener, keyboard, modifiers);
+ /*
+ * A seat can only have one keyboard, but this is a limitation of the
+ * Wayland protocol - not wlroots. We assign all connected keyboards to the
+ * same seat. You can swap out the underlying wlr_keyboard like this and
+ * wlr_seat handles this transparently.
+ */
+ wlr_seat_set_keyboard(keyboard->server->seat->seat, keyboard->keyboard);
+ /* Send modifiers to the client. */
+ wlr_seat_keyboard_notify_modifiers(keyboard->server->seat->seat,
+ &keyboard->keyboard->modifiers);
+}
+
+static bool handle_keybinding(xfwmWaylandCompositor *server, xkb_keysym_t sym,
+ uint32_t modifiers) {
+ /*
+ * Here we handle compositor keybindings. This is when the compositor is
+ * processing keys, rather than passing them on to the client for its own
+ * processing.
+ */
+
+ struct wlr_session *session;
+ server->backend = wlr_backend_autocreate(server->wl_display, &session);
+
+ if (modifiers & (WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT) &&
+ sym >= XKB_KEY_XF86Switch_VT_1 && sym <= XKB_KEY_XF86Switch_VT_12) {
+ unsigned int vt = sym - XKB_KEY_XF86Switch_VT_1 + 1;
+ wlr_session_change_vt(session, vt);
+
+ return true;
+ }
+
+ if (modifiers & WLR_MODIFIER_ALT && sym == XKB_KEY_Tab) {
+ /* Cycle to the next view */
+ if (wl_list_length(&server->views) >= 1) {
+ Client *current_view =
+ wl_container_of(server->views.prev, current_view, link);
+ deiconify_view(current_view);
+ focus_view(current_view, current_view->xdg_toplevel->base->surface);
+ }
+ } else if (sym == XKB_KEY_Escape && modifiers & WLR_MODIFIER_CTRL)
+ gtk_main_quit();
+ else
+ return false;
+ return true;
+}
+
+static void keyboard_handle_key(struct wl_listener *listener, void *data) {
+ /* This event is raised when a key is pressed or released. */
+ Keyboard *keyboard = wl_container_of(listener, keyboard, key);
+ xfwmWaylandCompositor *server = keyboard->server;
+ struct wlr_keyboard_key_event *event = data;
+ struct wlr_seat *seat = server->seat->seat;
+
+ /* Translate libinput keycode -> xkbcommon */
+ uint32_t keycode = event->keycode + 8;
+ /* Get a list of keysyms based on the keymap for this keyboard */
+ const xkb_keysym_t *syms;
+ int nsyms =
+ xkb_state_key_get_syms(keyboard->keyboard->xkb_state, keycode, &syms);
+
+ bool handled = false;
+ uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->keyboard);
+ if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ /* If this button was _pressed_, we attempt to
+ * process it as a compositor keybinding. */
+ for (int i = 0; i < nsyms; i++) {
+ handled = handle_keybinding(server, syms[i], modifiers);
+ }
+ }
+
+ if (!handled) {
+ /* Otherwise, we pass it along to the client. */
+ wlr_seat_set_keyboard(seat, keyboard->keyboard);
+ wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode,
+ event->state);
+ }
+
+ wlr_idle_notifier_v1_notify_activity(server->idle_notifier, seat);
+}
+
+static void handle_new_keyboard(xfwmWaylandCompositor *server,
+ struct wlr_input_device *device) {
+ struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(device);
+
+ Keyboard *keyboard = calloc(1, sizeof(Keyboard));
+ keyboard->server = server;
+ keyboard->keyboard = wlr_keyboard;
+
+ /* We need to prepare an XKB keymap and assign it to the keyboard. This
+ * assumes the defaults (e.g. layout = "us"). */
+ struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ struct xkb_keymap *keymap =
+ xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS);
+
+ wlr_keyboard_set_keymap(wlr_keyboard, keymap);
+ xkb_keymap_unref(keymap);
+ xkb_context_unref(context);
+ wlr_keyboard_set_repeat_info(wlr_keyboard, 25, 600);
+
+ /* Here we set up listeners for keyboard events. */
+ keyboard->modifiers.notify = keyboard_handle_modifiers;
+ wl_signal_add(&keyboard->keyboard->events.modifiers, &keyboard->modifiers);
+ keyboard->key.notify = keyboard_handle_key;
+ wl_signal_add(&keyboard->keyboard->events.key, &keyboard->key);
+ keyboard->destroy.notify = keyboard_handle_destroy;
+ wl_signal_add(&device->events.destroy, &keyboard->destroy);
+
+ wlr_seat_set_keyboard(server->seat->seat, keyboard->keyboard);
+
+ /* And add the keyboard to our list of keyboards */
+ wl_list_insert(&server->seat->keyboards, &keyboard->link);
+}
+
+static void handle_new_pointer(xfwmWaylandCompositor *server,
+ struct wlr_input_device *device) {
+ /* We don't do anything special with pointers. All of our pointer handling
+ * is proxied through wlr_cursor. On another compositor, you might take this
+ * opportunity to do libinput configuration on the device to set
+ * acceleration, etc. */
+ wlr_cursor_attach_input_device(server->cursor->cursor, device);
+}
+
+static void new_input_notify(struct wl_listener *listener, void *data) {
+ /* This event is raised by the backend when a new input device becomes
+ * available. */
+ xfwmWaylandCompositor *server = wl_container_of(listener, server, new_input);
+ struct wlr_input_device *device = data;
+ switch (device->type) {
+ case WLR_INPUT_DEVICE_KEYBOARD:
+ handle_new_keyboard(server, device);
+ break;
+ case WLR_INPUT_DEVICE_POINTER:
+ handle_new_pointer(server, device);
+ break;
+ default:
+ break;
+ }
+ /* We need to let the wlr_seat know what our capabilities are, which is
+ * communiciated to the client. In xfwm4-wayland we always have a cursor, even
+ * if there are no pointer devices, so we always include that capability. */
+ uint32_t caps = WL_SEAT_CAPABILITY_POINTER;
+ if (!wl_list_empty(&server->keyboards)) {
+ caps |= WL_SEAT_CAPABILITY_KEYBOARD;
+ }
+ wlr_seat_set_capabilities(server->seat->seat, caps);
+}
+
+void seat_focus_surface(Seat *seat, struct wlr_surface *surface) {
+ if (!surface) {
+ wlr_seat_keyboard_notify_clear_focus(seat->seat);
+ return;
+ }
+
+ struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat->seat);
+ if (kb != NULL) {
+ wlr_seat_keyboard_notify_enter(seat->seat, surface, kb->keycodes,
+ kb->num_keycodes, &kb->modifiers);
+ }
+}
+
+void seat_set_focus_layer(Seat *seat, struct wlr_layer_surface_v1 *layer) {
+ if (!layer) {
+ seat->focused_layer = NULL;
+ return;
+ }
+ seat_focus_surface(seat, layer->surface);
+ if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
+ seat->focused_layer = layer;
+ }
+}
+
+static void handle_request_set_primary_selection(struct wl_listener *listener,
+ void *data) {
+ Seat *seat = wl_container_of(listener, seat, request_set_primary_selection);
+ struct wlr_seat_request_set_primary_selection_event *event = data;
+ wlr_seat_set_primary_selection(seat->seat, event->source, event->serial);
+}
+
+static void handle_request_set_selection(struct wl_listener *listener,
+ void *data) {
+ /* This event is raised by the seat when a client wants to set the selection,
+ * usually when the user copies something. wlroots allows compositors to
+ * ignore such requests if they so choose, but in tinywl we always honor
+ */
+ Seat *seat = wl_container_of(listener, seat, request_set_selection);
+ struct wlr_seat_request_set_selection_event *event = data;
+ wlr_seat_set_selection(seat->seat, event->source, event->serial);
+}
+
+Seat *seatCreate(xfwmWaylandCompositor *server) {
+ Seat *seat = malloc(sizeof(Seat));
+
+ wl_list_init(&seat->keyboards);
+ server->new_input.notify = new_input_notify;
+ wl_signal_add(&server->backend->events.new_input, &server->new_input);
+ seat->seat = wlr_seat_create(server->wl_display, "seat0");
+
+ seat->request_set_selection.notify = handle_request_set_selection;
+ wl_signal_add(&seat->seat->events.request_set_selection,
+ &seat->request_set_selection);
+ seat->request_set_primary_selection.notify =
+ handle_request_set_primary_selection;
+ wl_signal_add(&seat->seat->events.request_set_primary_selection,
+ &seat->request_set_primary_selection);
+
+ return seat;
+}
+
+void seatDestroy(Seat *seat) {
+ wl_list_remove(&seat->keyboards);
+ wl_list_remove(&seat->request_set_primary_selection.link);
+ wl_list_remove(&seat->request_set_selection.link);
+ free(seat);
+}
\ No newline at end of file
diff --git a/src/wayland/seat.h b/src/wayland/seat.h
new file mode 100644
index 000000000..da8b30390
--- /dev/null
+++ b/src/wayland/seat.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2018 - 2023 adlo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _XFWM_SEAT_H
+#define _XFWM_SEAT_H
+
+#include
+
+struct _xfwmWaylandCompositor;
+typedef struct _xfwmWaylandCompositor xfwmWaylandCompositor;
+
+struct _Seat {
+ struct wlr_seat *seat;
+
+ struct wlr_layer_surface_v1 *focused_layer;
+
+ struct wl_list keyboards;
+
+ struct wl_listener request_set_primary_selection;
+ struct wl_listener request_set_selection;
+};
+
+typedef struct _Seat Seat;
+
+struct _Keyboard {
+ struct wl_list link;
+ xfwmWaylandCompositor *server;
+ struct wlr_keyboard *keyboard;
+
+ struct wl_listener destroy;
+ struct wl_listener modifiers;
+ struct wl_listener key;
+};
+
+typedef struct _Keyboard Keyboard;
+
+Seat *seatCreate (xfwmWaylandCompositor *server);
+void seat_focus_surface(Seat *seat, struct wlr_surface *surface);
+void seat_set_focus_layer(Seat *seat, struct wlr_layer_surface_v1 *layer);
+void seatDestroy (Seat *seat);
+#endif
diff --git a/src/wayland/server.c b/src/wayland/server.c
new file mode 100644
index 000000000..aefb86372
--- /dev/null
+++ b/src/wayland/server.c
@@ -0,0 +1,213 @@
+/* Copyright (c) 2018 - 2023 adlo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "server.h"
+#include "xdg_shell.h"
+#include "xwayland.h"
+
+static xfwmWaylandCompositor _xfwm_wayland_compositor;
+
+typedef struct {
+ GSource source;
+ struct wl_display *display;
+} WaylandEventSource;
+
+static gboolean wayland_event_source_prepare(GSource *base, int *timeout) {
+ WaylandEventSource *source = (WaylandEventSource *)base;
+
+ *timeout = -1;
+
+ wl_display_flush_clients(source->display);
+
+ return FALSE;
+}
+
+static gboolean
+wayland_event_source_dispatch(GSource *base, GSourceFunc callback, void *data) {
+ WaylandEventSource *source = (WaylandEventSource *)base;
+ struct wl_event_loop *loop = wl_display_get_event_loop(source->display);
+
+ wl_event_loop_dispatch(loop, 0);
+
+ return TRUE;
+}
+
+static GSourceFuncs wayland_event_source_funcs = {
+ wayland_event_source_prepare, NULL, wayland_event_source_dispatch, NULL};
+
+static GSource *wayland_event_source_new(struct wl_display *display) {
+ GSource *source;
+ WaylandEventSource *wayland_source;
+ struct wl_event_loop *loop = wl_display_get_event_loop(display);
+
+ source =
+ g_source_new(&wayland_event_source_funcs, sizeof(WaylandEventSource));
+ wayland_source = (WaylandEventSource *)source;
+ wayland_source->display = display;
+ g_source_add_unix_fd(&wayland_source->source, wl_event_loop_get_fd(loop),
+ G_IO_IN | G_IO_ERR);
+
+ return &wayland_source->source;
+}
+
+void xfwmWaylandInit(void) {
+ GSource *wayland_event_source;
+ xfwmWaylandCompositor *compositor = &_xfwm_wayland_compositor;
+
+ memset(compositor, 0, sizeof(xfwmWaylandCompositor));
+
+ /* The Wayland display is managed by libwayland. It handles accepting
+ * clients from the Unix socket, manging Wayland globals, and so on. */
+ compositor->wl_display = wl_display_create();
+
+ wayland_event_source = wayland_event_source_new(compositor->wl_display);
+
+ g_source_set_priority(wayland_event_source, GDK_PRIORITY_EVENTS + 1);
+ g_source_attach(wayland_event_source, NULL);
+
+ /* The backend is a wlroots feature which abstracts the underlying input and
+ * output hardware. The autocreate option will choose the most suitable
+ * backend based on the current environment, such as opening an X11 window
+ * if an X11 server is running. */
+ compositor->backend = wlr_backend_autocreate(compositor->wl_display, NULL);
+ if (compositor->backend == NULL) {
+ wlr_log(WLR_ERROR, "failed to create wlr_backend");
+ return;
+ }
+ /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user
+ * can also specify a renderer using the WLR_RENDERER env var.
+ * The renderer is responsible for defining the various pixel formats it
+ * supports for shared memory, this configures that for clients. */
+ compositor->renderer = wlr_renderer_autocreate(compositor->backend);
+ if (compositor->renderer == NULL) {
+ wlr_log(WLR_ERROR, "failed to create wlr_renderer");
+ return;
+ }
+
+ wlr_renderer_init_wl_display(compositor->renderer, compositor->wl_display);
+
+ /* Autocreates an allocator for us.
+ * The allocator is the bridge between the renderer and the backend. It
+ * handles the buffer creation, allowing wlroots to render onto the
+ * screen */
+ compositor->allocator =
+ wlr_allocator_autocreate(compositor->backend, compositor->renderer);
+ if (compositor->allocator == NULL) {
+ wlr_log(WLR_ERROR, "failed to create wlr_allocator");
+ return;
+ }
+
+ /* This creates some hands-off wlroots interfaces. The compositor is
+ * necessary for clients to allocate surfaces, the subcompositor allows to
+ * assign the role of subsurfaces to surfaces and the data device manager
+ * handles the clipboard. Each of these wlroots interfaces has room for you
+ * to dig your fingers in and play with their behavior if you want. Note that
+ * the clients cannot set the selection directly without compositor approval,
+ * see the handling of the request_set_selection event below.*/
+ compositor->compositor =
+ wlr_compositor_create(compositor->wl_display, 5, compositor->renderer);
+ compositor->subcompositor = wlr_subcompositor_create(compositor->wl_display);
+ wlr_data_device_manager_create(compositor->wl_display);
+
+ wlr_primary_selection_v1_device_manager_create(compositor->wl_display);
+
+ /* Creates an output layout, which a wlroots utility for working with an
+ * arrangement of screens in a physical layout. */
+ compositor->output_layout = wlr_output_layout_create();
+
+ /* Configure a listener to be notified when new outputs are available on the
+ * backend. */
+ wl_list_init(&compositor->outputs);
+ compositor->new_output.notify = new_output_notify;
+ wl_signal_add(&compositor->backend->events.new_output,
+ &compositor->new_output);
+
+ /* Create a scene graph. This is a wlroots abstraction that handles all
+ * rendering and damage tracking. All the compositor author needs to do
+ * is add things that should be rendered to the scene graph at the proper
+ * positions and then call wlr_scene_output_commit() to render a frame if
+ * necessary.
+ */
+ compositor->scene = wlr_scene_create();
+ wlr_scene_attach_output_layout(compositor->scene, compositor->output_layout);
+
+ compositor->idle_notifier =
+ wlr_idle_notifier_v1_create(compositor->wl_display);
+
+ /* Set up xdg-shell version 3. The xdg-shell is a Wayland protocol which is
+ * used for application windows. For more detail on shells, refer to Drew
+ * DeVault's article:
+ *
+ * https://drewdevault.com/2018/07/29/Wayland-shells.html
+ */
+ init_xdg_shell(compositor);
+
+ init_xwayland(compositor);
+
+ init_xdg_decoration(compositor);
+ init_layer_shell(compositor);
+
+ /*
+ * Configures a seat, which is a single "seat" at which a user sits and
+ * operates the computer. This conceptually includes up to one keyboard,
+ * pointer, touch, and drawing tablet device. We also rig up a listener to
+ * let us know when new input devices are available on the backend.
+ */
+ compositor->seat = seatCreate(compositor);
+
+ /*
+ * Creates a cursor, which is a wlroots utility for tracking the cursor
+ * image shown on screen.
+ */
+ compositor->cursor = cursor_create(compositor);
+
+ /* Add a Unix socket to the Wayland display. */
+ const char *socket = wl_display_add_socket_auto(compositor->wl_display);
+ if (!socket) {
+ wlr_backend_destroy(compositor->backend);
+ return;
+ }
+
+ /* Start the backend. This will enumerate outputs and inputs, become the DRM
+ * master, etc */
+ if (!wlr_backend_start(compositor->backend)) {
+ wlr_backend_destroy(compositor->backend);
+ wl_display_destroy(compositor->wl_display);
+ return;
+ }
+
+ /* Set the WAYLAND_DISPLAY environment variable to our socket */
+ setenv("WAYLAND_DISPLAY", socket, true);
+
+ wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket);
+}
+
+xfwmWaylandCompositor *xfwmWaylandGetDefault(void) {
+ return &_xfwm_wayland_compositor;
+}
+
+gboolean terminate(xfwmWaylandCompositor *compositor) {
+ cursor_destroy(compositor->cursor);
+ wl_list_remove(&compositor->new_xdg_decoration.link); /* decoration destroy */
+ wl_display_destroy_clients(compositor->wl_display);
+ wl_display_destroy(compositor->wl_display);
+ seatDestroy(compositor->seat);
+ wlr_output_layout_destroy(compositor->output_layout);
+
+ wlr_log(WLR_INFO, "%s", _("Display destroyed"));
+
+ return true;
+}
\ No newline at end of file
diff --git a/src/wayland/server.h b/src/wayland/server.h
new file mode 100644
index 000000000..e9eb62a0f
--- /dev/null
+++ b/src/wayland/server.h
@@ -0,0 +1,123 @@
+/* Copyright (c) 2018 - 2023 adlo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _XFWM_SERVER_H
+#define _XFWM_SERVER_H
+
+#define _POSIX_C_SOURCE 200112L
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#ifdef USE_NLS
+#include
+#include
+#define _ gettext
+#else
+#define _(s) (s)
+#endif
+
+#include "cursor.h"
+#include "decoration.h"
+#include "layer_shell.h"
+#include "output.h"
+#include "seat.h"
+
+typedef struct _Client Client;
+
+struct _xfwmWaylandCompositor {
+ struct wl_display *wl_display;
+ struct wlr_backend *backend;
+ struct wlr_renderer *renderer;
+ struct wlr_allocator *allocator;
+ struct wlr_compositor *compositor;
+ struct wlr_subcompositor *subcompositor;
+ struct wlr_scene *scene;
+
+ struct wlr_xdg_shell *xdg_shell;
+ struct wl_listener new_xdg_surface;
+ struct wl_list views;
+
+ xfwmCursor *cursor;
+ struct wlr_xcursor_manager *cursor_mgr;
+ struct wl_listener cursor_motion;
+ struct wl_listener cursor_motion_absolute;
+ struct wl_listener cursor_button;
+ struct wl_listener cursor_axis;
+ struct wl_listener cursor_frame;
+
+ Seat *seat;
+ struct wl_listener new_input;
+ struct wl_listener request_cursor;
+ struct wl_listener request_set_selection;
+ struct wl_list keyboards;
+ Client *grabbed_view;
+ double grab_x, grab_y;
+ struct wlr_box grab_geobox;
+ uint32_t resize_edges;
+
+ struct wlr_layer_shell_v1 *layer_shell;
+ struct wl_listener new_layer_surface;
+
+ struct wlr_idle_notifier_v1 *idle_notifier;
+
+ struct wlr_xwayland *xwayland;
+ struct wl_listener new_xwayland_surface;
+ struct wl_listener xwayland_ready;
+
+ struct wl_listener new_xdg_decoration;
+
+ struct wlr_output_layout *output_layout;
+ struct wl_list outputs;
+ struct wl_listener new_output;
+};
+
+typedef struct _xfwmWaylandCompositor xfwmWaylandCompositor;
+
+void xfwmWaylandInit(void);
+xfwmWaylandCompositor *xfwmWaylandGetDefault(void);
+gboolean terminate(xfwmWaylandCompositor *server);
+
+#endif /* server.h */
\ No newline at end of file
diff --git a/src/wayland/xdg_shell.c b/src/wayland/xdg_shell.c
new file mode 100644
index 000000000..d2efc078b
--- /dev/null
+++ b/src/wayland/xdg_shell.c
@@ -0,0 +1,439 @@
+/* Copyright (c) 2018 - 2023 adlo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "xdg_shell.h"
+#include "client.h"
+#include "display.h"
+#include "stacking.h"
+#include "wayland/server.h"
+
+Client *get_view_at(xfwmWaylandCompositor *server, double lx, double ly,
+ struct wlr_surface **surface, double *sx, double *sy) {
+ /* This returns the topmost node in the scene at the given layout coords.
+ * we only care about surface nodes as we are specifically looking for a
+ * surface in the surface tree of a Client. */
+ struct wlr_scene_node *node =
+ wlr_scene_node_at(&server->scene->tree.node, lx, ly, sx, sy);
+ if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) {
+ return NULL;
+ }
+ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
+ struct wlr_scene_surface *scene_surface =
+ wlr_scene_surface_try_from_buffer(scene_buffer);
+ if (!scene_surface) {
+ return NULL;
+ }
+
+ *surface = scene_surface->surface;
+ /* Find the node corresponding to the Client at the root of this
+ * surface tree, it is the only one for which we set the data field. */
+ struct wlr_scene_tree *tree = node->parent;
+ while (tree != NULL && tree->node.data == NULL) {
+ tree = tree->node.parent;
+ }
+ return tree->node.data;
+}
+
+void focus_view(Client *c, struct wlr_surface *surface) {
+ /* Note: this function only deals with keyboard focus. */
+ if (c == NULL || surface == NULL) {
+ return;
+ }
+ struct wlr_xwayland_surface *xway_surface =
+ wlr_xwayland_surface_try_from_wlr_surface(surface);
+ struct wlr_xdg_surface *xdg_surface =
+ wlr_xdg_surface_try_from_wlr_surface(surface);
+ if (!(xway_surface || xdg_surface)) {
+ return;
+ }
+
+ if (xdg_surface)
+ wlr_log(WLR_INFO, "%s: %s", _("Keyboard focus is now on surface"),
+ xdg_surface->toplevel->app_id);
+
+ xfwmWaylandCompositor *server = c->server;
+ struct wlr_seat *seat = server->seat->seat;
+ struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface;
+ if (prev_surface == surface) {
+ /* Don't re-focus an already focused surface. */
+ return;
+ }
+ if (prev_surface && xdg_surface) {
+ /*
+ * Deactivate the previously focused surface. This lets the client know
+ * it no longer has focus and the client will repaint accordingly, e.g.
+ * stop displaying a caret.
+ */
+ struct wlr_xdg_surface *previous = xdg_surface;
+ wlr_xdg_toplevel_set_activated(previous->toplevel, false);
+ } else if (prev_surface && xway_surface) {
+ struct wlr_xwayland_surface *previous = xway_surface;
+ wlr_xwayland_surface_activate(previous, false);
+ }
+ /* Move the view to the front */
+ clientRaise(c, None);
+ if (xdg_surface) {
+ /* Activate the new surface */
+ wlr_xdg_toplevel_set_activated(c->xdg_toplevel, true);
+ /*
+ * Tell the seat to have the keyboard enter this surface. wlroots will keep
+ * track of this and automatically send key events to the appropriate
+ * clients without additional work on your part.
+ */
+ seat_focus_surface(server->seat, c->xdg_toplevel->base->surface);
+ } else if (xway_surface) {
+ wlr_xwayland_surface_activate(c->xwayland_surface, true);
+ seat_focus_surface(server->seat, c->xwayland_surface->surface);
+ }
+}
+
+struct wlr_output *get_active_output(Client *c) {
+ double closest_x, closest_y;
+ struct wlr_output *output = NULL;
+ wlr_output_layout_closest_point(c->server->output_layout, output,
+ c->x + c->width / 2, c->y + c->height / 2,
+ &closest_x, &closest_y);
+ return wlr_output_layout_output_at(c->server->output_layout, closest_x,
+ closest_y);
+}
+
+static GdkRectangle get_usable_area(Client *c) {
+ struct wlr_output *output = get_active_output(c);
+ GdkRectangle usable_area = {0};
+ wlr_output_effective_resolution(output, &usable_area.width,
+ &usable_area.height);
+ return usable_area;
+}
+
+static void xdg_toplevel_map(struct wl_listener *listener, void *data) {
+ /* Called when the surface is mapped, or ready to display on-screen. */
+ Client *c = wl_container_of(listener, c, map);
+
+ if (c->xdg_toplevel->base->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL)
+ return;
+
+ struct wlr_box geo_box = {0};
+ GdkRectangle usable_area = get_usable_area(c);
+ wlr_xdg_surface_get_geometry(c->xdg_toplevel->base, &geo_box);
+
+ c->height = MIN(geo_box.height, usable_area.height);
+ c->width = MIN(geo_box.width, usable_area.width);
+ c->x = 0;
+ c->y = 0;
+
+ wlr_xdg_toplevel_set_size(c->xdg_toplevel, c->width, c->height);
+
+ focus_view(c, c->xdg_toplevel->base->surface);
+
+ wlr_scene_node_set_position(&c->scene_tree->node, c->x, c->y);
+}
+
+static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) {
+ /* Called when the surface is unmapped, and should no longer be shown. */
+ Client *c = wl_container_of(listener, c, unmap);
+
+ if (c->xdg_toplevel->base->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL)
+ return;
+
+ reset_cursor_mode(c->server);
+}
+
+static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) {
+ /* Called when the surface is destroyed and should never be shown again. */
+ Client *c = wl_container_of(listener, c, destroy);
+
+ wl_list_remove(&c->map.link);
+ wl_list_remove(&c->unmap.link);
+ wl_list_remove(&c->destroy.link);
+ wl_list_remove(&c->new_popup.link);
+
+ if (c->xdg_toplevel->base->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
+ wl_list_remove(&c->request_fullscreen.link);
+ wl_list_remove(&c->request_minimize.link);
+ wl_list_remove(&c->request_maximize.link);
+ wl_list_remove(&c->request_move.link);
+ wl_list_remove(&c->request_resize.link);
+ }
+
+ clientUnframe(c, FALSE);
+}
+
+static void xdg_toplevel_request_maximize(struct wl_listener *listener,
+ void *data) {
+ /* This event is raised when a client would like to maximize itself,
+ * typically because the user clicked on the maximize button on
+ * client-side decorations.
+ */
+ Client *c = wl_container_of(listener, c, request_maximize);
+ GdkRectangle usable_area = get_usable_area(c);
+
+ bool is_maximized = c->xdg_toplevel->current.maximized;
+ if (!is_maximized) {
+ c->saved_geometry.x = c->x;
+ c->saved_geometry.y = c->y;
+ c->saved_geometry.width = c->width;
+ c->saved_geometry.height = c->height;
+
+ c->x = 0;
+ c->y = 0;
+ } else {
+ usable_area = c->saved_geometry;
+ c->x = c->saved_geometry.x;
+ c->y = c->saved_geometry.y;
+ }
+ wlr_xdg_toplevel_set_size(c->xdg_toplevel, usable_area.width,
+ usable_area.height);
+ wlr_xdg_toplevel_set_maximized(c->xdg_toplevel, !is_maximized);
+ wlr_scene_node_set_position(&c->scene_tree->node, c->x, c->y);
+}
+
+static void xdg_toplevel_request_minimize(struct wl_listener *listener,
+ void *data) {
+ Client *c = wl_container_of(listener, c, request_minimize);
+ bool minimize_requested = c->xdg_toplevel->requested.minimized;
+ if (minimize_requested) {
+ c->saved_geometry.x = c->x;
+ c->saved_geometry.y = c->y;
+ c->saved_geometry.width = c->width;
+ c->saved_geometry.height = c->height;
+ c->y = -c->height;
+ } else {
+ c->x = c->saved_geometry.x;
+ c->y = c->saved_geometry.y;
+ c->width = c->saved_geometry.width;
+ c->height = c->saved_geometry.height;
+ }
+
+ wlr_scene_node_set_position(&c->scene_tree->node, c->x, c->y);
+}
+
+static void xdg_toplevel_request_fullscreen(struct wl_listener *listener,
+ void *data) {
+ Client *c = wl_container_of(listener, c, request_fullscreen);
+ wlr_xdg_surface_schedule_configure(c->xdg_toplevel->base);
+}
+
+static void begin_interactive(Client *c, enum CursorMode mode, uint32_t edges) {
+ /* This function sets up an interactive move or resize operation, where the
+ * compositor stops propegating pointer events to clients and instead
+ * consumes them itself, to move or resize windows. */
+ xfwmWaylandCompositor *server = c->server;
+ struct wlr_surface *focused_surface =
+ server->seat->seat->pointer_state.focused_surface;
+ if (focused_surface && c->xdg_toplevel->base->surface !=
+ wlr_surface_get_root_surface(focused_surface)) {
+ /* Deny move/resize requests from unfocused clients. */
+ return;
+ }
+ server->grabbed_view = c;
+ server->cursor->cursor_mode = mode;
+
+ if (mode == XFWM_CURSOR_MOVE) {
+ server->grab_x = server->cursor->cursor->x - c->x;
+ server->grab_y = server->cursor->cursor->y - c->y;
+ } else {
+ struct wlr_box geo_box;
+ wlr_xdg_surface_get_geometry(c->xdg_toplevel->base, &geo_box);
+
+ double border_x =
+ (c->x + geo_box.x) + ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0);
+ double border_y =
+ (c->y + geo_box.y) + ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0);
+ server->grab_x = server->cursor->cursor->x - border_x;
+ server->grab_y = server->cursor->cursor->y - border_y;
+
+ server->grab_geobox = geo_box;
+ server->grab_geobox.x += c->x;
+ server->grab_geobox.y += c->y;
+
+ server->resize_edges = edges;
+ }
+}
+
+static void xdg_toplevel_request_move(struct wl_listener *listener,
+ void *data) {
+ /* This event is raised when a client would like to begin an interactive
+ * move, typically because the user clicked on their client-side
+ * decorations. Note that a more sophisticated compositor should check the
+ * provided serial against a list of button press serials sent to this
+ * client, to prevent the client from requesting this whenever they want. */
+ Client *c = wl_container_of(listener, c, request_move);
+ begin_interactive(c, XFWM_CURSOR_MOVE, 0);
+}
+
+static void xdg_toplevel_request_resize(struct wl_listener *listener,
+ void *data) {
+ /* This event is raised when a client would like to begin an interactive
+ * resize, typically because the user clicked on their client-side
+ * decorations. Note that a more sophisticated compositor should check the
+ * provided serial against a list of button press serials sent to this
+ * client, to prevent the client from requesting this whenever they want. */
+ struct wlr_xdg_toplevel_resize_event *event = data;
+ Client *c = wl_container_of(listener, c, request_resize);
+ begin_interactive(c, XFWM_CURSOR_RESIZE, event->edges);
+}
+
+static void handle_new_popup(struct wl_listener *listener, void *data) {
+ struct wlr_xdg_popup *popup = data;
+ Client *c = wl_container_of(listener, c, new_popup);
+
+ struct wlr_output *wlr_output = wlr_output_layout_output_at(
+ c->server->output_layout, c->x + popup->current.geometry.x,
+ c->y + popup->current.geometry.y);
+
+ if (!wlr_output)
+ return;
+ Output *output = wlr_output->data;
+
+ int top_margin = 0;
+ struct wlr_box output_toplevel_box = {
+ .x = output->geometry.x - c->x,
+ .y = output->geometry.y - c->y,
+ .width = output->geometry.width,
+ .height = output->geometry.height - top_margin,
+ };
+ wlr_xdg_popup_unconstrain_from_box(popup, &output_toplevel_box);
+}
+
+Client *clientFrameWayland(DisplayInfo *display_info,
+ struct wlr_xdg_surface *xdg_surface,
+ gboolean recapture) {
+ ScreenInfo *screen_info;
+ XWindowAttributes attrs;
+ Client *c = NULL;
+
+ myDisplayGrabServer(display_info);
+ myDisplayErrorTrapPush(display_info);
+
+ if (!display_info) {
+ goto out;
+ }
+
+ screen_info = myDisplayGetDefaultScreen(display_info);
+
+ if (!screen_info) {
+ goto out;
+ }
+
+ attrs.x = 0;
+ attrs.y = 0;
+ attrs.width = 0;
+ attrs.height = 0;
+ attrs.border_width = 0;
+ attrs.depth = 24;
+ attrs.visual = NULL;
+ attrs.root = screen_info->xroot;
+ attrs.class = InputOutput;
+ attrs.bit_gravity = NorthWestGravity;
+ attrs.win_gravity = NorthWestGravity;
+ attrs.backing_store = 0;
+ attrs.backing_planes = ~0;
+ attrs.backing_pixel = 0;
+ attrs.save_under = 0;
+ attrs.colormap = 0;
+ attrs.map_installed = 1;
+ attrs.map_state = IsUnmapped;
+ attrs.all_event_masks = ~0;
+ attrs.your_event_mask = 0;
+ attrs.do_not_propagate_mask = 0;
+ attrs.override_redirect = 0;
+ attrs.screen = screen_info->xscreen;
+
+ c = _clientFrame(display_info, screen_info, XFWM_CLIENT_TYPE_WAYLAND,
+ xdg_surface, None, &attrs, FALSE);
+
+out:
+ /* Window is reparented now, so we can safely release the grab
+ * on the server
+ */
+ myDisplayErrorTrapPopIgnored(display_info);
+ myDisplayUngrabServer(display_info);
+
+ return c;
+}
+
+static void handle_new_xdg_surface(struct wl_listener *listener, void *data) {
+ /* This event is raised when wlr_xdg_shell receives a new xdg surface from a
+ * client, either a toplevel (application window) or popup. */
+ xfwmWaylandCompositor *server =
+ wl_container_of(listener, server, new_xdg_surface);
+ struct wlr_xdg_surface *xdg_surface = data;
+ DisplayInfo *display_info = myDisplayGetDefault();
+
+ /* We must add xdg popups to the scene graph so they get rendered. The
+ * wlroots scene graph provides a helper for this, but to use it we must
+ * provide the proper parent scene node of the xdg popup. To enable this,
+ * we always set the user data field of xdg_surfaces to the corresponding
+ * scene node. */
+ if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
+ struct wlr_xdg_surface *parent =
+ wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent);
+ if (parent) {
+ struct wlr_scene_tree *parent_tree = parent->data;
+ xdg_surface->data =
+ wlr_scene_xdg_surface_create(parent_tree, xdg_surface);
+ }
+ return;
+ }
+ if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE) {
+ return;
+ }
+
+ /* Allocate a Client for this surface */
+ Client *c = clientFrameWayland(display_info, xdg_surface, FALSE);
+ if (!c) {
+ return;
+ }
+ c->server = server;
+
+ /* Listen to the various events it can emit */
+ c->map.notify = xdg_toplevel_map;
+ wl_signal_add(&xdg_surface->surface->events.map, &c->map);
+ c->unmap.notify = xdg_toplevel_unmap;
+ wl_signal_add(&xdg_surface->surface->events.unmap, &c->unmap);
+ c->destroy.notify = xdg_toplevel_destroy;
+ wl_signal_add(&xdg_surface->events.destroy, &c->destroy);
+ c->new_popup.notify = handle_new_popup;
+ wl_signal_add(&xdg_surface->events.new_popup, &c->new_popup);
+
+ if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
+ c->scene_tree = wlr_scene_xdg_surface_create(&c->server->scene->tree,
+ c->xdg_toplevel->base);
+ c->scene_tree->node.data = c;
+ xdg_surface->data = c->scene_tree;
+
+ /* cotd */
+ struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel;
+ c->request_move.notify = xdg_toplevel_request_move;
+ wl_signal_add(&toplevel->events.request_move, &c->request_move);
+ c->request_resize.notify = xdg_toplevel_request_resize;
+ wl_signal_add(&toplevel->events.request_resize, &c->request_resize);
+ c->request_maximize.notify = xdg_toplevel_request_maximize;
+ wl_signal_add(&toplevel->events.request_maximize, &c->request_maximize);
+ c->request_minimize.notify = xdg_toplevel_request_minimize;
+ wl_signal_add(&toplevel->events.request_minimize, &c->request_minimize);
+ c->request_fullscreen.notify = xdg_toplevel_request_fullscreen;
+ wl_signal_add(&toplevel->events.request_fullscreen, &c->request_fullscreen);
+ }
+}
+
+void init_xdg_shell(xfwmWaylandCompositor *server) {
+ /* Set up xdg-shell version 3.*/
+ server->xdg_shell = wlr_xdg_shell_create(server->wl_display, 3);
+ server->new_xdg_surface.notify = handle_new_xdg_surface;
+ wl_signal_add(&server->xdg_shell->events.new_surface,
+ &server->new_xdg_surface);
+}
\ No newline at end of file
diff --git a/src/wayland/xdg_shell.h b/src/wayland/xdg_shell.h
new file mode 100644
index 000000000..904119853
--- /dev/null
+++ b/src/wayland/xdg_shell.h
@@ -0,0 +1,14 @@
+#ifndef _XFWM_XDG_SHELL_H
+#define _XFWM_XDG_SHELL_H
+
+#include "server.h"
+
+typedef struct _Client Client;
+
+void init_xdg_shell(xfwmWaylandCompositor *server);
+void focus_view(Client *view, struct wlr_surface *surface);
+struct wlr_output *get_active_output(Client *view);
+Client *get_view_at(
+ xfwmWaylandCompositor *server, double lx, double ly,
+ struct wlr_surface **surface, double *sx, double *sy);
+#endif
diff --git a/src/wayland/xwayland.c b/src/wayland/xwayland.c
new file mode 100644
index 000000000..62084b162
--- /dev/null
+++ b/src/wayland/xwayland.c
@@ -0,0 +1,93 @@
+/* Copyright (c) 2018 - 2023 adlo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "xwayland.h"
+#include "client.h"
+
+static void xwayland_toplevel_destroy(struct wl_listener *listener,
+ void *data) {
+ Client *view = wl_container_of(listener, view, destroy);
+
+ wl_list_remove(&view->map.link);
+ wl_list_remove(&view->destroy.link);
+
+ clientUnframe(view, FALSE);
+}
+
+static void xwayland_toplevel_map(struct wl_listener *listener, void *data) {
+ Client *view = wl_container_of(listener, view, map);
+
+ view->scene_tree = wlr_scene_subsurface_tree_create(
+ &view->server->scene->tree, view->xwayland_surface->surface);
+
+ view->scene_tree->node.data = view;
+}
+
+void handle_new_xwayland_surface(struct wl_listener *listener, void *data) {
+ xfwmWaylandCompositor *server =
+ wl_container_of(listener, server, new_xwayland_surface);
+ struct wlr_xwayland_surface *xsurface = data;
+ DisplayInfo *display_info = myDisplayGetDefault();
+
+ if ((Window)xsurface->window_id == None) {
+ return;
+ }
+
+ Client *c = clientFrameX11(display_info, (Window)xsurface->window_id, FALSE);
+
+ if (!c) {
+ return;
+ }
+
+ c->server = server;
+ c->xwayland_surface = xsurface;
+
+ c->destroy.notify = xwayland_toplevel_destroy;
+ wl_signal_add(&xsurface->events.destroy, &c->destroy);
+ c->map.notify = xwayland_toplevel_map;
+ wl_signal_add(&xsurface->surface->events.map, &c->map);
+}
+
+void handle_xwayland_ready(struct wl_listener *listener, void *data) {
+ xfwmWaylandCompositor *compositor =
+ wl_container_of(listener, compositor, xwayland_ready);
+
+ /* At this point xwayland is all setup to start accepting
+ * connections so we can quit the transient initialization mainloop
+ * and unblock xfwmWaylandInit() to continue initializing xfwm4. */
+ gtk_main_quit();
+}
+
+void init_xwayland(xfwmWaylandCompositor *server) {
+ server->xwayland =
+ wlr_xwayland_create(server->wl_display, server->compositor, false);
+ if (!server->xwayland) {
+ wlr_log(WLR_ERROR, "Failed to start Xwayland\n");
+ unsetenv("DISPLAY");
+ } else {
+ server->new_xwayland_surface.notify = handle_new_xwayland_surface;
+ wl_signal_add(&server->xwayland->events.new_surface,
+ &server->new_xwayland_surface);
+ server->xwayland_ready.notify = handle_xwayland_ready;
+ wl_signal_add(&server->xwayland->events.ready, &server->xwayland_ready);
+
+ setenv("DISPLAY", server->xwayland->display_name, true);
+ }
+
+ /* We need to run a mainloop until xwayland is ready to
+ * start accepting connections */
+ gtk_main();
+}
diff --git a/src/wayland/xwayland.h b/src/wayland/xwayland.h
new file mode 100644
index 000000000..196703518
--- /dev/null
+++ b/src/wayland/xwayland.h
@@ -0,0 +1,10 @@
+#ifndef _XFWM_XWAYLAND_H
+#define _XFWM_XWAYLAND_H
+
+#include "server.h"
+
+typedef struct _Client Client;
+
+void init_xwayland (xfwmWaylandCompositor *server);
+
+#endif