Compare commits

...

246 commits
v0.1 ... master

Author SHA1 Message Date
Simon Ser
f9626f7951 build: bump version to 0.2.1
Some checks failed
Continuous integration build / compile (clang, alpine:edge, disabled) (push) Has been cancelled
Continuous integration build / compile (clang, alpine:edge, enabled) (push) Has been cancelled
Continuous integration build / compile (clang, archlinux:base-devel, disabled) (push) Has been cancelled
Continuous integration build / compile (clang, archlinux:base-devel, enabled) (push) Has been cancelled
Continuous integration build / compile (gcc, alpine:edge, disabled) (push) Has been cancelled
Continuous integration build / compile (gcc, alpine:edge, enabled) (push) Has been cancelled
Continuous integration build / compile (gcc, archlinux:base-devel, disabled) (push) Has been cancelled
Continuous integration build / compile (gcc, archlinux:base-devel, enabled) (push) Has been cancelled
Continuous integration build / format (push) Has been cancelled
Continuous integration build / scan-build (push) Has been cancelled
2025-10-01 17:46:45 +02:00
Simon Ser
2e593fe5a8 Fix renderer, allocator and scene memory leaks on exit
Some checks failed
Continuous integration build / compile (clang, alpine:edge, disabled) (push) Has been cancelled
Continuous integration build / compile (clang, alpine:edge, enabled) (push) Has been cancelled
Continuous integration build / compile (clang, archlinux:base-devel, disabled) (push) Has been cancelled
Continuous integration build / compile (clang, archlinux:base-devel, enabled) (push) Has been cancelled
Continuous integration build / compile (gcc, alpine:edge, disabled) (push) Has been cancelled
Continuous integration build / compile (gcc, alpine:edge, enabled) (push) Has been cancelled
Continuous integration build / compile (gcc, archlinux:base-devel, disabled) (push) Has been cancelled
Continuous integration build / compile (gcc, archlinux:base-devel, enabled) (push) Has been cancelled
Continuous integration build / format (push) Has been cancelled
Continuous integration build / scan-build (push) Has been cancelled
2025-07-02 09:09:20 +02:00
Simon Ser
3da3ec0c27 output: use backend commits
Some checks failed
Continuous integration build / compile (clang, alpine:edge, disabled) (push) Has been cancelled
Continuous integration build / compile (clang, alpine:edge, enabled) (push) Has been cancelled
Continuous integration build / compile (clang, archlinux:base-devel, disabled) (push) Has been cancelled
Continuous integration build / compile (clang, archlinux:base-devel, enabled) (push) Has been cancelled
Continuous integration build / compile (gcc, alpine:edge, disabled) (push) Has been cancelled
Continuous integration build / compile (gcc, alpine:edge, enabled) (push) Has been cancelled
Continuous integration build / compile (gcc, archlinux:base-devel, disabled) (push) Has been cancelled
Continuous integration build / compile (gcc, archlinux:base-devel, enabled) (push) Has been cancelled
Continuous integration build / format (push) Has been cancelled
Continuous integration build / scan-build (push) Has been cancelled
Pass the whole new desired state to the backend, so that the
backend can leverage KMS atomic commits.
2025-06-20 19:19:25 +02:00
Simon Ser
c311ee5cdc ci: set --wrap-mode=nodownload
Some checks failed
Continuous integration build / format (push) Has been cancelled
Continuous integration build / compile (clang, alpine:edge, disabled) (push) Has been cancelled
Continuous integration build / compile (clang, alpine:edge, enabled) (push) Has been cancelled
Continuous integration build / compile (clang, archlinux:base-devel, disabled) (push) Has been cancelled
Continuous integration build / compile (clang, archlinux:base-devel, enabled) (push) Has been cancelled
Continuous integration build / compile (gcc, alpine:edge, disabled) (push) Has been cancelled
Continuous integration build / compile (gcc, alpine:edge, enabled) (push) Has been cancelled
Continuous integration build / compile (gcc, archlinux:base-devel, disabled) (push) Has been cancelled
Continuous integration build / compile (gcc, archlinux:base-devel, enabled) (push) Has been cancelled
Continuous integration build / scan-build (push) Has been cancelled
Make sure Meson doesn't automagically download and build
dependencies, so that we use system libraries.
2025-06-12 18:03:53 +02:00
Simon Ser
e8eec54129 ci: install libdisplay-info
This dependency was missing.
2025-06-12 16:04:05 +02:00
Simon Ser
63f186632e xdg_shell: bump to v5
Some checks failed
Continuous integration build / compile (clang, alpine:edge, disabled) (push) Has been cancelled
Continuous integration build / compile (clang, alpine:edge, enabled) (push) Has been cancelled
Continuous integration build / compile (clang, archlinux:base-devel, disabled) (push) Has been cancelled
Continuous integration build / compile (clang, archlinux:base-devel, enabled) (push) Has been cancelled
Continuous integration build / compile (gcc, alpine:edge, disabled) (push) Has been cancelled
Continuous integration build / compile (gcc, alpine:edge, enabled) (push) Has been cancelled
Continuous integration build / compile (gcc, archlinux:base-devel, disabled) (push) Has been cancelled
Continuous integration build / compile (gcc, archlinux:base-devel, enabled) (push) Has been cancelled
Continuous integration build / format (push) Has been cancelled
Continuous integration build / scan-build (push) Has been cancelled
v5 adds the wm_capabilities event. cage only supports fullscreen.
2025-06-03 23:22:53 +02:00
Simon Ser
60d41fb93c xdg_shell: handle xdg_popup reposition signal
We are advertising xdg-shell v4, but forgot to handle this signal.
2025-06-03 23:22:35 +02:00
Simon Ser
119f98f2b8 xdg_shell: rename xdg_toplevel signal handlers
Some checks failed
Continuous integration build / compile (clang, archlinux:base-devel, disabled) (push) Has been cancelled
Continuous integration build / compile (clang, alpine:edge, disabled) (push) Has been cancelled
Continuous integration build / compile (clang, alpine:edge, enabled) (push) Has been cancelled
Continuous integration build / compile (clang, archlinux:base-devel, enabled) (push) Has been cancelled
Continuous integration build / compile (gcc, alpine:edge, disabled) (push) Has been cancelled
Continuous integration build / compile (gcc, alpine:edge, enabled) (push) Has been cancelled
Continuous integration build / compile (gcc, archlinux:base-devel, disabled) (push) Has been cancelled
Continuous integration build / compile (gcc, archlinux:base-devel, enabled) (push) Has been cancelled
Continuous integration build / format (push) Has been cancelled
Continuous integration build / scan-build (push) Has been cancelled
These handlers aren't invoked for all types of xdg_shell surfaces:
they are only invoked for xdg_toplevel, and are not for xdg_popup.
Rename them accordingly.
2025-06-01 15:08:50 +02:00
Simon Ser
9ad44e4f52 Upgrade wlroots to v0.19
Some checks failed
Continuous integration build / compile (clang, alpine:edge, disabled) (push) Has been cancelled
Continuous integration build / compile (clang, alpine:edge, enabled) (push) Has been cancelled
Continuous integration build / compile (clang, archlinux:base-devel, disabled) (push) Has been cancelled
Continuous integration build / compile (clang, archlinux:base-devel, enabled) (push) Has been cancelled
Continuous integration build / scan-build (push) Has been cancelled
Continuous integration build / compile (gcc, alpine:edge, disabled) (push) Has been cancelled
Continuous integration build / compile (gcc, alpine:edge, enabled) (push) Has been cancelled
Continuous integration build / compile (gcc, archlinux:base-devel, disabled) (push) Has been cancelled
Continuous integration build / compile (gcc, archlinux:base-devel, enabled) (push) Has been cancelled
Continuous integration build / format (push) Has been cancelled
2025-05-26 23:09:43 +02:00
Simon Ser
6efb3b5042 cage: remove global server listeners on shutdown 2025-04-15 15:26:03 +02:00
Simon Ser
e21c155bcd seat: destroy keyboard groups on shutdown
These are not destroyed automatically because they are entirely
managed by the compositor.
2025-04-15 15:26:03 +02:00
Simon Ser
6b1ba34a4a output: drop unused wlr_matrix.h include
This is dropped in the next wlroots release.
2025-04-10 17:49:51 +02:00
Horror Proton
360e259ca5 Fix getopt("D") 2024-12-23 14:56:15 +01:00
Simon Ser
19157d3564 Make application arguments optional
If no application is specified, run without starting a child
process. Leave it up to the user to start applications externally
and stop cage with a signal.
2024-11-22 12:09:16 +01:00
Simon Ser
852839e59f Add CLI flag to enable debug logs
For bug reports, it's useful to ask for a debug log. However there's
no way for users to enable debug logs without recompiling. Add a
CLI flag to do so.
2024-11-22 12:03:40 +01:00
Michal Vaniš
0208f565dc
readme: add link to man page 2024-11-03 16:29:19 +01:00
Simon Ser
e128a9f251 build: bump to version 0.2.0 2024-10-06 15:24:22 +02:00
Simon Ser
34de3f7bac ci: drop libwayland C23 workaround 2024-10-02 09:15:44 +02:00
Simon Ser
f0651c7671 xdg_shell: wait for initial commit to unconstrain popups 2024-09-07 23:42:24 +02:00
Simon Ser
412c11ea91 cage: fix abort on shutdown
Workaround for [1]: register a listener for wl_display destroy and
avoid calling wl_display_terminate() after.

[1]: https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/421
2024-09-07 23:42:24 +02:00
Simon Ser
1abf7e5a4b xdg_shell: wait for initial commit to set xdg-decoration mode
Sending the xdg-decoration mode when the xdg_toplevel_decoration
object is created is incorrect: we need to wait for the initial
commit.
2024-09-07 23:42:24 +02:00
Simon Ser
fa90174607 xdg_shell: configure surface on initial commit
Instead of waiting for the surface to be mapped before sending a
configure event, do it on initial commit.

Note, wlroots 0.18 no longer sends automatic configure events on
initial commit.
2024-09-07 23:42:24 +02:00
Simon Ser
17af2f7be9 ci: workaround libwayland C23 bug 2024-09-07 23:42:24 +02:00
Simon Ser
a81cc2a8e4 seat: rename WLR_INPUT_DEVICE_TABLET_TOOL to WLR_INPUT_DEVICE_TABLET 2024-09-07 23:42:24 +02:00
Simon Ser
874a4a1e79 xdg_shell: use new lifecycle events 2024-09-07 23:42:24 +02:00
Simon Ser
7cc49d2292 cage: drop wlr_scene_set_presentation()
Scene-graph will do the right thing without this call.
2024-09-07 23:42:24 +02:00
Simon Ser
b9add8b729 cage: create backend with wl_event_loop and output layout with wl_display 2024-09-07 23:42:24 +02:00
Simon Ser
47ea6a5d68 seat: pass relative direction to wlr_seat_pointer_notify_axis() 2024-09-07 23:42:24 +02:00
Simon Ser
8714a82c24 Bump wlroots requirement to v0.18 2024-09-07 23:42:24 +02:00
Simon Ser
eaeab71ffa cage: fix SIGINT/SIGTERM handler data
server.wl_display is already a pointer.

Reported-by: Jonathan GUILLOT <jonathan@joggee.fr>
2024-08-28 12:03:10 +02:00
Simon Ser
69c5eccc50 ci: turn on --fatal-meson-warnings
Catches mistakes such as non-existing build options.
2024-08-26 18:10:26 +02:00
Jonathan GUILLOT
df508d65e7 readme: update meson commands 2024-08-22 08:35:58 +02:00
Jonathan GUILLOT
018d5bc4b9 readme: update wlroots dependency version 2024-08-22 08:35:58 +02:00
Simon Ser
d3fb99d665 build: drop xwayland option
This is unnecessary because Xwayland logic doesn't bring in new
dependencies. We can just compile in our Xwayland-related code
when wlroots has been built with Xwayland support.

See this Sway patch: https://github.com/swaywm/sway/pull/8165
2024-07-30 00:42:32 +02:00
Simon Ser
e7d8780f46 Add support for primary selection 2024-02-26 16:41:17 +01:00
Simon Ser
9d43282fa0 Add .editorconfig
Allows text editors to display files with the correct tab width.
2024-02-23 12:14:39 +01:00
Simon Ser
d07afac4ae output: fix assert when re-adding output to layout
wlr_scene_output_layout_add_output() aborts when called with an
already-added output.

To reproduce, run wlr-randr to reconfigure one of the enabled
outputs.
2024-02-23 12:14:00 +01:00
Simon Ser
767ccf9bbd output: use wlr_output_head_v1_state_apply()
No need to hand-roll this code, there is a wlroots helper for it.
Additionally, this fixes missing adaptive sync handling.
2024-02-20 11:01:41 +01:00
Supreeeme
b6f8f92585 xwayland: fix double wl_list_remove
When destroying an xwayland surface, the dissociate and destroy handlers
are called, but both of these were removing the map and unmap signal
handlers, causing a segfault when the destroy handler went to remove
them.
Fixes #309
2024-02-12 11:57:38 +01:00
Kenny Levinsen
8a009212bc output: Transition to wlr_output_state
We previously used the wlr_output's built-in pending state and
wlr_output_rollback. The modern state API is much nicer.
2024-01-26 23:45:57 +01:00
Kenny Levinsen
f0bc13bef7 output: Add scene output layout helper manually 2024-01-26 23:45:57 +01:00
Kenny Levinsen
63c0887664 output: Implement request_state event listener
wlroots backends no longer change state on their own, and instead send a
request_state event. Monitor this event and apply any state we receive.
2024-01-26 23:45:57 +01:00
Kenny Levinsen
c9d2f3afac meson: Bump minimum wlroots to 0.17 2024-01-26 23:45:57 +01:00
Kenny Levinsen
b40be06da2 ci: Upgrade wlroots to 0.17 2024-01-26 23:45:57 +01:00
Kenny Levinsen
67def26d83 ci: Fix Alpine xwayland dependency 2024-01-26 23:45:57 +01:00
Kenny Levinsen
c801544d61 meson: Convert xwayland option to feature type
wlroots as a subproject now yields the xwayland meson option to its
parent. We need to match the type for this to work. This also adds
support for auto mode, where xwayland is used if present but no warning
is given otherwise.
2024-01-26 23:45:57 +01:00
Kenny Levinsen
1ff7945ba1 Switch from wlr_idle to wlr_idle_notify_v1
The latter implemented the KDE protocol which has been dropped.
2024-01-26 23:45:57 +01:00
Kenny Levinsen
a30f2bcec1 Use new wlr_compositor_create signature 2024-01-26 23:45:57 +01:00
Kenny Levinsen
b55e40ad9d seat: New cursor/xcursor interface 2024-01-26 23:45:57 +01:00
Kenny Levinsen
2d4b7a4e23 shell: Use new map/unmap events
For xwayland we must listen on associate/dissociate to set up and tear
down the map/unmap event handlers instead of during surface
create/destroy.
2024-01-26 23:45:57 +01:00
Kenny Levinsen
4ea6a8b1a7 shell: Use new try_from surface getters 2024-01-26 23:45:57 +01:00
Kenny Levinsen
8df120dafd output: Use state field in wlr_output_event_commit 2024-01-26 23:45:57 +01:00
Kenny Levinsen
2f7ab094d4 output: Remove output mode handler
This is replaced by monitoring for WLR_OUTPUT_STATE_MODE on commit.
2024-01-26 23:45:57 +01:00
Kenny Levinsen
b51a6e950f output: wlr_scene_output_commit NULL options 2024-01-26 23:45:57 +01:00
Kenny Levinsen
b772a00df8 output: Call wlr_scene_output_create 2024-01-26 23:45:57 +01:00
Kenny Levinsen
64e2a44124 output: Remove wlr_output_damage include 2024-01-26 23:45:57 +01:00
Kenny Levinsen
624355485a Use new wlr_backend_autocreate signature
The session is now stored explicitly, with wlr_backend_get_session being
dropped.
2024-01-26 23:45:57 +01:00
Simon Ser
34eb3ec2c8 Make Xwayland optional at runtime
Closes: https://github.com/cage-kiosk/cage/issues/293
2023-12-11 17:25:08 +01:00
Moon Sungjoon
1f3e3043dd CI: Set `fail-fast' to false
This allows other jobs to continue running even if one job fails
2023-11-22 15:21:03 +01:00
Jonathan GUILLOT
d40dd3bd99 cage: remove not really used variables in server initialization 2023-11-10 14:38:28 +01:00
Jonathan GUILLOT
2ab480910e cage: add relative-pointer-unstable-v1 support 2023-11-10 14:38:28 +01:00
Jonathan GUILLOT
efbf7c035b seat: move initial cursor center to dedicated function 2023-11-10 14:38:28 +01:00
Jonathan GUILLOT
4dc3cf80b2 output: fix crash when re-enabling DRM output
The output is disabled then re-enabled using the wlr-output-management
protocol with tool such as wlr-randr.

$ wlr-randr --output HDMI-A-1 --off
$ wlr-randr --output HDMI-A-1 --on

When re-enabled the new output configuration is committed before the
output to be added to the global output layout. However,
handle_output_commit() expects the output to be already part of this
global layout and assert failed. Now, do not treat this in
handle_output_commit() and simply ensure the resulting scene output is
not NULL in handle_output_frame().
2023-09-20 00:01:35 +09:00
Jonathan GUILLOT
a769943447 output: ensure output is marked as enabled before trying to set mode
Otherwise, testing to set preferred / "best" mode will always return an
error. Consequently, output will simply be configured with last and
probably "worst" mode.
2023-09-01 18:37:53 +09:00
Jonathan GUILLOT
96ffaa340e output: do not always terminate when last output is destroyed
Only terminate if the last output was nested under the Wayland or X11
backend. If not, using DRM backend for example, terminating Cage when
unplugging the last monitor or simply turning it off does not seem to be
the right behavior.
2023-09-01 18:37:53 +09:00
Jonathan GUILLOT
7ec7e3df2b seat: add missing touch_frame handler
wl_touch::frame() is expected to be sent to client to indicate end of
touch frame event and not sending it may cause issues.
For example, Qt applications using Qt Wayland platform plugin do not
consider touch events until this end of frame to be received.
2023-08-24 10:05:12 +02:00
Jonathan GUILLOT
121e3ac8b2 cage: return exit code of primary client
Some applications indicate different shutdown conditions by returning
specific exit codes. One of these is e.g. Kodi, which returns 64 in case
the user chose "Power off" and 66 in case the user chose "Reboot".
In order to act on these exit codes, it thus makes sense in some
situations to pass them on from the primary client to the caller of
Cage.

This exit code is only returned if the primary client is the cause of
Cage terminating.

Co-authored-by: Patrick Steinhardt <ps@pks.im>
2023-08-21 14:40:05 +02:00
Jan Beich
9a4310f8b6 Chase HTTP redirect 2023-08-14 09:18:22 +02:00
Jente Hidskes Ankarberg
d519b5b529 Establish chain of trust for signed releases 2023-07-29 10:27:05 +02:00
Simon Ser
ea95a8af72 readme: update PGP key ID 2023-07-29 01:08:56 +02:00
Simon Ser
83ffc574be build: bump version to 0.1.5 2023-07-28 16:46:37 +02:00
Simon Ser
794803adc9 build: fix default xwayland option type 2023-07-28 16:42:58 +02:00
Simon Ser
8dcc901522 Log execvp() failures
Closes: https://github.com/cage-kiosk/cage/issues/255
2023-07-28 16:41:03 +02:00
Jonathan GUILLOT
b1129ca72e output: fix segfault when using -m last option
Check output list length before accessing previous or next item to avoid
segfault.
2023-07-28 15:36:34 +02:00
Jonathan GUILLOT
6f78d9d5b2 cage: add basic wlr-output-management support
Co-authored-by: Dima Krasner <dima@dimakrasner.com>
2023-07-28 15:36:34 +02:00
Jente Hidskes
27b6971b36 cage: remove support for rotating outputs
This is now handled through wlr_output_management
2023-07-28 15:36:34 +02:00
ChemicalXandco
bd5b20e1fa add wlr_virtual_keyboard_manager_v1 and wlr_virtual_pointer_manager_v1 2023-02-01 00:26:37 +01:00
Simon Ser
46f0ec1b72 readme: drop CI badge
cage currently uses GitHub CI, not SourceHut.
2023-01-03 22:30:54 +01:00
Alexander Orzechowski
5f4c43db04 CI: bump to wlroots 0.16 2022-12-17 21:56:03 +01:00
Alexander Orzechowski
a8e4e3d131 Enable single-pixel-buffer
Why not.
2022-12-17 21:56:03 +01:00
Alexander Orzechowski
491d508d75 meson: Bump wlroots version 2022-12-17 21:56:03 +01:00
Alexander Orzechowski
36c9c52d3f Port 0.16 surface header includes 2022-12-17 21:56:03 +01:00
Alexander Orzechowski
3015e0368b Give xdg_shell a version 2022-12-17 21:56:03 +01:00
Alexander Orzechowski
32c44ddb5f Port 0.16 scene changes 2022-12-17 21:56:03 +01:00
Alexander Orzechowski
979ff58a24 Port 0.16 wlr_output_layout_get_box changes
Fixes memory leaks.
2022-12-17 21:56:03 +01:00
Alexander Orzechowski
5818a32a99 xdg_shell: Port 0.16 configure changes 2022-12-17 21:56:03 +01:00
Alexander Orzechowski
ea14819153 xdg_shell: Port to 0.16 double buffered geometry 2022-12-17 21:56:03 +01:00
Alexander Orzechowski
b43c7adcd9 xdg_shell: Port 0.16 xdg_toplevel changes 2022-12-17 21:56:03 +01:00
Alexander Orzechowski
47f5daba20 seat: Port 0.16 device changes 2022-12-17 21:56:03 +01:00
Phil Jones
a57e0067c1 Explicitly create wlr_subcompositor.
Following wlroots b6f43ab2, the subcompositor must now be created
explicitly, rather than being handled by wlr_compositor_create.
2022-12-17 21:56:03 +01:00
Jan Beich
d3d4a129e7 cage: allow running as root when not using setuid bit 2022-12-14 11:46:48 +01:00
Ethan D. Twardy
a81ab701d7 Update meson.build not to fail if git rev-parse fails 2022-08-19 11:59:41 +02:00
Ethan D. Twardy
1b9d750dc2 Fix warnings in `meson build' output for deprecated functions.
Previously, the output of `meson build` would warn about the use of
some functions that have been deprecated since meson v0.56.0, and
the use of the `check` kwarg in the `run_command()` function.
Update meson.build to fix these build warnings.
2022-08-19 11:59:41 +02:00
Leonardo Hernández Hernández
46778e6d6c output: get scene_output by wlroots function 2022-03-22 16:58:24 +01:00
Jente Hidskes
f71844ab54 xdg_shell: add note why we're setting the size for fullscreen clients 2022-01-14 14:59:13 +01:00
Jente Hidskes
38f8566155 xdg_shell: check role in is_primary 2022-01-14 14:59:13 +01:00
Jente Hidskes
ee5410d3fb util: remove unneeded module 2022-01-14 14:59:13 +01:00
Jente Hidskes
d708a73aca view: require non-NULL surface in view_from_wlr_surface 2022-01-14 14:59:13 +01:00
Jente Hidskes
ba9c245393 view: implement view_from_wlr_surface in terms of wlr_surface->data pointer 2022-01-14 14:59:13 +01:00
Jente Hidskes
1ccbacf198 output: set pointer to cg_output in wlr_output->data
This will be used later in the layer shell implementation.
2022-01-14 14:59:13 +01:00
Dima Krasner
3321daef98 fall back to a lower output mode if needed (swaywm/sway@4cdc4ac) 2021-12-22 07:36:43 +01:00
Dima Krasner
f44ba0cea2 use find_library() for libm 2021-12-22 07:19:26 +01:00
Dima Krasner
0e87c2c8d8 restore direct libm dependency (fixes #222) 2021-12-22 07:09:09 +01:00
lunacb
de03250ca0 fixed typo 2021-12-21 21:56:56 +01:00
Jente Hidskes
dfba2d8f9d Remove no longer needed dependencies 2021-12-21 21:53:28 +01:00
Simon Ser
7fe306a79a Enable presentation-time
We get this for free with wlr_scene.
2021-12-21 21:53:28 +01:00
Simon Ser
36309d0c07 Enable viewporter
We get this for free with wlr_scene.
2021-12-21 21:53:28 +01:00
Simon Ser
77a3f6bb0c Use wlr_scene_output_send_frame_done 2021-12-21 21:53:28 +01:00
Simon Ser
7a7325c0ee Drop cg_view_child
This is now unused.
2021-12-21 21:53:28 +01:00
Simon Ser
79950cd691 Use wlr_scene_xdg_surface_create for popups
See https://github.com/swaywm/wlroots/pull/3298
2021-12-21 21:53:28 +01:00
Simon Ser
af610192f4 Use wlr_scene_output_for_each_surface 2021-12-21 21:53:28 +01:00
Simon Ser
5a1933f660 Delegate direct scan-out to wlr_scene
Depends on [1].

[1]: https://github.com/swaywm/wlroots/pull/3279
2021-12-21 21:53:28 +01:00
Simon Ser
e6f89ed6f1 Remove output_for_each_surface
It's used only by send_frame_done. Instead of keeping a generic
iterator, let's inline it in the only user.
2021-12-21 21:53:28 +01:00
Simon Ser
48b31afb5a Drop unused output_surface_for_each_surface 2021-12-21 21:53:28 +01:00
Simon Ser
0736d72e88 Use wlr_scene_attach_output_layout
This provides wlr_output_layout integration.

References: https://github.com/swaywm/wlroots/pull/3160
2021-12-21 21:53:28 +01:00
Simon Ser
d46e8a82dd Use scene-graph for damage tracking
References: https://github.com/swaywm/wlroots/pull/3117
2021-12-21 21:53:28 +01:00
Simon Ser
128fa90ea1 Use scene-graph helpers for sub-surfaces
Delegate sub-surface handling to wlr_scene_surface_tree_create.

References: https://github.com/swaywm/wlroots/pull/3128
2021-12-21 21:53:28 +01:00
Simon Ser
fb3dc58237 Replace view_wlr_surface_at with scene-graph 2021-12-21 21:53:28 +01:00
Simon Ser
6d60c6c464 Drop view_for_each_surface and view_for_each_popup_surface
With the scene-graph, these are no longer used.
2021-12-21 21:53:28 +01:00
Simon Ser
f544483340 Use the wlroots scene-graph API
References: https://github.com/swaywm/wlroots/pull/1966
2021-12-21 21:53:28 +01:00
Simon Ser
395189fb05 Upgrade to wlroots 0.15
- Update wlr_box includes to util/box.h: the wlroots header has
  been moved upstream.
- Subsurface fields have been moved
- Create renderer and allocator, stop using wlr_backend_get_renderer
- Initalize output rendering
2021-12-21 21:53:28 +01:00
Jente Hidskes
388d60d6b8 Tune compiler options 2021-12-19 11:12:19 +01:00
Jente Hidskes
8385b62a9b Move to GitHub actions 2021-12-19 11:12:19 +01:00
Palanix
d1367b1c46 removed focus_changed logic 2021-09-06 16:09:14 +02:00
Palanix
0ba1c40aa6 fix drag 2021-09-06 16:09:14 +02:00
Lorenz Brun
60492959a7 Set xdg_toplevel geometry for fullscreen clients 2021-09-06 15:46:09 +02:00
Simon Ser
b21f9c6322 Fix count_surface_iterator
This increments the pointer value, not the actual count.

References: https://github.com/Hjdskes/cage/issues/176
2021-08-11 12:54:49 +02:00
Simon Ser
1a3ab3eb3a build: allow using subproject for wlroots
This is handy when testing cage with a newer wlroots copy, or
when developing cage and wlroots patches in parallel.
2021-08-09 20:54:11 +02:00
Simon Ser
5424a35aba Damage drag icon when destroyed
This fixes the drag icon not disappearing when releasing the
pointer button.

Reproduction steps:

- Open gedit
- Drag some text around
- Release the pointer button
2021-08-09 20:53:24 +02:00
Simon Ser
cdb1cdf3b4 Don't manually parse XKB_* env vars
libxkbcommon will do it for us if we provide a NULL struct
xkb_rule_names.
2021-08-09 20:52:51 +02:00
Jente Hidskes
646b3e80b4 Update version to 0.1.4 2021-06-26 09:39:40 +02:00
Jan Beich
08e58c2b45 CI: bump wlroots version to 0.14.0 2021-06-26 09:25:36 +02:00
Jan Beich
636185bf9c meson: require wlroots 0.14.0 after 9a4523d47e
view.c:238:52: error: no member named 'subsurfaces_below' in 'struct wlr_surface'
        wl_list_for_each (subsurface, &view->wlr_surface->subsurfaces_below, parent_link) {
                                       ~~~~~~~~~~~~~~~~~  ^
/usr/include/wayland-util.h:443:30: note: expanded from macro 'wl_list_for_each'
        for (pos = wl_container_of((head)->next, pos, member);  \
                                    ^~~~
/usr/include/wayland-util.h:409:32: note: expanded from macro 'wl_container_of'
        (__typeof__(sample))((char *)(ptr) -                            \
                                      ^~~
2021-06-26 09:25:36 +02:00
Jan Beich
851268bedc view: chase swaywm/wlroots@9e58301df7
view.c:238:52: error: no member named 'subsurfaces' in 'struct wlr_surface'
        wl_list_for_each (subsurface, &view->wlr_surface->subsurfaces, parent_link) {
                                       ~~~~~~~~~~~~~~~~~  ^
/usr/include/wayland-util.h:443:30: note: expanded from macro 'wl_list_for_each'
        for (pos = wl_container_of((head)->next, pos, member);  \
                                    ^~~~
/usr/include/wayland-util.h:409:32: note: expanded from macro 'wl_container_of'
        (__typeof__(sample))((char *)(ptr) -                            \
                                      ^~~

Based on 3162766eef
2021-06-26 09:25:36 +02:00
Jente Hidskes
efaf76e9ab Include release scripts 2021-04-16 14:34:57 +02:00
Jente Hidskes
aa91af32a5 Update version to 0.1.3 2021-04-16 14:01:38 +02:00
Jan Beich
0891f4b814 CI: bump wlroots version to 0.13.0 2021-04-09 19:33:14 +02:00
Jan Beich
b07647fb78 CI: explicitly depend on Mesa on Arch Linux
meson.build:102:0: ERROR: Dependency "egl" not found, tried pkgconfig
2021-04-09 19:33:14 +02:00
Jan Beich
40fc92f7e7 CI: chase swaywm/wlroots@de5347d0f2
Run-time dependency xcb-icccm found: NO (tried pkgconfig and cmake)
Message: Required for Xwayland support.
2021-04-09 19:33:14 +02:00
Jan Beich
cb93785649 CI: chase swaywm/wlroots@3504bb587d + swaywm/wlroots@e8ad05913f
meson.build:70:4: ERROR: Problem encountered: Cannot build Cage with XWayland support: wlroots has been built without it
2021-04-09 19:33:14 +02:00
Jan Beich
eb18383e8e meson: require wlroots 0.13.0 after 0db62672a4
cage.c:307:59: error: too few arguments to function call, expected 2, have 1
        server.backend = wlr_backend_autocreate(server.wl_display);
                         ~~~~~~~~~~~~~~~~~~~~~~                  ^
subprojects/wlroots/include/wlr/backend.h:43:21: note: 'wlr_backend_autocreate' declared here
struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
                    ^
2021-04-09 19:33:14 +02:00
Jan Beich
a5aa497ff1 output: chase swaywm/wlroots@cc56b4f073
output.c:456:36: error: no member named 'transform' in 'struct wlr_output::(anonymous at /usr/local/include/wlr/types/wlr_output.h:154:2)'
        wl_signal_add(&wlr_output->events.transform, &output->transform);
                       ~~~~~~~~~~~~~~~~~~ ^

Based on 2c76923282
2021-04-09 19:33:14 +02:00
Jan Beich
82bad3f0fc xdg_shell: chase swaywm/wlroots@07111828c5
xdg_shell.c:230:2: warning: implicit declaration of function 'wlr_xdg_surface_for_each_popup' is invalid in C99 [-Wimplicit-function-declaration]
        wlr_xdg_surface_for_each_popup(xdg_shell_view->xdg_surface, iterator, data);
        ^
ld: error: undefined symbol: wlr_xdg_surface_for_each_popup
>>> referenced by xdg_shell.c:230 (../xdg_shell.c:230)
>>>               cage.p/xdg_shell.c.o:(for_each_popup)

Based on 5438cc158a
2021-04-09 19:33:14 +02:00
Jan Beich
90da4ee4dc cage: chase swaywm/wlroots@4b03bdc3ab
cage.c:307:61: error: too many arguments to function call, expected single argument 'display', have 2 arguments
        server.backend = wlr_backend_autocreate(server.wl_display, NULL);
                         ~~~~~~~~~~~~~~~~~~~~~~                    ^~~~
/usr/include/sys/_null.h:34:14: note: expanded from macro 'NULL'
 #define NULL    ((void *)0)
                 ^~~~~~~~~~~
/usr/local/include/wlr/backend.h:36:21: note: 'wlr_backend_autocreate' declared here
struct wlr_backend *wlr_backend_autocreate(struct wl_display *display);
                    ^

Based on 53f5197c26
2021-04-09 19:33:14 +02:00
Jan Beich
e6762725ed seat: chase swaywm/wlroots@e06c9e43af
seat.c:453:58: error: incomplete definition of type 'struct wlr_event_touch_down'
        wlr_cursor_absolute_to_layout_coords(seat->cursor, event->device, event->x, event->y, &lx, &ly);
                                                           ~~~~~^
seat.c:450:9: note: forward declaration of 'struct wlr_event_touch_down'
        struct wlr_event_touch_down *event = data;
               ^

Based on a56098a24e
2021-04-09 19:33:14 +02:00
Jan Beich
4a01da76f1 seat: chase swaywm/wlroots@7693f61d81
seat.c:263:56: error: use of undeclared identifier 'WLR_KEY_PRESSED'
        if ((modifiers & WLR_MODIFIER_ALT) && event->state == WLR_KEY_PRESSED) {
                                                              ^

Based on bb342ac5e6
2021-04-09 19:33:14 +02:00
Michael Weiss
0d85c1652e meson.build: Unify the indentation (replace all tabs) 2020-08-02 17:43:05 +02:00
Michael Weiss
7d37322906 seat.c: Make clangformat happy and simplify the control flow
This restores the original behaviour from 2cf40f7, is easier to read,
and satisfies clangformat.
This also doesn't call wlr_idle_notify_activity anymore, which was
probably unintended.
2020-08-02 17:43:05 +02:00
Jente Hidskes
d097393732
man: indent with tabs 2020-07-16 16:25:19 +02:00
Jente Hidskes
7b861bf175
Release Cage 0.1.2 2020-07-16 15:41:38 +02:00
Jente Hidskes
1037adbab7
man: document -s and fix some typos 2020-07-16 15:41:38 +02:00
Jan Beich
efb1121769 CI: bump wlroots version to 0.11.0 2020-07-16 15:25:11 +02:00
Jan Beich
99aba6b89b meson: require wlroots 0.11.0 after 6a12da1960
../output.c:235:57: error: no member named 'base' in 'struct wlr_buffer'
        wlr_output_attach_buffer(wlr_output, &surface->buffer->base);
                                              ~~~~~~~~~~~~~~~  ^
2020-07-16 15:25:11 +02:00
Jente Hidskes
0d694db6fe Invert wlr_xcursor_manager_load return value 2020-07-16 15:25:11 +02:00
Jan Beich
a6b1cf1d69 output: unbreak with wlroots 0.11.0
Port changes changes from
9d0aa0cb83
40e87fa98a
2020-07-16 15:25:11 +02:00
Jan Tatje
79992f39f3 Add option to allow changing VT
Code that does the VT switch taken from sway.
2020-07-16 14:46:28 +02:00
Jente Hidskes
bd961db6f5 cage: allow setting output mode 2020-06-26 20:56:11 +02:00
Jente Hidskes
72f6c0bae3 output: implement CAGE_MULTI_OUTPUT_MODE_LAST
In this mode, only the last connected output will be used. If that one
is unplugged, the previously last connected output will be enabled. This
for example allows one to switch between two outputs, e.g. on a handheld
device such as a mobile phone.
2020-06-26 20:56:11 +02:00
Jente Hidskes
64299054db output: add output_mode enum
This enum provides two means of behaviour for multi-output setups:
extend the display across all outputs, or only use the last one. The
former is the current (and default) behaviour; the latter will be added
in the next commit.
2020-06-26 20:56:11 +02:00
Jente Hidskes
f4b63cd6b8 output: add enable and disable functions
These will allow different patterns of multimonitor behaviour. In a
followup commit, we will introduce a behaviour where only the last
connected output is in use.
2020-06-26 20:56:11 +02:00
travankor
4e96d913fb
Add man page. (#141)
* Add man page.

* CI: add scdoc
2020-05-31 15:14:18 +02:00
travankor
6eb693c05b
Cage: drop gid before uid (#142)
If setuid is called first then the target user may not have the ability to
setgid. See https://wiki.sei.cmu.edu/confluence/display/c/POS36-C.+Observe+correct+revocation+order+while+relinquishing+privileges
2020-04-17 21:58:26 +02:00
Matthew Bauer
5d7ff9e64d
Don’t terminate display when no view is found (#132)
* Don’t terminate display when no view is found

Some apps like RetroArch will quit and fork itself to switch
modes (like from the menu to a loaded core). This means that for a
very short period of time we have no view available for Wayland. Right
now, Cage doesn’t actually exit when it does this terminate, so you
get this kind of zombie child process that is running but not showing
anything on the screen because there is no compositor.

The solution I have here is to just keep Cage’s Wayland server running
to avoid this issue. I’m open to other idea, but this seems sane to
me. Perhaps an alternative is to check if the process is still alive
when this happens, if not then we can do the quitting behavior. In
addition, we could make this an option if some users don’t want this
behavior.

* Remove "ever_been_mapped" from cg_wayland_view
2020-03-16 20:32:24 +01:00
Jente Hidskes
fc5564645e
seat: remove signals earlier in seat destroy handler (#130)
This fixes a bunch of invalid writes found with valgrind.
2020-02-27 20:39:15 +01:00
Jente Hidskes
9886efa112
seat: log around input device mapping (#128)
This should make it easier to troubleshoot why, for example, a touch
input device isn't mapped to a touch output device. See #126.
2020-02-23 16:00:57 +01:00
Jente Hidskes
27391f174a
Cage: check for sigchld_source being NULL before removing it
If something goes wrong during startup (i.e., the wlroots backend cannot
be created), we jump to the end. This then unconditionally removes the
sigchld_source, but that hasn't been set at this point yet.
2020-02-22 00:23:03 +01:00
project-repo
e96fccfa54
Implement support for wlr_keyboard_group (#123)
* Implement support for wlr_keyboard_group

* Cast pointers to void type when passing to wlr_log

* Fix bracket style

* Improve style

* Remove unnecessary function

* Prefer defined variable for brevity

* Remove unnecessary pointer printing

* Ameliorate style

* Remove duplicate functionality

* Set group repeat info

* Ameliorate style

* Fix bug introduced through renaming

* Ameliorate style in seat.h

* Remove cg_keyboard in favour of cg_keyboard_group

* Remove unused signalling

* Apply clang-format

* Fix ordering of commands

* Remove unnecessary field in cg_keyboard_group
2020-02-21 21:19:12 +01:00
Jente Hidskes
b2bcf06eee CI: add tasks that build without XWayland 2020-02-18 21:20:23 +01:00
Jente Hidskes
1e3417cd65 CI: add clang-format task 2020-02-18 21:20:23 +01:00
Jente Hidskes
6d0714bfa3 Begrudgingly format wl_list_* macros with a space before parameter list
I can't seem to configure clang-format to remove this space...
2020-02-18 21:20:23 +01:00
Jente Hidskes
6cbc202662 Apply clang-format 2020-02-18 21:20:23 +01:00
Jente Hidskes
bd48cad492 Add clang-format file
This adds a first iteration of a clang-format style definition. It
formats the current code such that it aligns (mostly..) with the style
I prefer, but it is most likely incomplete and there will be corner
cases.

Fixes #124.
2020-02-18 21:20:23 +01:00
Jente Hidskes
6fa2734651
meson: fix snapshot builds
Previously, is git was found but the build was run from a snapshot,
`version` wouldn't be set. This commit fixes this by first setting
version to the project version and only then, conditionally, setting it
to the git version.

Fixes #125.
2020-02-17 19:34:54 +01:00
Jente Hidskes
42782bda1d
server: remove backend pointer 2020-02-08 16:49:12 +01:00
Jente Hidskes
dc002acd20 cage: cleanup_primary_client when jumping to 'end'
With the new CLOEXEC setting, spawning the primary client
can fail *after* forking. In this case, the client process
has been forked and will need to be cleaned up.

In case something fails before pid has been set, it's set to 0.
From waitpid(2):

  The value of pid can be:

     0    meaning wait for any child process whose process group ID
          is equal to that of the calling process at the time of the
          call to waitpid().

That will be none in this case, and hence this won't block and is thus
safe.
2020-02-07 19:04:16 +01:00
Jente Hidskes
24cc576377 cage: set CLOEXEC on the file descriptors
As mentioned by @emersion:

By default, pipe creates FDs without the CLOEXEC flag set, which means
they will be leaked to any other child process spawned. Would be nice to
set the CLOEXEC flag to prevent the leak.
2020-02-07 19:04:16 +01:00
Jente Hidskes
009cca3fa9 cage: switch SIGCHLD handling for pipe
As explained in [1] and [2], SIGCHLD is a tricky signal to handle. A
pipe can be used to signal completion instead.

[1]: https://github.com/swaywm/wlroots/issues/2012#issuecomment-578908333
[2]: https://stackoverflow.com/questions/8976004/using-waitpid-or-sigaction/8976461#8976461
2020-02-07 19:04:16 +01:00
Jente Hidskes
6be4306b4d cage: destroy output_layout after wl_display
wl_display will destroy the outputs, whose destroy handler will remove
them from the output layout. But by that point, the output layout has
already been destroyed.
2020-02-07 19:04:16 +01:00
Jente Hidskes
b570cdb22c cage: report on exit status of primary client 2020-02-07 19:04:16 +01:00
Jente Hidskes
bb5d4cf52e cage: handle SIGCHLD of primary client
Fixes #13, #53.
2020-02-07 19:04:16 +01:00
Jente Hidskes
f33fe5d6f2 cage: change copyright to include 2020 2020-02-07 19:04:16 +01:00
Jente Hidskes
acf5925a86 cage: return 0 always in the signal handler
According to the Wayland docs:

If the event source is registered for re-check with
wl_event_source_check(): 0 for all done, 1 for needing a re-check. If
not registered, the return value is ignored and should be zero.

See e.g. http://manpages.ubuntu.com/manpages/cosmic/man3/wl_event_source.3.html

Since we don't register any of these for re-checking, we should
return 0.
2020-02-07 19:04:16 +01:00
Jente Hidskes
8069893b0d render: use output backend to get renderer
This prevents a segfault when shutting down during startup, when a
client is passed that doesn't spawn a window.

It also brings us one step closer to not having to have a pointer to the
backend in cg_server.
2020-02-07 19:04:16 +01:00
Jente Hidskes
8a6d16391f
output: include seat.h 2020-02-03 21:23:39 +01:00
Damon
e3be8159b3
output: wrap xwayland.h include in CAGE_HAS_XWAYLAND (#120)
* move xwayland.h include to ifdef block for X11

* use correct ifdef CAGE_HAS_XWAYLAND
2020-02-03 21:14:29 +01:00
Jente Hidskes
b04e02cd60 output: add support for direct scan-out
Fixes #88.
2020-02-01 23:24:49 +01:00
Jente Hidskes
e664fdce79
README: update build instructions 2020-02-01 16:17:58 +01:00
Jente Hidskes
66313efb01
CI: build with --werror 2020-01-31 22:58:15 +01:00
Jente Hidskes
33cd4c2af9
meson: don't build with -Werror by default
Fixes #70. For development, initialise meson with the `--werror` option:

```
meson build --werror
```

Alternatively, for an existing build, run

```
cd build
meson configure -Dwerror=true
```
2020-01-31 22:55:16 +01:00
Jente Hidskes
15eeb7784e cage: add -v argument to print version 2020-01-26 18:18:49 +01:00
Jente Hidskes
5e27683961 Move docs to the wiki 2020-01-26 18:18:49 +01:00
Jente Hidskes
a63c75c849 Cage: check if XDG_RUNTIME_DIR is set
Wayland requires this environment variable to be set, see
https://manpages.debian.org/experimental/libwayland-doc/wl_display_connect.3.en.html
2020-01-25 23:30:28 +01:00
Jente Hidskes
30d805d6ff CI: build against wlroots 0.10.0
There are no breaking changes otherwise, so in meson.build we can leave
the version to anything greater than 0.9.1.
2020-01-25 23:27:30 +01:00
Jente Hidskes
cc1f975c44
cage: center cursor only on initial startup
With Cage now supporting hotplugging of outputs, we shouldn't warp the
cursor to the center of every new output. Rather, we should warp it only
on the initial startup.
2020-01-18 11:57:16 +01:00
Jente Hidskes
e3f9959519 view: maximize dialog if it extends output layout
If the dialog extends the output layout in at least one dimension, then
the part of it that is larger than the output will be rendered
of-screen. This commit maximizes such dialogs instead.

Fixes #110.
2020-01-11 17:11:16 +01:00
Jente Hidskes
cfb15d20ba xwayland: use view's (x,y) coordinate when maximizing
It might happen that the (x,y) coordinate of the left-most monitor is
not 0. If it isn't, this doesn't cost us anything, so better safe than
sorry.
2020-01-11 17:11:16 +01:00
Jente Hidskes
fbff19939a README: update Copyright to include 2020 2020-01-11 17:11:16 +01:00
Jente Hidskes
12aa43abba output: initialise wlr_output before positioning views 2020-01-11 17:07:33 +01:00
Jente Hidskes
2f9442906d output: refactor get_surface_box
The function also did an intersection check, so it did more than just
getting the surface box. This refactoring makes it more clear.

This commit also fixes a bug in coordinate spaces that makes dialogs
render properly over multiple outputs.
2020-01-11 17:07:33 +01:00
Jente Hidskes
23f8d609ce output: check whether outputs are enabled 2020-01-11 17:07:33 +01:00
Jente Hidskes
a53dca91ce Render popups separately from their toplevel view when possible 2020-01-11 17:07:33 +01:00
Jente Hidskes
2db815aa23 Refactor surface damaging and rendering code
This is based on code from Sway, which is also MIT licensed hence
compatible. This makes the surface damaging and rendering code easier to
follow and makes it easier to import future changes to Sway as well.
2020-01-11 17:07:33 +01:00
Tristan Daniel
bfd7d605b8 meson: require wlroots 0.9.1
And update CI builds accordingly.
2020-01-10 19:33:30 +01:00
Tristan Daniel
84216ca2a4 output: call wlr_output_rollback if not rendering
Required by wlroots 0.9.0.
2020-01-10 19:33:30 +01:00
Tristan Daniel
51e6c760da Update new output handler for wlroots 0.9.0
Following breaking changes in wlroots 0.9.0, wlr_output_commit must be
called after wlr_output_set_mode, wlr_output_set_transform and wlr_output_enable.
Additionally, wlr_output_enable is no longer implicitly called by
wlr_output_set_mode.

Fixes #102
2020-01-06 11:49:57 +01:00
Jente Hidskes
8f6ffa1419
Use hard tabs 2020-01-05 13:42:17 +01:00
Jente Hidskes
651d579c30
seat: be explicit about output- and layout coordinates
This commit follows up on 95b7782c72
2020-01-05 13:16:01 +01:00
Jente Hidskes
530b32d496
xwayland: fix regression after 95b7782
Fixes #101
2020-01-05 12:59:26 +01:00
Jente Hidskes
1c59946e20
Rename view_damage_surface to view_damage_part
To better align it with its counterpart view_damage_whole
2019-12-29 22:54:25 +01:00
Jente Hidskes
95b7782c72
Be explicit about output- and layout coordinates 2019-12-29 16:07:14 +01:00
Jente Hidskes
21229984ff
view: use output layout to set x,y coordinates 2019-12-29 13:17:47 +01:00
Jente Hidskes
22adaae715
output: log error when failing to allocate output 2019-12-26 17:25:22 +01:00
Kenny Levinsen
4e832c6aaf Revert "Set title on outputs that the focused surface is intersecting with"
This reverts commit 915c6df244.
2019-12-26 17:14:57 +01:00
Kenny Levinsen
edfe4e07ce Avoid excessive log messages with multiple outputs 2019-12-26 17:14:57 +01:00
Kenny Levinsen
bcd0086f13 Set title on outputs that the focused surface is intersecting with 2019-12-26 17:14:57 +01:00
Kenny Levinsen
24f48966d8 Annotate wl_list link field 2019-12-26 17:14:57 +01:00
Kenny Levinsen
739ba1fe65 Check calloc return value 2019-12-26 17:14:57 +01:00
Kenny Levinsen
06ada15661 Support multiple outputs
Outputs are arranged in a horizontal layout in the order they are
created in by wlroots. Maximized xdg_shell views will span all outputs,
like the global fullscreen mode in sway.

Fixes #87
2019-12-26 17:14:57 +01:00
Jente Hidskes
0aeba8085c
Replace all wayland-server.h includes with wayland-server-core.h
The documentation for `wayland-server.h` says:

> Use of this header file is discouraged. Prefer including
> wayland-server-core.h instead, which does not include the server protocol
> header and as such only defines the library PI, excluding the deprecated API
> below.

See also
ca45f4490c (diff-b57e10fe0774258a6d21b22077001cff)
2019-12-20 17:16:53 +01:00
Jente Hidskes
abfe6a66ca
README: refer to docs/ 2019-12-20 17:05:00 +01:00
Jente Hidskes
7c6d6c75ae Implement wlr_export_dmabuf_unstable_v1 2019-12-20 17:02:16 +01:00
Jente Hidskes
90e70c6882 Document environment variables
Fixes #93.
2019-12-20 16:51:11 +01:00
Jente Hidskes
dd87c1fab5 Rename data_device_mgr to _manager 2019-12-20 12:35:41 +01:00
Jente Hidskes
65177466e7 Implement wlr-gamma-control-unstable-v1
Closes #20
2019-12-20 12:35:41 +01:00
Jente Hidskes
531cdc84b7 Fix the FreeBSD build 2019-12-18 21:51:05 +01:00
Jente Hidskes
436ad33db8 Update wlroots in CI builds to 0.8.1 2019-12-18 21:51:05 +01:00
Derek Wallace
33bb3c818c Removed destructors per wlroots PR 1915 2019-12-18 21:30:29 +01:00
Ruben Vandamme
0fb513fb85 Set startup color to black. 2019-08-27 18:14:57 +02:00
Andri Yngvason
c13ada98de Add xdg output manager 2019-08-25 18:14:01 +02:00
Andri Yngvason
da6b6c208e Add screencopy 2019-08-25 18:14:01 +02:00
Jente Hidskes
69340921a4 CI: use a version of wlroots that compiles with libinput 1.14
See https://github.com/swaywm/wlroots/issues/1796
2019-08-25 17:48:07 +02:00
Jente Hidskes
016ef340d2
README: update email address 2019-07-09 14:25:33 +02:00
Jente Hidskes
35166e4e1b
README: use www subdomain
Fixes #76
2019-07-07 16:43:20 +02:00
Jente Hidskes
1ecba7e67c
Release Cage 0.1.1
A new tag for packagers to build Cage with wlroots 0.6.0.

Changelog:

* Update to wlroots 0.6.0
* Don't submit too much damage in damage tracking implementation
* Use XKB_* environment variables to configure keyboard devices
2019-06-03 19:55:52 +02:00
Jente Hidskes
6bb73b3658
meson: require wlroots 0.6.0
Missed in 9354781f32
2019-05-03 23:33:50 +02:00
Jente Hidskes
1de6054d7e seat: use XKB_* env vars for configuring keyboard devices
Fixes #66
2019-05-03 21:32:58 +02:00
Jente Hidskes
b16fc7d780
output: use the output's preferred mode
On outputs that have modes, we need to set one before we can use it.  We
pick the preferred mode, or if this is not advertised, the last listed
mode. This mode is generally the mode with the highest resolution.

Fixes #10
2019-05-03 21:14:51 +02:00
Jente Hidskes
5af455a905
output: fix damage tracking debug mode
We want to damage the whole output in this mode. However if we overwrite
the damaged region after it's useless.

See
8b1220f5a3 (diff-95fe5e5b411cc64a5dc31e2b72669a0d)
2019-05-03 21:07:40 +02:00
Jente Hidskes
f23257d170 output: don't submit too much damage
See https://github.com/swaywm/wlroots/issues/1666#issue-436967396 and
the linked changes
https://github.com/swaywm/wlroots/pull/1668/files#diff-95fe5e5b411cc64a5dc31e2b72669a0d
2019-05-03 21:03:09 +02:00
Drew DeVault
9354781f32 Update to wlroots 0.6.0 2019-05-03 21:03:09 +02:00
Jente Hidskes
887c855a3b CI: use wlroots 0.6.0 2019-05-03 21:03:09 +02:00
Jan Beich
61894994f3
Cage: drop root on startup like Sway
wlroots may need setuid to run on DRM if built without (e)logind
support.
2019-05-01 13:38:19 +02:00
Jan Beich
3fb89563ae Switch to system epoll-shim.pc in FreeBSD build 2019-04-27 20:27:23 +02:00
Michael Vetter
c69d394bed Set version to 0.1
Since we now have a tag with 0.1 and also a release with that version
number was published on GitHub we should adjust the version in the meson
file too.
2019-04-24 21:18:12 +02:00
Jente Hidskes
2eb28ce216
README: set logo size in px 2019-04-24 21:01:50 +02:00
Jente Hidskes
fa5142e773
README: add logo and signed release note 2019-04-23 22:53:23 +02:00
31 changed files with 1847 additions and 1351 deletions

View file

@ -1,28 +0,0 @@
image: alpine/edge
packages:
- eudev-dev
- mesa-dev
- meson
- libinput-dev
- libxkbcommon-dev
- pixman-dev
- wayland-dev
- wayland-protocols
- xorg-server-xwayland
sources:
- https://github.com/swaywm/wlroots
- https://github.com/Hjdskes/cage
tasks:
# Install wlroots, which is required by Cage. Note that we compile a tagged
# version, instead of master, to avoid any breaking changes in wlroots.
- wlroots: |
cd wlroots
# This corresponds to the tag of 0.5.0
git checkout c9137cba691b57c3eaf3ff94f9bf8e623f66ccc5
meson --prefix=/usr build -Drootston=false -Dexamples=false
ninja -C build
sudo ninja -C build install
- build: |
cd cage
meson build -Dxwayland=true
ninja -C build

View file

@ -1,30 +0,0 @@
image: archlinux
packages:
- clang
- meson
- libinput
- libxkbcommon
- wayland
- wayland-protocols
- xorg-server-xwayland
sources:
- https://github.com/swaywm/wlroots
- https://github.com/Hjdskes/cage
tasks:
# Install wlroots, which is required by Cage. Note that we compile a tagged
# version, instead of master, to avoid any breaking changes in wlroots.
- wlroots: |
cd wlroots
# This corresponds to the tag of 0.5.0
git checkout c9137cba691b57c3eaf3ff94f9bf8e623f66ccc5
meson --prefix=/usr build -Drootston=false -Dexamples=false
ninja -C build
sudo ninja -C build install
- build: |
cd cage
meson build -Dxwayland=true
ninja -C build
- scan-build: |
cd cage
CC=clang meson build -Dxwayland=true
CC=clang ninja -C build scan-build

View file

@ -1,45 +0,0 @@
image: freebsd/latest
packages:
- devel/evdev-proto
- devel/meson
- devel/libepoll-shim
- devel/pkgconf
- graphics/mesa-libs
- graphics/wayland
- graphics/wayland-protocols
- x11/libinput
- x11/libxkbcommon
- x11/pixman
sources:
- https://github.com/swaywm/wlroots
- https://github.com/Hjdskes/cage
tasks:
# Taken from sway
- fixup_epoll: |
cat << 'EOF' | sudo tee /usr/local/libdata/pkgconfig/epoll-shim.pc
prefix=/usr/local
exec_prefix=\$\{\$prefix\}
libdir=${prefix}/lib
sharedlibdir=${prefix}/lib
includedir=${prefix}/include/libepoll-shim
Name: epoll-shim
Description: epoll shim implemented using kevent
Version: 0
Requires:
Libs: -L${libdir} -L${sharedlibdir} -lepoll-shim
Libs.private: -pthread -lrt
Cflags: -I${includedir}
EOF
# Install wlroots, which is required by Cage. Note that we compile a tagged
# version, instead of master, to avoid any breaking changes in wlroots.
- wlroots: |
cd wlroots
# This corresponds to the tag of 0.5.0
git checkout c9137cba691b57c3eaf3ff94f9bf8e623f66ccc5
meson --prefix=/usr/local build -Drootston=false -Dexamples=false
ninja -C build
sudo ninja -C build install
- build: |
cd cage
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig meson build -Dxwayland=true
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ninja -C build

13
.clang-format Normal file
View file

@ -0,0 +1,13 @@
AlignAfterOpenBracket: Align
AlignTrailingComments: false
AlwaysBreakAfterReturnType: TopLevelDefinitions
BreakBeforeBraces: Linux
ColumnLimit: 120
ContinuationIndentWidth: 8
ForEachMacros: [wl_list_for_each, wl_list_for_each_safe, wl_list_for_each_reverse]
IndentWidth: 8
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: true
TabWidth: 8
UseTab: Always

1
.clang-format-ignore Normal file
View file

@ -0,0 +1 @@
subprojects/**/*

10
.editorconfig Normal file
View file

@ -0,0 +1,10 @@
root = true
[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = tab
indent_size = 8
max_line_length = 120

81
.github/workflows/main.yml vendored Normal file
View file

@ -0,0 +1,81 @@
name: Continuous integration build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
env:
WLROOTS_VERSION: 0.19
jobs:
compile:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
CC: [ gcc, clang ]
OS: [ "alpine:edge", "archlinux:base-devel" ]
xwayland: [ enabled, disabled ]
container: ${{ matrix.OS }}
env:
CC: ${{ matrix.CC }}
steps:
- name: Checkout Cage
uses: actions/checkout@v2
- name: Install dependencies (Alpine)
if: "matrix.OS == 'alpine:edge'"
run: apk add build-base xcb-util-wm-dev libseat-dev clang git eudev-dev mesa-dev libdrm-dev libinput-dev libxkbcommon-dev pixman-dev wayland-dev meson wayland-protocols xwayland-dev scdoc-doc hwdata libdisplay-info-dev
- name: Install dependencies (Arch)
if: "matrix.OS == 'archlinux:base-devel'"
run: |
pacman-key --init
pacman -Syu --noconfirm xcb-util-wm seatd git clang meson libinput libdrm mesa libxkbcommon wayland wayland-protocols xorg-server-xwayland scdoc libdisplay-info
- name: Fetch wlroots as a subproject
run: git clone https://gitlab.freedesktop.org/wlroots/wlroots.git subprojects/wlroots -b $WLROOTS_VERSION
- name: Compile Cage (XWayland=${{ matrix.xwayland }})
run: |
meson --fatal-meson-warnings --wrap-mode=nodownload \
build-${{ matrix.CC }}-${{matrix.xwayland }} \
-Dwlroots:xwayland=${{ matrix.xwayland }}
ninja -C build-${{ matrix.CC }}-${{matrix.xwayland }}
format:
runs-on: ubuntu-latest
container: "archlinux:base-devel"
steps:
- name: Checkout Cage
uses: actions/checkout@v2
- name: Install dependencies
run: |
pacman-key --init
pacman -Syu --noconfirm xcb-util-wm seatd git clang meson libinput libdrm mesa libxkbcommon wayland wayland-protocols xorg-server-xwayland scdoc hwdata libdisplay-info
- name: Fetch wlroots as a subproject
run: git clone https://gitlab.freedesktop.org/wlroots/wlroots.git subprojects/wlroots -b $WLROOTS_VERSION
- name: Check for formatting changes
run: |
meson --wrap-mode=nodownload build-clang-format -Dwlroots:xwayland=enabled
ninja -C build-clang-format clang-format-check
scan-build:
runs-on: ubuntu-latest
container: "archlinux:base-devel"
env:
CC: clang
steps:
- name: Checkout Cage
uses: actions/checkout@v2
- name: Install dependencies
run: |
pacman-key --init
pacman -Syu --noconfirm xcb-util-wm seatd git clang meson libinput libdrm mesa libxkbcommon wayland wayland-protocols xorg-server-xwayland scdoc hwdata libdisplay-info
- name: Fetch wlroots as a subproject
run: git clone https://gitlab.freedesktop.org/wlroots/wlroots.git subprojects/wlroots -b $WLROOTS_VERSION
- name: Run scan-build
run: |
meson --wrap-mode=nodownload build-scan-build -Dwlroots:xwayland=enabled
ninja -C build-scan-build scan-build

View file

@ -1,4 +1,5 @@
Copyright (c) 2018-2019 Jente Hidskes
Copyright (c) 2018-2020 Jente Hidskes
Copyright (c) 2019 The Sway authors
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

View file

@ -1,52 +1,61 @@
# Cage: a Wayland kiosk [![builds.sr.ht status](https://builds.sr.ht/~hjdskes.svg)](https://builds.sr.ht/~hjdskes?)
# Cage: a Wayland kiosk
<img src="https://www.hjdskes.nl/img/projects/cage/cage.svg" alt="Cage's logo" width="150px" align="right">
This is Cage, a Wayland kiosk. A kiosk runs a single, maximized
application.
This README is only relevant for development resources and
instructions. For a description of Cage and installation instructions
for end-users, please see [its project
page](https://hjdskes.nl/projects/cage).
This README is only relevant for development resources and instructions. For a
description of Cage and installation instructions for end-users, please see
[its project page](https://www.hjdskes.nl/projects/cage) and [the
Wiki](https://github.com/cage-kiosk/cage/wiki/).
See [the man page](./cage.1.scd) for a list of possible environment variables and run options.
## Release signatures
Releases up to version 0.1.4 are signed with [6EBC43B1](http://keys.gnupg.net/pks/lookup?op=vindex&fingerprint=on&search=0x37C445296EBC43B1). Releases from 0.1.5 onwards are signed with
[E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
All releases are published on [GitHub](https://github.com/cage-kiosk/cage/releases).
## Building and running Cage
You can build Cage with the [meson](https://mesonbuild.com/) build system. It
requires wayland, wlroots and xkbcommon to be installed. Note that Cage is
developed against the latest tag of wlroots, in order to not constantly chase
breaking changes as soon as they occur.
requires wayland, wlroots, and xkbcommon to be installed. Optionally, install
scdoc for manual pages. Cage is currently based on branch 0.18 of wlroots.
Simply execute the following steps to build Cage:
```
$ meson build
$ ninja -C build
$ meson setup build
$ meson compile -C build
```
Cage comes with compile-time support for XWayland. To enable this,
first make sure that your version of wlroots is compiled with this
option. Then, add `-Dxwayland=true` to the `meson` command above. Note
that you'll need to have the XWayland binary installed on your system
for this to work.
By default, this builds a debug build. To build a release build, use `meson
setup build --buildtype=release`.
You can run Cage by running `./build/cage APPLICATION`. If you run it
from within an existing X11 or Wayland session, it will open in a
virtual output as a window in your existing session. If you run it at
a TTY, it'll run with the KMS+DRM backend. In debug mode (default
build type with Meson), press Alt+Esc to quit. To build a release
build, use `meson build --buildtype=release`.
Cage comes with compile-time support for XWayland. To enable this, make sure
that your version of wlroots is compiled with this option. Note that you'll
need to have the XWayland binary installed on your system for this to work.
You can run Cage by running `./build/cage APPLICATION`. If you run it from
within an existing X11 or Wayland session, it will open in a virtual output as
a window in your existing session. If you run it at a TTY, it'll run with the
KMS+DRM backend. In debug mode (default build type with Meson), press
<kbd>Alt</kbd>+<kbd>Esc</kbd> to quit. For more configuration options, see
[Configuration](https://github.com/cage-kiosk/cage/wiki/Configuration).
Cage is based on the annotated source of tinywl and rootston.
## Bugs
For any bug, please [create an
issue](https://github.com/Hjdskes/cage/issues/new) on
[GitHub](https://github.com/Hjdskes/cage).
issue](https://github.com/cage-kiosk/cage/issues/new) on
[GitHub](https://github.com/cage-kiosk/cage).
## License
Please see
[LICENSE](https://github.com/Hjdskes/cage/blob/master/LICENSE) on
[GitHub](https://github.com/Hjdskes/cage).
[LICENSE](https://github.com/cage-kiosk/cage/blob/master/LICENSE) on
[GitHub](https://github.com/cage-kiosk/cage).
Copyright © 2018-2019 Jente Hidskes <hjdskes@gmail.com>
Copyright © 2018-2020 Jente Hidskes <dev@hjdskes.nl>

71
cage.1.scd Normal file
View file

@ -0,0 +1,71 @@
cage(1)
# NAME
cage - a Wayland kiosk compositor
# SYNOPSIS
*cage* [options...] [--] [_application_...]
# DESCRIPTION
Cage runs a single, maximized application. Cage can run multiple applications,
but only a single one is visible at any point in time. User interaction and
activities outside the scope of the running application are prevented.
# OPTIONS
*-d*
Don't draw client side decorations when possible.
*-D*
Enable debug logging.
*-h*
Show the help message.
*-m* <mode>
Set the multi-monitor behavior. Supported modes are:
*last* Cage uses only the last connected monitor.
*extend* Cage extends the display across all connected monitors.
*-s*
Allow VT switching
*-v*
Show the version number and exit.
# ENVIRONMENT
_DISPLAY_
If compiled with Xwayland support, this will be set to the name of the
X display used for Xwayland. Otherwise, probe the X11 backend.
_WAYLAND_DISPLAY_
Specifies the name of the Wayland display that Cage is running on.
_XCURSOR_PATH_
Directory where cursors are located.
_XCURSOR_SIZE_
Specifies the configured cursor size.
_XCURSOR_THEME_
Specifies the configured cursor theme.
_XKB_DEFAULT_RULES_, _XKB_DEFAULT_MODEL_, _XKB_DEFAULT_LAYOUT_,
_XKB_DEFAULT_VARIANT_, _XKB_DEFAULT_OPTIONS_
Configures the xkb keyboard settings. See *xkeyboard-config*(7).
# SEE ALSO
*xkeyboard-config(7)*
# BUGS
Report bugs at https://github.com/cage-kiosk/cage
# AUTHORS
Jente Hidskes <dev@hjdskes.nl>

498
cage.c
View file

@ -1,7 +1,7 @@
/*
* Cage: A Wayland kiosk.
*
* Copyright (C) 2018-2019 Jente Hidskes
* Copyright (C) 2018-2020 Jente Hidskes
*
* See the LICENSE file accompanying this file.
*/
@ -10,25 +10,41 @@
#include "config.h"
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output_management_v1.h>
#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_primary_selection_v1.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_single_pixel_buffer_v1.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h>
#if CAGE_HAS_XWAYLAND
#include <wlr/types/wlr_xcursor_manager.h>
#endif
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#if CAGE_HAS_XWAYLAND
@ -45,53 +61,178 @@
#include "xwayland.h"
#endif
static bool
spawn_primary_client(char *argv[], pid_t *pid_out)
void
server_terminate(struct cg_server *server)
{
// Workaround for https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/421
if (server->terminated) {
return;
}
wl_display_terminate(server->wl_display);
}
static void
handle_display_destroy(struct wl_listener *listener, void *data)
{
struct cg_server *server = wl_container_of(listener, server, display_destroy);
server->terminated = true;
}
static int
sigchld_handler(int fd, uint32_t mask, void *data)
{
struct cg_server *server = data;
/* Close Cage's read pipe. */
close(fd);
if (mask & WL_EVENT_HANGUP) {
wlr_log(WLR_DEBUG, "Child process closed normally");
} else if (mask & WL_EVENT_ERROR) {
wlr_log(WLR_DEBUG, "Connection closed by server");
}
server->return_app_code = true;
server_terminate(server);
return 0;
}
static bool
set_cloexec(int fd)
{
int flags = fcntl(fd, F_GETFD);
if (flags == -1) {
wlr_log(WLR_ERROR, "Unable to set the CLOEXEC flag: fnctl failed");
return false;
}
flags = flags | FD_CLOEXEC;
if (fcntl(fd, F_SETFD, flags) == -1) {
wlr_log(WLR_ERROR, "Unable to set the CLOEXEC flag: fnctl failed");
return false;
}
return true;
}
static bool
spawn_primary_client(struct cg_server *server, char *argv[], pid_t *pid_out, struct wl_event_source **sigchld_source)
{
int fd[2];
if (pipe(fd) != 0) {
wlr_log(WLR_ERROR, "Unable to create pipe");
return false;
}
pid_t pid = fork();
if (pid == 0) {
sigset_t set;
sigemptyset(&set);
sigprocmask(SIG_SETMASK, &set, NULL);
/* Close read, we only need write in the primary client process. */
close(fd[0]);
execvp(argv[0], argv);
/* execvp() returns only on failure */
wlr_log_errno(WLR_ERROR, "Failed to spawn client");
_exit(1);
} else if (pid == -1) {
wlr_log_errno(WLR_ERROR, "Unable to fork");
return false;
}
/* Set this early so that if we fail, the client process will be cleaned up properly. */
*pid_out = pid;
if (!set_cloexec(fd[0]) || !set_cloexec(fd[1])) {
return false;
}
/* Close write, we only need read in Cage. */
close(fd[1]);
struct wl_event_loop *event_loop = wl_display_get_event_loop(server->wl_display);
uint32_t mask = WL_EVENT_HANGUP | WL_EVENT_ERROR;
*sigchld_source = wl_event_loop_add_fd(event_loop, fd[0], mask, sigchld_handler, server);
wlr_log(WLR_DEBUG, "Child process created with pid %d", pid);
return true;
}
static int
cleanup_primary_client(pid_t pid)
{
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
wlr_log(WLR_DEBUG, "Child exited normally with exit status %d", WEXITSTATUS(status));
return WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
/* Mimic Bash and other shells for the exit status */
wlr_log(WLR_DEBUG, "Child was terminated by a signal (%d)", WTERMSIG(status));
return 128 + WTERMSIG(status);
}
return 0;
}
static bool
drop_permissions(void)
{
if (getuid() == 0 || getgid() == 0) {
wlr_log(WLR_INFO, "Running as root user, this is dangerous");
return true;
}
if (getuid() != geteuid() || getgid() != getegid()) {
wlr_log(WLR_INFO, "setuid/setgid bit detected, dropping permissions");
// Set the gid and uid in the correct order.
if (setgid(getgid()) != 0 || setuid(getuid()) != 0) {
wlr_log(WLR_ERROR, "Unable to drop root, refusing to start");
return false;
}
}
if (setgid(0) != -1 || setuid(0) != -1) {
wlr_log(WLR_ERROR,
"Unable to drop root (we shouldn't be able to restore it after setuid), refusing to start");
return false;
}
return true;
}
static int
handle_signal(int signal, void *data)
{
struct wl_display *display = data;
struct cg_server *server = data;
switch (signal) {
case SIGINT:
/* Fallthrough */
case SIGTERM:
wl_display_terminate(display);
server_terminate(server);
return 0;
default:
return 1;
return 0;
}
}
static void
usage(FILE *file, const char *cage)
{
fprintf(file, "Usage: %s [OPTIONS] [--] APPLICATION\n"
fprintf(file,
"Usage: %s [OPTIONS] [--] [APPLICATION...]\n"
"\n"
" -d\t Don't draw client side decorations, when possible\n"
" -r\t Rotate the output 90 degrees clockwise, specify up to three times\n"
#ifdef DEBUG
" -D\t Turn on damage tracking debugging\n"
#endif
" -D\t Enable debug logging\n"
" -h\t Display this help message\n"
" -m extend Extend the display across all connected outputs (default)\n"
" -m last Use only the last connected output\n"
" -s\t Allow VT switching\n"
" -v\t Show the version number and exit\n"
"\n"
" Use -- when you want to pass arguments to APPLICATION\n",
cage);
@ -101,71 +242,62 @@ static bool
parse_args(struct cg_server *server, int argc, char *argv[])
{
int c;
#ifdef DEBUG
while ((c = getopt(argc, argv, "drDh")) != -1) {
#else
while ((c = getopt(argc, argv, "drh")) != -1) {
#endif
while ((c = getopt(argc, argv, "dDhm:sv")) != -1) {
switch (c) {
case 'd':
server->xdg_decoration = true;
break;
case 'r':
server->output_transform++;
if (server->output_transform > WL_OUTPUT_TRANSFORM_270) {
server->output_transform = WL_OUTPUT_TRANSFORM_NORMAL;
}
break;
#ifdef DEBUG
case 'D':
server->debug_damage_tracking = true;
server->log_level = WLR_DEBUG;
break;
#endif
case 'h':
usage(stdout, argv[0]);
return false;
case 'm':
if (strcmp(optarg, "last") == 0) {
server->output_mode = CAGE_MULTI_OUTPUT_MODE_LAST;
} else if (strcmp(optarg, "extend") == 0) {
server->output_mode = CAGE_MULTI_OUTPUT_MODE_EXTEND;
}
break;
case 's':
server->allow_vt_switch = true;
break;
case 'v':
fprintf(stdout, "Cage version " CAGE_VERSION "\n");
exit(0);
default:
usage(stderr, argv[0]);
return false;
}
}
if (optind >= argc) {
usage(stderr, argv[0]);
return false;
}
return true;
}
int
main(int argc, char *argv[])
{
struct cg_server server = {0};
struct wl_event_loop *event_loop = NULL;
struct wl_event_source *sigint_source = NULL;
struct wl_event_source *sigterm_source = NULL;
struct wlr_renderer *renderer = NULL;
struct wlr_compositor *compositor = NULL;
struct wlr_data_device_manager *data_device_mgr = NULL;
struct wlr_server_decoration_manager *server_decoration_manager = NULL;
struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager = NULL;
struct wlr_xdg_shell *xdg_shell = NULL;
#if CAGE_HAS_XWAYLAND
struct wlr_xwayland *xwayland = NULL;
struct wlr_xcursor_manager *xcursor_manager = NULL;
struct cg_server server = {.log_level = WLR_INFO};
struct wl_event_source *sigchld_source = NULL;
pid_t pid = 0;
int ret = 0, app_ret = 0;
#ifdef DEBUG
server.log_level = WLR_DEBUG;
#endif
int ret = 0;
if (!parse_args(&server, argc, argv)) {
return 1;
}
#ifdef DEBUG
wlr_log_init(WLR_DEBUG, NULL);
#else
wlr_log_init(WLR_ERROR, NULL);
#endif
wlr_log_init(server.log_level, NULL);
/* Wayland requires XDG_RUNTIME_DIR to be set. */
if (!getenv("XDG_RUNTIME_DIR")) {
wlr_log(WLR_ERROR, "XDG_RUNTIME_DIR is not set in the environment");
return 1;
}
server.wl_display = wl_display_create();
if (!server.wl_display) {
@ -173,57 +305,101 @@ main(int argc, char *argv[])
return 1;
}
event_loop = wl_display_get_event_loop(server.wl_display);
sigint_source = wl_event_loop_add_signal(event_loop, SIGINT, handle_signal, &server.wl_display);
sigterm_source = wl_event_loop_add_signal(event_loop, SIGTERM, handle_signal, &server.wl_display);
server.display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server.wl_display, &server.display_destroy);
server.backend = wlr_backend_autocreate(server.wl_display, NULL);
struct wl_event_loop *event_loop = wl_display_get_event_loop(server.wl_display);
struct wl_event_source *sigint_source = wl_event_loop_add_signal(event_loop, SIGINT, handle_signal, &server);
struct wl_event_source *sigterm_source = wl_event_loop_add_signal(event_loop, SIGTERM, handle_signal, &server);
server.backend = wlr_backend_autocreate(event_loop, &server.session);
if (!server.backend) {
wlr_log(WLR_ERROR, "Unable to create the wlroots backend");
ret = 1;
goto end;
}
renderer = wlr_backend_get_renderer(server.backend);
wlr_renderer_init_wl_display(renderer, server.wl_display);
if (!drop_permissions()) {
ret = 1;
goto end;
}
server.renderer = wlr_renderer_autocreate(server.backend);
if (!server.renderer) {
wlr_log(WLR_ERROR, "Unable to create the wlroots renderer");
ret = 1;
goto end;
}
server.allocator = wlr_allocator_autocreate(server.backend, server.renderer);
if (!server.allocator) {
wlr_log(WLR_ERROR, "Unable to create the wlroots allocator");
ret = 1;
goto end;
}
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
wl_list_init(&server.views);
wl_list_init(&server.outputs);
server.output_layout = wlr_output_layout_create();
server.output_layout = wlr_output_layout_create(server.wl_display);
if (!server.output_layout) {
wlr_log(WLR_ERROR, "Unable to create output layout");
ret = 1;
goto end;
}
server.output_layout_change.notify = handle_output_layout_change;
wl_signal_add(&server.output_layout->events.change, &server.output_layout_change);
compositor = wlr_compositor_create(server.wl_display, renderer);
server.scene = wlr_scene_create();
if (!server.scene) {
wlr_log(WLR_ERROR, "Unable to create scene");
ret = 1;
goto end;
}
server.scene_output_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout);
struct wlr_compositor *compositor = wlr_compositor_create(server.wl_display, 6, server.renderer);
if (!compositor) {
wlr_log(WLR_ERROR, "Unable to create the wlroots compositor");
ret = 1;
goto end;
}
data_device_mgr = wlr_data_device_manager_create(server.wl_display);
if (!data_device_mgr) {
if (!wlr_subcompositor_create(server.wl_display)) {
wlr_log(WLR_ERROR, "Unable to create the wlroots subcompositor");
ret = 1;
goto end;
}
if (!wlr_data_device_manager_create(server.wl_display)) {
wlr_log(WLR_ERROR, "Unable to create the data device manager");
ret = 1;
goto end;
}
if (!wlr_primary_selection_v1_device_manager_create(server.wl_display)) {
wlr_log(WLR_ERROR, "Unable to create primary selection device manager");
ret = 1;
goto end;
}
/* Configure a listener to be notified when new outputs are
* available on the backend. We use this only to detect the
* first output and ignore subsequent outputs. */
server.new_output.notify = handle_new_output;
wl_signal_add(&server.backend->events.new_output, &server.new_output);
server.seat = seat_create(&server);
server.seat = seat_create(&server, server.backend);
if (!server.seat) {
wlr_log(WLR_ERROR, "Unable to create the seat");
ret = 1;
goto end;
}
server.idle = wlr_idle_create(server.wl_display);
server.idle = wlr_idle_notifier_v1_create(server.wl_display);
if (!server.idle) {
wlr_log(WLR_ERROR, "Unable to create the idle tracker");
ret = 1;
@ -240,16 +416,19 @@ main(int argc, char *argv[])
wl_signal_add(&server.idle_inhibit_v1->events.new_inhibitor, &server.new_idle_inhibitor_v1);
wl_list_init(&server.inhibitors);
xdg_shell = wlr_xdg_shell_create(server.wl_display);
struct wlr_xdg_shell *xdg_shell = wlr_xdg_shell_create(server.wl_display, 5);
if (!xdg_shell) {
wlr_log(WLR_ERROR, "Unable to create the XDG shell interface");
ret = 1;
goto end;
}
server.new_xdg_shell_surface.notify = handle_xdg_shell_surface_new;
wl_signal_add(&xdg_shell->events.new_surface, &server.new_xdg_shell_surface);
server.new_xdg_toplevel.notify = handle_new_xdg_toplevel;
wl_signal_add(&xdg_shell->events.new_toplevel, &server.new_xdg_toplevel);
server.new_xdg_popup.notify = handle_new_xdg_popup;
wl_signal_add(&xdg_shell->events.new_popup, &server.new_xdg_popup);
xdg_decoration_manager = wlr_xdg_decoration_manager_v1_create(server.wl_display);
struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager =
wlr_xdg_decoration_manager_v1_create(server.wl_display);
if (!xdg_decoration_manager) {
wlr_log(WLR_ERROR, "Unable to create the XDG decoration manager");
ret = 1;
@ -258,51 +437,128 @@ main(int argc, char *argv[])
wl_signal_add(&xdg_decoration_manager->events.new_toplevel_decoration, &server.xdg_toplevel_decoration);
server.xdg_toplevel_decoration.notify = handle_xdg_toplevel_decoration;
server_decoration_manager = wlr_server_decoration_manager_create(server.wl_display);
struct wlr_server_decoration_manager *server_decoration_manager =
wlr_server_decoration_manager_create(server.wl_display);
if (!server_decoration_manager) {
wlr_log(WLR_ERROR, "Unable to create the server decoration manager");
ret = 1;
goto end;
}
wlr_server_decoration_manager_set_default_mode(server_decoration_manager,
server.xdg_decoration ?
WLR_SERVER_DECORATION_MANAGER_MODE_SERVER :
WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT);
wlr_server_decoration_manager_set_default_mode(
server_decoration_manager, server.xdg_decoration ? WLR_SERVER_DECORATION_MANAGER_MODE_SERVER
: WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT);
#if CAGE_HAS_XWAYLAND
xwayland = wlr_xwayland_create(server.wl_display, compositor, true);
if (!xwayland) {
wlr_log(WLR_ERROR, "Cannot create XWayland server");
if (!wlr_viewporter_create(server.wl_display)) {
wlr_log(WLR_ERROR, "Unable to create the viewporter interface");
ret = 1;
goto end;
}
server.new_xwayland_surface.notify = handle_xwayland_surface_new;
wl_signal_add(&xwayland->events.new_surface, &server.new_xwayland_surface);
xcursor_manager = wlr_xcursor_manager_create(DEFAULT_XCURSOR, XCURSOR_SIZE);
if (!xcursor_manager) {
wlr_log(WLR_ERROR, "Cannot create XWayland XCursor manager");
ret = 1;
struct wlr_presentation *presentation = wlr_presentation_create(server.wl_display, server.backend, 2);
if (!presentation) {
wlr_log(WLR_ERROR, "Unable to create the presentation interface");
ret = 1;
goto end;
}
if (setenv("DISPLAY", xwayland->display_name, true) < 0) {
wlr_log_errno(WLR_ERROR, "Unable to set DISPLAY for XWayland.",
"Clients may not be able to connect");
} else {
wlr_log(WLR_DEBUG, "XWayland is running on display %s", xwayland->display_name);
if (!wlr_export_dmabuf_manager_v1_create(server.wl_display)) {
wlr_log(WLR_ERROR, "Unable to create the export DMABUF manager");
ret = 1;
goto end;
}
if (wlr_xcursor_manager_load(xcursor_manager, 1)) {
wlr_log(WLR_ERROR, "Cannot load XWayland XCursor theme");
if (!wlr_screencopy_manager_v1_create(server.wl_display)) {
wlr_log(WLR_ERROR, "Unable to create the screencopy manager");
ret = 1;
goto end;
}
struct wlr_xcursor *xcursor =
wlr_xcursor_manager_get_xcursor(xcursor_manager, DEFAULT_XCURSOR, 1);
if (xcursor) {
struct wlr_xcursor_image *image = xcursor->images[0];
wlr_xwayland_set_cursor(xwayland, image->buffer,
image->width * 4, image->width, image->height,
image->hotspot_x, image->hotspot_y);
if (!wlr_single_pixel_buffer_manager_v1_create(server.wl_display)) {
wlr_log(WLR_ERROR, "Unable to create the single pixel buffer manager");
ret = 1;
goto end;
}
if (!wlr_xdg_output_manager_v1_create(server.wl_display, server.output_layout)) {
wlr_log(WLR_ERROR, "Unable to create the output manager");
ret = 1;
goto end;
}
server.output_manager_v1 = wlr_output_manager_v1_create(server.wl_display);
if (!server.output_manager_v1) {
wlr_log(WLR_ERROR, "Unable to create the output manager");
ret = 1;
goto end;
}
server.output_manager_apply.notify = handle_output_manager_apply;
wl_signal_add(&server.output_manager_v1->events.apply, &server.output_manager_apply);
server.output_manager_test.notify = handle_output_manager_test;
wl_signal_add(&server.output_manager_v1->events.test, &server.output_manager_test);
if (!wlr_gamma_control_manager_v1_create(server.wl_display)) {
wlr_log(WLR_ERROR, "Unable to create the gamma control manager");
ret = 1;
goto end;
}
struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard =
wlr_virtual_keyboard_manager_v1_create(server.wl_display);
if (!virtual_keyboard) {
wlr_log(WLR_ERROR, "Unable to create the virtual keyboard manager");
ret = 1;
goto end;
}
wl_signal_add(&virtual_keyboard->events.new_virtual_keyboard, &server.new_virtual_keyboard);
struct wlr_virtual_pointer_manager_v1 *virtual_pointer =
wlr_virtual_pointer_manager_v1_create(server.wl_display);
if (!virtual_pointer) {
wlr_log(WLR_ERROR, "Unable to create the virtual pointer manager");
ret = 1;
goto end;
}
wl_signal_add(&virtual_pointer->events.new_virtual_pointer, &server.new_virtual_pointer);
server.relative_pointer_manager = wlr_relative_pointer_manager_v1_create(server.wl_display);
if (!server.relative_pointer_manager) {
wlr_log(WLR_ERROR, "Unable to create the relative pointer manager");
ret = 1;
goto end;
}
#if CAGE_HAS_XWAYLAND
struct wlr_xcursor_manager *xcursor_manager = NULL;
struct wlr_xwayland *xwayland = wlr_xwayland_create(server.wl_display, compositor, true);
if (!xwayland) {
wlr_log(WLR_ERROR, "Cannot create XWayland server");
} else {
server.new_xwayland_surface.notify = handle_xwayland_surface_new;
wl_signal_add(&xwayland->events.new_surface, &server.new_xwayland_surface);
xcursor_manager = wlr_xcursor_manager_create(DEFAULT_XCURSOR, XCURSOR_SIZE);
if (!xcursor_manager) {
wlr_log(WLR_ERROR, "Cannot create XWayland XCursor manager");
ret = 1;
goto end;
}
if (setenv("DISPLAY", xwayland->display_name, true) < 0) {
wlr_log_errno(WLR_ERROR,
"Unable to set DISPLAY for XWayland. Clients may not be able to connect");
} else {
wlr_log(WLR_DEBUG, "XWayland is running on display %s", xwayland->display_name);
}
if (!wlr_xcursor_manager_load(xcursor_manager, 1)) {
wlr_log(WLR_ERROR, "Cannot load XWayland XCursor theme");
}
struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(xcursor_manager, DEFAULT_XCURSOR, 1);
if (xcursor) {
struct wlr_xcursor_image *image = xcursor->images[0];
wlr_xwayland_set_cursor(xwayland, image->buffer, image->width * 4, image->width, image->height,
image->hotspot_x, image->hotspot_y);
}
}
#endif
@ -310,7 +566,7 @@ main(int argc, char *argv[])
if (!socket) {
wlr_log_errno(WLR_ERROR, "Unable to open Wayland socket");
ret = 1;
goto end;
goto end;
}
if (!wlr_backend_start(server.backend)) {
@ -320,48 +576,62 @@ main(int argc, char *argv[])
}
if (setenv("WAYLAND_DISPLAY", socket, true) < 0) {
wlr_log_errno(WLR_ERROR, "Unable to set WAYLAND_DISPLAY.",
"Clients may not be able to connect");
wlr_log_errno(WLR_ERROR, "Unable to set WAYLAND_DISPLAY. Clients may not be able to connect");
} else {
wlr_log(WLR_DEBUG, "Cage is running on Wayland display %s", socket);
wlr_log(WLR_DEBUG, "Cage " CAGE_VERSION " is running on Wayland display %s", socket);
}
#if CAGE_HAS_XWAYLAND
wlr_xwayland_set_seat(xwayland, server.seat->seat);
if (xwayland) {
wlr_xwayland_set_seat(xwayland, server.seat->seat);
}
#endif
pid_t pid;
if (!spawn_primary_client(argv + optind, &pid)) {
if (optind < argc && !spawn_primary_client(&server, argv + optind, &pid, &sigchld_source)) {
ret = 1;
goto end;
}
seat_center_cursor(server.seat);
wl_display_run(server.wl_display);
#if CAGE_HAS_XWAYLAND
if (xwayland) {
wl_list_remove(&server.new_xwayland_surface.link);
}
wlr_xwayland_destroy(xwayland);
wlr_xcursor_manager_destroy(xcursor_manager);
#endif
wl_display_destroy_clients(server.wl_display);
waitpid(pid, NULL, 0);
wl_list_remove(&server.new_virtual_pointer.link);
wl_list_remove(&server.new_virtual_keyboard.link);
wl_list_remove(&server.output_manager_apply.link);
wl_list_remove(&server.output_manager_test.link);
wl_list_remove(&server.xdg_toplevel_decoration.link);
wl_list_remove(&server.new_xdg_toplevel.link);
wl_list_remove(&server.new_xdg_popup.link);
wl_list_remove(&server.new_idle_inhibitor_v1.link);
wl_list_remove(&server.new_output.link);
wl_list_remove(&server.output_layout_change.link);
end:
if (pid != 0)
app_ret = cleanup_primary_client(pid);
if (!ret && server.return_app_code)
ret = app_ret;
wl_event_source_remove(sigint_source);
wl_event_source_remove(sigterm_source);
seat_destroy(server.seat);
wlr_server_decoration_manager_destroy(server_decoration_manager);
wlr_xdg_decoration_manager_v1_destroy(xdg_decoration_manager);
wlr_xdg_shell_destroy(xdg_shell);
wlr_idle_inhibit_v1_destroy(server.idle_inhibit_v1);
if (server.idle) {
wlr_idle_destroy(server.idle);
if (sigchld_source) {
wl_event_source_remove(sigchld_source);
}
wlr_data_device_manager_destroy(data_device_mgr);
wlr_compositor_destroy(compositor);
wlr_output_layout_destroy(server.output_layout);
seat_destroy(server.seat);
/* This function is not null-safe, but we only ever get here
with a proper wl_display. */
wl_display_destroy(server.wl_display);
wlr_scene_node_destroy(&server.scene->tree.node);
wlr_allocator_destroy(server.allocator);
wlr_renderer_destroy(server.renderer);
return ret;
}

View file

@ -3,4 +3,6 @@
#mesondefine CAGE_HAS_XWAYLAND
#mesondefine CAGE_VERSION
#endif

25
contrib/increment-version Executable file
View file

@ -0,0 +1,25 @@
#!/usr/bin/env bash
if [ "$#" -ne 1 ]; then
echo "usage: $0 <new-version>" >&2
exit 1
fi
new_version="$1"
if [ "$new_version" != "${new_version#v}" ]; then
echo "Error: The new version shouldn't be prefixed with a \"v\"." >&2
exit 1
fi
set -x
sed -i meson.build -e "s/^ version: '.*'/ version: '$new_version'/"
echo -n "Minimum wlroots version? "
read -r wlr_version_min
sed -i meson.build -e "s/'wlroots', version: '.*'/'wlroots', version: '>= $wlr_version_min'/"
git add meson.build
git commit -m "Update version to $new_version"

21
contrib/release Executable file
View file

@ -0,0 +1,21 @@
#!/usr/bin/env bash
if [ "$#" -ne 1 ]; then
echo "usage: $0 <new-version>" >&2
exit 1
fi
new_version="$1"
if [ "$new_version" != "${new_version#v}" ]; then
echo "Error: The new version shouldn't be prefixed with a \"v\"." >&2
exit 1
fi
set -x
./increment_version "$new_version"
./tag-release "$new_version"
./sign-release
git push --tags

12
contrib/sign-release Executable file
View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -x
project="$(basename "$(pwd)")"
last=$(git describe --tags --abbrev=0)
prefix="$project-${last#v}"
archive="$prefix.tar.gz"
git archive --prefix="$prefix/" -o "$archive" "$last"
gpg --output "$archive".sig --detach-sig "$archive"

26
contrib/tag-release Executable file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -x
if [ "$#" -ne 1 ]; then
echo "usage: $0 <new-version>" >&2
exit 1
fi
last=$(git describe --tags --abbrev=0)
echo "Last release was $last"
next="v$1"
shortlog="$(git shortlog --no-merges "$last"..)"
printf "Shortlog: \n\n%s\n\nRelease $next? [y/N] " "$shortlog"
read -r answer
if [ "$answer" != "y" ]; then
exit 0
fi
project="$(basename "$(pwd)")"
(echo "$project $next"; echo ""; echo "$shortlog") | git tag "$next" -ase -F -

View file

@ -1,15 +1,15 @@
/*
* Cage: A Wayland kiosk.
*
*
* Copyright (C) 2018-2019 Jente Hidskes
*
* See the LICENSE file accompanying this file.
*/
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/types/wlr_idle.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include "idle_inhibit_v1.h"
#include "server.h"
@ -32,7 +32,7 @@ idle_inhibit_v1_check_active(struct cg_server *server)
Hence, we simply check for any inhibitors and inhibit
accordingly. */
bool inhibited = !wl_list_empty(&server->inhibitors);
wlr_idle_set_enabled(server->idle, NULL, !inhibited);
wlr_idle_notifier_v1_set_inhibited(server->idle, inhibited);
}
static void

View file

@ -1,7 +1,7 @@
#ifndef CG_IDLE_INHIBIT_H
#define CG_IDLE_INHIBIT_H
#include <wayland-server.h>
#include <wayland-server-core.h>
void handle_idle_inhibitor_v1_new(struct wl_listener *listener, void *data);

View file

@ -1,9 +1,10 @@
project('cage', 'c',
version: '0.0.1',
version: '0.2.1',
license: 'MIT',
meson_version: '>=0.58.1',
default_options: [
'c_std=c11',
'warning_level=3',
'warning_level=2',
'werror=true',
],
)
@ -11,7 +12,6 @@ project('cage', 'c',
add_project_arguments(
[
'-DWLR_USE_UNSTABLE',
'-Wall',
'-Wundef',
'-Wno-unused-parameter',
],
@ -35,14 +35,13 @@ if is_freebsd
)
endif
wlroots = dependency('wlroots', version: '>= 0.5.0')
wlroots = dependency('wlroots-0.19', fallback: ['wlroots', 'wlroots'])
wayland_protos = dependency('wayland-protocols', version: '>=1.14')
wayland_server = dependency('wayland-server')
pixman = dependency('pixman-1')
xkbcommon = dependency('xkbcommon')
math = cc.find_library('m')
wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
wayland_scanner = find_program('wayland-scanner')
wayland_scanner_server = generator(
wayland_scanner,
@ -65,19 +64,51 @@ server_protos = declare_dependency(
sources: server_protos_headers,
)
if get_option('xwayland')
wlroots_has_xwayland = cc.get_define('WLR_HAS_XWAYLAND', prefix: '#include <wlr/config.h>', dependencies: wlroots) == '1'
if not wlroots_has_xwayland
error('Cannot build Cage with XWayland support: wlroots has been built without it')
else
have_xwayland = true
have_xwayland = wlroots.get_variable(pkgconfig: 'have_xwayland', internal: 'have_xwayland') == 'true'
version = '@0@'.format(meson.project_version())
git = find_program('git', native: true, required: false)
if git.found()
git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false)
git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false)
if git_commit.returncode() == 0 and git_branch.returncode() == 0
version = '@0@-@1@ (branch \'@2@\')'.format(
meson.project_version(),
git_commit.stdout().strip(),
git_branch.stdout().strip(),
)
endif
else
have_xwayland = false
endif
conf_data = configuration_data()
conf_data.set10('CAGE_HAS_XWAYLAND', have_xwayland)
conf_data.set_quoted('CAGE_VERSION', version)
scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
if scdoc.found()
scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true)
sh = find_program('sh', native: true)
mandir = get_option('mandir')
man_files = [
'cage.1.scd'
]
foreach filename : man_files
topic = filename.split('.')[-3].split('/')[-1]
section = filename.split('.')[-2]
output = '@0@.@1@'.format(topic, section)
custom_target(
output,
input: filename,
output: output,
command: [
sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.full_path(), output)
],
install: true,
install_dir: '@0@/man@1@'.format(mandir, section)
)
endforeach
endif
cage_sources = [
'cage.c',
@ -90,8 +121,8 @@ cage_sources = [
cage_headers = [
configure_file(input: 'config.h.in',
output: 'config.h',
configuration: conf_data),
output: 'config.h',
configuration: conf_data),
'idle_inhibit_v1.h',
'output.h',
'seat.h',
@ -113,17 +144,16 @@ executable(
wayland_server,
wlroots,
xkbcommon,
pixman,
math,
],
install: true,
)
summary = [
'',
'Cage @0@'.format(meson.project_version()),
'',
' xwayland: @0@'.format(conf_data.get('CAGE_HAS_XWAYLAND', false)),
''
'',
'Cage @0@'.format(version),
'',
' xwayland: @0@'.format(have_xwayland),
''
]
message('\n'.join(summary))

View file

@ -1 +1 @@
option('xwayland', type: 'boolean', value: 'false', description: 'Enable support for X11 applications')
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')

629
output.c
View file

@ -1,7 +1,8 @@
/*
* Cage: A Wayland kiosk.
*
* Copyright (C) 2018-2019 Jente Hidskes
* Copyright (C) 2018-2021 Jente Hidskes
* Copyright (C) 2019 The Sway authors
*
* See the LICENSE file accompanying this file.
*/
@ -9,329 +10,224 @@
#define _POSIX_C_SOURCE 200112L
#include "config.h"
#include <wlr/config.h>
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/backend/wayland.h>
#include <wlr/config.h>
#if WLR_HAS_X11_BACKEND
#include <wlr/backend/x11.h>
#endif
#include <wlr/render/swapchain.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_damage.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_output_management_v1.h>
#include <wlr/types/wlr_output_swapchain_manager.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
#include "output.h"
#include "seat.h"
#include "server.h"
#include "view.h"
#if CAGE_HAS_XWAYLAND
#include "xwayland.h"
#endif
#define OUTPUT_CONFIG_UPDATED \
(WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_TRANSFORM | \
WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED)
static void
scissor_output(struct wlr_output *output, pixman_box32_t *rect)
update_output_manager_config(struct cg_server *server)
{
struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create();
struct wlr_box box = {
.x = rect->x1,
.y = rect->y1,
.width = rect->x2 - rect->x1,
.height = rect->y2 - rect->y1,
};
int output_width, output_height;
wlr_output_transformed_resolution(output, &output_width, &output_height);
enum wl_output_transform transform = wlr_output_transform_invert(output->transform);
wlr_box_transform(&box, &box, transform, output_width, output_height);
wlr_renderer_scissor(renderer, &box);
}
static void
send_frame_done(struct wlr_surface *surface, int _unused, int _not_used, void *data)
{
struct timespec *now = data;
wlr_surface_send_frame_done(surface, now);
}
/* Used to move all of the data necessary to damage a surface. */
struct damage_data {
struct cg_output *output;
double x;
double y;
bool whole;
};
wl_list_for_each (output, &server->outputs, link) {
struct wlr_output *wlr_output = output->wlr_output;
struct wlr_output_configuration_head_v1 *config_head =
wlr_output_configuration_head_v1_create(config, wlr_output);
struct wlr_box output_box;
wlr_output_layout_get_box(server->output_layout, wlr_output, &output_box);
if (!wlr_box_empty(&output_box)) {
config_head->state.x = output_box.x;
config_head->state.y = output_box.y;
}
}
wlr_output_manager_v1_set_configuration(server->output_manager_v1, config);
}
static inline void
output_layout_add_auto(struct cg_output *output)
{
assert(output->scene_output != NULL);
struct wlr_output_layout_output *layout_output =
wlr_output_layout_add_auto(output->server->output_layout, output->wlr_output);
wlr_scene_output_layout_add_output(output->server->scene_output_layout, layout_output, output->scene_output);
}
static inline void
output_layout_add(struct cg_output *output, int32_t x, int32_t y)
{
assert(output->scene_output != NULL);
bool exists = wlr_output_layout_get(output->server->output_layout, output->wlr_output);
struct wlr_output_layout_output *layout_output =
wlr_output_layout_add(output->server->output_layout, output->wlr_output, x, y);
if (exists) {
return;
}
wlr_scene_output_layout_add_output(output->server->scene_output_layout, layout_output, output->scene_output);
}
static inline void
output_layout_remove(struct cg_output *output)
{
wlr_output_layout_remove(output->server->output_layout, output->wlr_output);
}
static void
damage_surface(struct wlr_surface *surface, int sx, int sy, void *data)
output_enable(struct cg_output *output)
{
struct damage_data *ddata = data;
struct cg_output *output = ddata->output;
struct wlr_output *wlr_output = output->wlr_output;
if (!wlr_surface_has_buffer(surface)) {
return;
/* Outputs get enabled by the backend before firing the new_output event,
* so we can't do a check for already enabled outputs here unless we
* duplicate the enabled property in cg_output. */
wlr_log(WLR_DEBUG, "Enabling output %s", wlr_output->name);
struct wlr_output_state state = {0};
wlr_output_state_set_enabled(&state, true);
if (wlr_output_commit_state(wlr_output, &state)) {
output_layout_add_auto(output);
}
double x = ddata->x + sx, y = ddata->y + sy;
wlr_output_layout_output_coords(output->server->output_layout, wlr_output, &x, &y);
struct wlr_box box = {
.x = x * wlr_output->scale,
.y = y * wlr_output->scale,
.width = surface->current.width * wlr_output->scale,
.height = surface->current.height * wlr_output->scale,
};
if (ddata->whole) {
wlr_output_damage_add_box(output->damage, &box);
} else if (pixman_region32_not_empty(&surface->buffer_damage)) {
pixman_region32_t damage;
pixman_region32_init(&damage);
wlr_surface_get_effective_damage(surface, &damage);
wlr_region_scale(&damage, &damage, wlr_output->scale);
if (ceil(wlr_output->scale) > surface->current.scale) {
/* When scaling up a surface it'll become
blurry, so we need to expand the damage
region. */
wlr_region_expand(&damage, &damage,
ceil(wlr_output->scale) - surface->current.scale);
}
pixman_region32_translate(&damage, box.x, box.y);
wlr_output_damage_add(output->damage, &damage);
pixman_region32_fini(&damage);
}
}
/* Used to move all of the data necessary to render a surface from the
* top-level frame handler to the per-surface render function. */
struct render_data {
struct wlr_output_layout *output_layout;
struct wlr_output *output;
struct timespec *when;
pixman_region32_t *damage;
double x, y;
};
static void
render_surface(struct wlr_surface *surface, int sx, int sy, void *data)
{
struct render_data *rdata = data;
struct wlr_output *output = rdata->output;
if (!wlr_surface_has_buffer(surface)) {
return;
}
struct wlr_texture *texture = wlr_surface_get_texture(surface);
if (!texture) {
wlr_log(WLR_DEBUG, "Cannot obtain surface texture");
return;
}
double x = rdata->x + sx, y = rdata->y + sy;
wlr_output_layout_output_coords(rdata->output_layout, output, &x, &y);
struct wlr_box box = {
.x = x * output->scale,
.y = y * output->scale,
.width = surface->current.width * output->scale,
.height = surface->current.height * output->scale,
};
pixman_region32_t damage;
pixman_region32_init(&damage);
pixman_region32_union_rect(&damage, &damage, box.x, box.y, box.width, box.height);
pixman_region32_intersect(&damage, &damage, rdata->damage);
if (!pixman_region32_not_empty(&damage)) {
goto damage_finish;
}
float matrix[9];
enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform);
wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix);
int nrects;
pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
for (int i = 0; i < nrects; i++) {
scissor_output(output, &rects[i]);
wlr_render_texture_with_matrix(surface->renderer, texture, matrix, 1);
}
damage_finish:
pixman_region32_fini(&damage);
update_output_manager_config(output->server);
}
static void
drag_icons_for_each_surface(struct cg_server *server, wlr_surface_iterator_func_t iterator,
void *data)
output_disable(struct cg_output *output)
{
struct render_data *rdata = data;
struct cg_drag_icon *drag_icon;
wl_list_for_each(drag_icon, &server->seat->drag_icons, link) {
if (!drag_icon->wlr_drag_icon->mapped) {
continue;
}
rdata->x = drag_icon->x;
rdata->y = drag_icon->y;
wlr_surface_for_each_surface(drag_icon->wlr_drag_icon->surface,
iterator,
data);
struct wlr_output *wlr_output = output->wlr_output;
if (!wlr_output->enabled) {
wlr_log(WLR_DEBUG, "Not disabling already disabled output %s", wlr_output->name);
return;
}
wlr_log(WLR_DEBUG, "Disabling output %s", wlr_output->name);
struct wlr_output_state state = {0};
wlr_output_state_set_enabled(&state, false);
wlr_output_commit_state(wlr_output, &state);
output_layout_remove(output);
}
static void
handle_output_damage_frame(struct wl_listener *listener, void *data)
handle_output_frame(struct wl_listener *listener, void *data)
{
struct cg_output *output = wl_container_of(listener, output, damage_frame);
struct wlr_renderer *renderer = wlr_backend_get_renderer(output->server->backend);
struct cg_output *output = wl_container_of(listener, output, frame);
struct timespec now;
if (!output->wlr_output->enabled || !output->scene_output) {
return;
}
wlr_scene_output_commit(output->scene_output, NULL);
struct timespec now = {0};
clock_gettime(CLOCK_MONOTONIC, &now);
bool needs_swap;
pixman_region32_t damage;
pixman_region32_init(&damage);
if (!wlr_output_damage_make_current(output->damage, &needs_swap, &damage)) {
wlr_log(WLR_ERROR, "Cannot make damage output current");
goto damage_finish;
}
if (!needs_swap) {
wlr_log(WLR_DEBUG, "Output doesn't need swap and isn't damaged");
goto damage_finish;
}
wlr_renderer_begin(renderer, output->wlr_output->width, output->wlr_output->height);
if (!pixman_region32_not_empty(&damage)) {
wlr_log(WLR_DEBUG, "Output isn't damaged but needs a buffer swap");
goto renderer_end;
}
#ifdef DEBUG
if (output->server->debug_damage_tracking) {
wlr_renderer_clear(renderer, (float[]){1, 0, 0, 1});
}
#endif
float color[4] = {0.3, 0.3, 0.3, 1.0};
int nrects;
pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
for (int i = 0; i < nrects; i++) {
scissor_output(output->wlr_output, &rects[i]);
wlr_renderer_clear(renderer, color);
}
struct render_data rdata = {
.output_layout = output->server->output_layout,
.output = output->wlr_output,
.when = &now,
.damage = &damage,
};
struct cg_view *view;
wl_list_for_each_reverse(view, &output->server->views, link) {
rdata.x = view->x;
rdata.y = view->y;
view_for_each_surface(view, render_surface, &rdata);
}
drag_icons_for_each_surface(output->server, render_surface, &rdata);
renderer_end:
/* Draw software cursor in case hardware cursors aren't
available. This is a no-op when they are. */
wlr_output_render_software_cursors(output->wlr_output, &damage);
wlr_renderer_scissor(renderer, NULL);
wlr_renderer_end(renderer);
int output_width, output_height;
wlr_output_transformed_resolution(output->wlr_output, &output_width, &output_height);
#ifdef DEBUG
if (output->server->debug_damage_tracking) {
pixman_region32_union_rect(&damage, &damage, 0, 0, output_width, output_height);
}
#endif
enum wl_output_transform transform = wlr_output_transform_invert(output->wlr_output->transform);
wlr_region_transform(&damage, &damage, transform, output_width, output_height);
if (!wlr_output_damage_swap_buffers(output->damage, &now, &damage)) {
wlr_log(WLR_ERROR, "Could not swap buffers");
}
damage_finish:
pixman_region32_fini(&damage);
wl_list_for_each_reverse(view, &output->server->views, link) {
view_for_each_surface(view, send_frame_done, &now);
}
drag_icons_for_each_surface(output->server, send_frame_done, &now);
wlr_scene_output_send_frame_done(output->scene_output, &now);
}
static void
handle_output_transform(struct wl_listener *listener, void *data)
handle_output_commit(struct wl_listener *listener, void *data)
{
struct cg_output *output = wl_container_of(listener, output, transform);
struct cg_output *output = wl_container_of(listener, output, commit);
struct wlr_output_event_commit *event = data;
struct cg_view *view;
wl_list_for_each(view, &output->server->views, link) {
view_position(view);
/* Notes:
* - output layout change will also be called if needed to position the views
* - always update output manager configuration even if the output is now disabled */
if (event->state->committed & OUTPUT_CONFIG_UPDATED) {
update_output_manager_config(output->server);
}
}
static void
handle_output_mode(struct wl_listener *listener, void *data)
handle_output_request_state(struct wl_listener *listener, void *data)
{
struct cg_output *output = wl_container_of(listener, output, mode);
struct cg_output *output = wl_container_of(listener, output, request_state);
struct wlr_output_event_request_state *event = data;
struct cg_view *view;
wl_list_for_each(view, &output->server->views, link) {
view_position(view);
if (wlr_output_commit_state(output->wlr_output, event->state)) {
update_output_manager_config(output->server);
}
}
void
handle_output_layout_change(struct wl_listener *listener, void *data)
{
struct cg_server *server = wl_container_of(listener, server, output_layout_change);
view_position_all(server);
update_output_manager_config(server);
}
static bool
is_nested_output(struct cg_output *output)
{
if (wlr_output_is_wl(output->wlr_output)) {
return true;
}
#if WLR_HAS_X11_BACKEND
if (wlr_output_is_x11(output->wlr_output)) {
return true;
}
#endif
return false;
}
static void
output_destroy(struct cg_output *output)
{
struct cg_server *server = output->server;
bool was_nested_output = is_nested_output(output);
output->wlr_output->data = NULL;
wl_list_remove(&output->destroy.link);
wl_list_remove(&output->mode.link);
wl_list_remove(&output->transform.link);
wl_list_remove(&output->damage_frame.link);
wl_list_remove(&output->damage_destroy.link);
wl_list_remove(&output->commit.link);
wl_list_remove(&output->request_state.link);
wl_list_remove(&output->frame.link);
wl_list_remove(&output->link);
output_layout_remove(output);
free(output);
server->output = NULL;
/* Since there is no use in continuing without our (single)
* output, terminate. */
wl_display_terminate(server->wl_display);
}
static void
handle_output_damage_destroy(struct wl_listener *listener, void *data)
{
struct cg_output *output = wl_container_of(listener, output, damage_destroy);
output_destroy(output);
if (wl_list_empty(&server->outputs) && was_nested_output) {
server_terminate(server);
} else if (server->output_mode == CAGE_MULTI_OUTPUT_MODE_LAST && !wl_list_empty(&server->outputs)) {
struct cg_output *prev = wl_container_of(server->outputs.next, prev, link);
output_enable(prev);
view_position_all(server);
}
}
static void
handle_output_destroy(struct wl_listener *listener, void *data)
{
struct cg_output *output = wl_container_of(listener, output, destroy);
wlr_output_damage_destroy(output->damage);
output_destroy(output);
}
@ -341,85 +237,77 @@ handle_new_output(struct wl_listener *listener, void *data)
struct cg_server *server = wl_container_of(listener, server, new_output);
struct wlr_output *wlr_output = data;
/* On outputs that have modes, we need to set one before we
* can use it. Each monitor supports only a specific set of
* modes. We just pick the last, in the future we could pick
* the mode the display advertises as preferred. */
if (!wl_list_empty(&wlr_output->modes)) {
struct wlr_output_mode *mode = wl_container_of(wlr_output->modes.prev, mode, link);
wlr_output_set_mode(wlr_output, mode);
if (!wlr_output_init_render(wlr_output, server->allocator, server->renderer)) {
wlr_log(WLR_ERROR, "Failed to initialize output rendering");
return;
}
server->output = calloc(1, sizeof(struct cg_output));
server->output->wlr_output = wlr_output;
server->output->server = server;
server->output->damage = wlr_output_damage_create(wlr_output);
struct cg_output *output = calloc(1, sizeof(struct cg_output));
if (!output) {
wlr_log(WLR_ERROR, "Failed to allocate output");
return;
}
server->output->mode.notify = handle_output_mode;
wl_signal_add(&wlr_output->events.mode, &server->output->mode);
server->output->transform.notify = handle_output_transform;
wl_signal_add(&wlr_output->events.transform, &server->output->transform);
server->output->destroy.notify = handle_output_destroy;
wl_signal_add(&wlr_output->events.destroy, &server->output->destroy);
server->output->damage_frame.notify = handle_output_damage_frame;
wl_signal_add(&server->output->damage->events.frame, &server->output->damage_frame);
server->output->damage_destroy.notify = handle_output_damage_destroy;
wl_signal_add(&server->output->damage->events.destroy, &server->output->damage_destroy);
output->wlr_output = wlr_output;
wlr_output->data = output;
output->server = server;
wlr_output_set_transform(wlr_output, server->output_transform);
wl_list_insert(&server->outputs, &output->link);
wlr_output_layout_add_auto(server->output_layout, wlr_output);
output->commit.notify = handle_output_commit;
wl_signal_add(&wlr_output->events.commit, &output->commit);
output->request_state.notify = handle_output_request_state;
wl_signal_add(&wlr_output->events.request_state, &output->request_state);
output->destroy.notify = handle_output_destroy;
wl_signal_add(&wlr_output->events.destroy, &output->destroy);
output->frame.notify = handle_output_frame;
wl_signal_add(&wlr_output->events.frame, &output->frame);
/* Disconnect the signal now, because we only use one static output. */
wl_list_remove(&server->new_output.link);
output->scene_output = wlr_scene_output_create(server->scene, wlr_output);
if (!output->scene_output) {
wlr_log(WLR_ERROR, "Failed to allocate scene output");
return;
}
if (wlr_xcursor_manager_load(server->seat->xcursor_manager, wlr_output->scale)) {
wlr_log(WLR_ERROR, "Cannot load XCursor theme for output '%s' with scale %f",
wlr_output->name,
struct wlr_output_state state = {0};
wlr_output_state_set_enabled(&state, true);
if (!wl_list_empty(&wlr_output->modes)) {
struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
if (preferred_mode) {
wlr_output_state_set_mode(&state, preferred_mode);
}
if (!wlr_output_test_state(wlr_output, &state)) {
struct wlr_output_mode *mode;
wl_list_for_each (mode, &wlr_output->modes, link) {
if (mode == preferred_mode) {
continue;
}
wlr_output_state_set_mode(&state, mode);
if (wlr_output_test_state(wlr_output, &state)) {
break;
}
}
}
}
if (server->output_mode == CAGE_MULTI_OUTPUT_MODE_LAST && wl_list_length(&server->outputs) > 1) {
struct cg_output *next = wl_container_of(output->link.next, next, link);
output_disable(next);
}
if (!wlr_xcursor_manager_load(server->seat->xcursor_manager, wlr_output->scale)) {
wlr_log(WLR_ERROR, "Cannot load XCursor theme for output '%s' with scale %f", wlr_output->name,
wlr_output->scale);
}
/* Place the cursor in the center of the screen. */
wlr_cursor_warp(server->seat->cursor, NULL, wlr_output->width / 2, wlr_output->height / 2);
wlr_output_damage_add_whole(server->output->damage);
}
wlr_log(WLR_DEBUG, "Enabling new output %s", wlr_output->name);
if (wlr_output_commit_state(wlr_output, &state)) {
output_layout_add_auto(output);
}
void
output_damage_view_surface(struct cg_output *cg_output, struct cg_view *view)
{
struct damage_data data = {
.output = cg_output,
.x = view->x,
.y = view->y,
.whole = false,
};
view_for_each_surface(view, damage_surface, &data);
}
void
output_damage_view_whole(struct cg_output *cg_output, struct cg_view *view)
{
struct damage_data data = {
.output = cg_output,
.x = view->x,
.y = view->y,
.whole = true,
};
view_for_each_surface(view, damage_surface, &data);
}
void
output_damage_drag_icon(struct cg_output *cg_output, struct cg_drag_icon *drag_icon)
{
struct damage_data data = {
.output = cg_output,
.x = drag_icon->x,
.y = drag_icon->y,
.whole = true,
};
wlr_surface_for_each_surface(drag_icon->wlr_drag_icon->surface,
damage_surface,
&data);
view_position_all(output->server);
update_output_manager_config(output->server);
}
void
@ -427,6 +315,11 @@ output_set_window_title(struct cg_output *output, const char *title)
{
struct wlr_output *wlr_output = output->wlr_output;
if (!wlr_output->enabled) {
wlr_log(WLR_DEBUG, "Not setting window title for disabled output %s", wlr_output->name);
return;
}
if (wlr_output_is_wl(wlr_output)) {
wlr_wl_output_set_title(wlr_output, title);
#if WLR_HAS_X11_BACKEND
@ -435,3 +328,95 @@ output_set_window_title(struct cg_output *output, const char *title)
#endif
}
}
static bool
output_config_apply(struct cg_server *server, struct wlr_output_configuration_v1 *config, bool test_only)
{
bool ok = false;
size_t states_len;
struct wlr_backend_output_state *states = wlr_output_configuration_v1_build_state(config, &states_len);
if (states == NULL) {
return false;
}
struct wlr_output_swapchain_manager swapchain_manager;
wlr_output_swapchain_manager_init(&swapchain_manager, server->backend);
ok = wlr_output_swapchain_manager_prepare(&swapchain_manager, states, states_len);
if (!ok || test_only) {
goto out;
}
for (size_t i = 0; i < states_len; i++) {
struct wlr_backend_output_state *backend_state = &states[i];
struct cg_output *output = backend_state->output->data;
struct wlr_swapchain *swapchain =
wlr_output_swapchain_manager_get_swapchain(&swapchain_manager, backend_state->output);
struct wlr_scene_output_state_options options = {
.swapchain = swapchain,
};
struct wlr_output_state *state = &backend_state->base;
if (!wlr_scene_output_build_state(output->scene_output, state, &options)) {
ok = false;
goto out;
}
}
ok = wlr_backend_commit(server->backend, states, states_len);
if (!ok) {
goto out;
}
wlr_output_swapchain_manager_apply(&swapchain_manager);
struct wlr_output_configuration_head_v1 *head;
wl_list_for_each (head, &config->heads, link) {
struct cg_output *output = head->state.output->data;
if (head->state.enabled) {
output_layout_add(output, head->state.x, head->state.y);
} else {
output_layout_remove(output);
}
}
out:
wlr_output_swapchain_manager_finish(&swapchain_manager);
for (size_t i = 0; i < states_len; i++) {
wlr_output_state_finish(&states[i].base);
}
free(states);
return ok;
}
void
handle_output_manager_apply(struct wl_listener *listener, void *data)
{
struct cg_server *server = wl_container_of(listener, server, output_manager_apply);
struct wlr_output_configuration_v1 *config = data;
if (output_config_apply(server, config, false)) {
wlr_output_configuration_v1_send_succeeded(config);
} else {
wlr_output_configuration_v1_send_failed(config);
}
wlr_output_configuration_v1_destroy(config);
}
void
handle_output_manager_test(struct wl_listener *listener, void *data)
{
struct cg_server *server = wl_container_of(listener, server, output_manager_test);
struct wlr_output_configuration_v1 *config = data;
if (output_config_apply(server, config, true)) {
wlr_output_configuration_v1_send_succeeded(config);
} else {
wlr_output_configuration_v1_send_failed(config);
}
wlr_output_configuration_v1_destroy(config);
}

View file

@ -1,30 +1,29 @@
#ifndef CG_OUTPUT_H
#define CG_OUTPUT_H
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_damage.h>
#include "seat.h"
#include "server.h"
#include "view.h"
struct cg_output {
struct cg_server *server;
struct wlr_output *wlr_output;
struct wlr_output_damage *damage;
struct wlr_scene_output *scene_output;
struct wl_listener mode;
struct wl_listener transform;
struct wl_listener commit;
struct wl_listener request_state;
struct wl_listener destroy;
struct wl_listener damage_frame;
struct wl_listener damage_destroy;
struct wl_listener frame;
struct wl_list link; // cg_server::outputs
};
void handle_output_manager_apply(struct wl_listener *listener, void *data);
void handle_output_manager_test(struct wl_listener *listener, void *data);
void handle_output_layout_change(struct wl_listener *listener, void *data);
void handle_new_output(struct wl_listener *listener, void *data);
void output_damage_view_surface(struct cg_output *output, struct cg_view *view);
void output_damage_view_whole(struct cg_output *cg_output, struct cg_view *view);
void output_damage_drag_icon(struct cg_output *output, struct cg_drag_icon *icon);
void output_set_window_title(struct cg_output *output, const char *title);
#endif

621
seat.c

File diff suppressed because it is too large Load diff

34
seat.h
View file

@ -1,7 +1,7 @@
#ifndef CG_SEAT_H
#define CG_SEAT_H
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_input_device.h>
@ -20,24 +20,26 @@ struct cg_seat {
struct wl_listener destroy;
struct wl_list keyboards;
struct wl_list keyboard_groups;
struct wl_list pointers;
struct wl_list touch;
struct wl_listener new_input;
struct wlr_cursor *cursor;
struct wlr_xcursor_manager *xcursor_manager;
struct wl_listener cursor_motion;
struct wl_listener cursor_motion_relative;
struct wl_listener cursor_motion_absolute;
struct wl_listener cursor_button;
struct wl_listener cursor_axis;
struct wl_listener cursor_frame;
int32_t touch_id;
double touch_x;
double touch_y;
double touch_lx;
double touch_ly;
struct wl_listener touch_down;
struct wl_listener touch_up;
struct wl_listener touch_motion;
struct wl_listener touch_frame;
struct wl_list drag_icons;
struct wl_listener request_start_drag;
@ -48,20 +50,19 @@ struct cg_seat {
struct wl_listener request_set_primary_selection;
};
struct cg_keyboard {
struct wl_list link; // seat::keyboards
struct cg_keyboard_group {
struct wlr_keyboard_group *wlr_group;
struct cg_seat *seat;
struct wlr_input_device *device;
struct wl_listener modifiers;
struct wl_listener key;
struct wl_listener destroy;
struct wl_listener modifiers;
struct wl_list link; // cg_seat::keyboard_groups
bool is_virtual;
};
struct cg_pointer {
struct wl_list link; // seat::pointers
struct cg_seat *seat;
struct wlr_input_device *device;
struct wlr_pointer *pointer;
struct wl_listener destroy;
};
@ -69,7 +70,7 @@ struct cg_pointer {
struct cg_touch {
struct wl_list link; // seat::touch
struct cg_seat *seat;
struct wlr_input_device *device;
struct wlr_touch *touch;
struct wl_listener destroy;
};
@ -78,15 +79,18 @@ struct cg_drag_icon {
struct wl_list link; // seat::drag_icons
struct cg_seat *seat;
struct wlr_drag_icon *wlr_drag_icon;
double x;
double y;
struct wlr_scene_tree *scene_tree;
/* The drag icon has a position in layout coordinates. */
double lx, ly;
struct wl_listener destroy;
};
struct cg_seat *seat_create(struct cg_server *server);
struct cg_seat *seat_create(struct cg_server *server, struct wlr_backend *backend);
void seat_destroy(struct cg_seat *seat);
struct cg_view *seat_get_focus(struct cg_seat *seat);
void seat_set_focus(struct cg_seat *seat, struct cg_view *view);
void seat_center_cursor(struct cg_seat *seat);
#endif

View file

@ -3,46 +3,71 @@
#include "config.h"
#include <wayland-server.h>
#include <wlr/backend.h>
#include <wlr/types/wlr_idle.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include <wlr/util/log.h>
#if CAGE_HAS_XWAYLAND
#include <wlr/xwayland.h>
#endif
#include "output.h"
#include "seat.h"
#include "view.h"
enum cg_multi_output_mode {
CAGE_MULTI_OUTPUT_MODE_EXTEND,
CAGE_MULTI_OUTPUT_MODE_LAST,
};
struct cg_server {
struct wl_display *wl_display;
struct wlr_backend *backend;
struct wl_list views;
struct wlr_backend *backend;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_session *session;
struct wl_listener display_destroy;
struct cg_seat *seat;
struct wlr_idle *idle;
struct wlr_idle_notifier_v1 *idle;
struct wlr_idle_inhibit_manager_v1 *idle_inhibit_v1;
struct wl_listener new_idle_inhibitor_v1;
struct wl_list inhibitors;
enum cg_multi_output_mode output_mode;
struct wlr_output_layout *output_layout;
struct cg_output *output;
struct wlr_scene_output_layout *scene_output_layout;
struct wlr_scene *scene;
/* Includes disabled outputs; depending on the output_mode
* some outputs may be disabled. */
struct wl_list outputs; // cg_output::link
struct wl_listener new_output;
struct wl_listener output_layout_change;
struct wl_listener xdg_toplevel_decoration;
struct wl_listener new_xdg_shell_surface;
struct wl_listener new_xdg_toplevel;
struct wl_listener new_xdg_popup;
struct wl_listener new_virtual_keyboard;
struct wl_listener new_virtual_pointer;
#if CAGE_HAS_XWAYLAND
struct wl_listener new_xwayland_surface;
#endif
struct wlr_output_manager_v1 *output_manager_v1;
struct wl_listener output_manager_apply;
struct wl_listener output_manager_test;
struct wlr_relative_pointer_manager_v1 *relative_pointer_manager;
bool xdg_decoration;
enum wl_output_transform output_transform;
#ifdef DEBUG
bool debug_damage_tracking;
#endif
bool allow_vt_switch;
bool return_app_code;
bool terminated;
enum wlr_log_importance log_level;
};
void server_terminate(struct cg_server *server);
#endif

234
view.c
View file

@ -1,20 +1,20 @@
/*
* Cage: A Wayland kiosk.
*
* Copyright (C) 2018-2019 Jente Hidskes
* Copyright (C) 2018-2021 Jente Hidskes
*
* See the LICENSE file accompanying this file.
*/
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-server.h>
#include <wlr/types/wlr_box.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_scene.h>
#include "output.h"
#include "seat.h"
@ -24,96 +24,6 @@
#include "xwayland.h"
#endif
static void
view_child_handle_commit(struct wl_listener *listener, void *data)
{
struct cg_view_child *child = wl_container_of(listener, child, commit);
view_damage_surface(child->view);
}
static void subsurface_create(struct cg_view *view, struct wlr_subsurface *wlr_subsurface);
static void
view_child_handle_new_subsurface(struct wl_listener *listener, void *data)
{
struct cg_view_child *child = wl_container_of(listener, child, new_subsurface);
struct wlr_subsurface *wlr_subsurface = data;
subsurface_create(child->view, wlr_subsurface);
}
void
view_child_finish(struct cg_view_child *child)
{
if (!child) {
return;
}
view_damage_whole(child->view);
wl_list_remove(&child->link);
wl_list_remove(&child->commit.link);
wl_list_remove(&child->new_subsurface.link);
}
void
view_child_init(struct cg_view_child *child, struct cg_view *view, struct wlr_surface *wlr_surface)
{
child->view = view;
child->wlr_surface = wlr_surface;
child->commit.notify = view_child_handle_commit;
wl_signal_add(&wlr_surface->events.commit, &child->commit);
child->new_subsurface.notify = view_child_handle_new_subsurface;
wl_signal_add(&wlr_surface->events.new_subsurface, &child->new_subsurface);
wl_list_insert(&view->children, &child->link);
}
static void
subsurface_destroy(struct cg_view_child *child)
{
if (!child) {
return;
}
struct cg_subsurface *subsurface = (struct cg_subsurface *) child;
wl_list_remove(&subsurface->destroy.link);
view_child_finish(&subsurface->view_child);
free(subsurface);
}
static void
subsurface_handle_destroy(struct wl_listener *listener, void *data)
{
struct cg_subsurface *subsurface = wl_container_of(listener, subsurface, destroy);
struct cg_view_child *view_child = (struct cg_view_child *) subsurface;
subsurface_destroy(view_child);
}
static void
subsurface_create(struct cg_view *view, struct wlr_subsurface *wlr_subsurface)
{
struct cg_subsurface *subsurface = calloc(1, sizeof(struct cg_subsurface));
if (!subsurface) {
return;
}
view_child_init(&subsurface->view_child, view, wlr_subsurface->surface);
subsurface->view_child.destroy = subsurface_destroy;
subsurface->wlr_subsurface = wlr_subsurface;
subsurface->destroy.notify = subsurface_handle_destroy;
wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
}
static void
handle_new_subsurface(struct wl_listener *listener, void *data)
{
struct cg_view *view = wl_container_of(listener, view, new_subsurface);
struct wlr_subsurface *wlr_subsurface = data;
subsurface_create(view, wlr_subsurface);
}
char *
view_get_title(struct cg_view *view)
{
@ -131,67 +41,73 @@ view_is_primary(struct cg_view *view)
}
bool
view_is_transient_for(struct cg_view *child, struct cg_view *parent) {
view_is_transient_for(struct cg_view *child, struct cg_view *parent)
{
return child->impl->is_transient_for(child, parent);
}
void
view_damage_surface(struct cg_view *view)
{
output_damage_view_surface(view->server->output, view);
}
void
view_damage_whole(struct cg_view *view)
{
output_damage_view_whole(view->server->output, view);
}
void
view_activate(struct cg_view *view, bool activate)
{
view->impl->activate(view, activate);
}
static void
view_maximize(struct cg_view *view)
static bool
view_extends_output_layout(struct cg_view *view, struct wlr_box *layout_box)
{
struct cg_output *output = view->server->output;
int output_width, output_height;
wlr_output_transformed_resolution(output->wlr_output, &output_width, &output_height);
view->impl->maximize(view, output_width, output_height);
}
static void
view_center(struct cg_view *view)
{
struct wlr_output *output = view->server->output->wlr_output;
int output_width, output_height;
wlr_output_transformed_resolution(output, &output_width, &output_height);
int width, height;
view->impl->get_geometry(view, &width, &height);
view->x = (output_width - width) / 2;
view->y = (output_height - height) / 2;
return (layout_box->height < height || layout_box->width < width);
}
static void
view_maximize(struct cg_view *view, struct wlr_box *layout_box)
{
view->lx = layout_box->x;
view->ly = layout_box->y;
if (view->scene_tree) {
wlr_scene_node_set_position(&view->scene_tree->node, view->lx, view->ly);
}
view->impl->maximize(view, layout_box->width, layout_box->height);
}
static void
view_center(struct cg_view *view, struct wlr_box *layout_box)
{
int width, height;
view->impl->get_geometry(view, &width, &height);
view->lx = (layout_box->width - width) / 2;
view->ly = (layout_box->height - height) / 2;
if (view->scene_tree) {
wlr_scene_node_set_position(&view->scene_tree->node, view->lx, view->ly);
}
}
void
view_position(struct cg_view *view)
{
if (view_is_primary(view)) {
view_maximize(view);
struct wlr_box layout_box;
wlr_output_layout_get_box(view->server->output_layout, NULL, &layout_box);
if (view_is_primary(view) || view_extends_output_layout(view, &layout_box)) {
view_maximize(view, &layout_box);
} else {
view_center(view);
view_center(view, &layout_box);
}
}
void
view_for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data)
view_position_all(struct cg_server *server)
{
view->impl->for_each_surface(view, iterator, data);
struct cg_view *view;
wl_list_for_each (view, &server->views, link) {
view_position(view);
}
}
void
@ -199,28 +115,24 @@ view_unmap(struct cg_view *view)
{
wl_list_remove(&view->link);
wl_list_remove(&view->new_subsurface.link);
struct cg_view_child *child, *tmp;
wl_list_for_each_safe(child, tmp, &view->children, link) {
child->destroy(child);
}
wlr_scene_node_destroy(&view->scene_tree->node);
view->wlr_surface->data = NULL;
view->wlr_surface = NULL;
}
void
view_map(struct cg_view *view, struct wlr_surface *surface)
{
view->wlr_surface = surface;
struct wlr_subsurface *subsurface;
wl_list_for_each(subsurface, &view->wlr_surface->subsurfaces, parent_link) {
subsurface_create(view, subsurface);
view->scene_tree = wlr_scene_subsurface_tree_create(&view->server->scene->tree, surface);
if (!view->scene_tree) {
wl_resource_post_no_memory(surface->resource);
return;
}
view->scene_tree->node.data = view;
view->new_subsurface.notify = handle_new_subsurface;
wl_signal_add(&view->wlr_surface->events.new_subsurface, &view->new_subsurface);
view->wlr_surface = surface;
surface->data = view;
#if CAGE_HAS_XWAYLAND
/* We shouldn't position override-redirect windows. They set
@ -239,14 +151,6 @@ void
view_destroy(struct cg_view *view)
{
struct cg_server *server = view->server;
bool ever_been_mapped = true;
#if CAGE_HAS_XWAYLAND
if (view->type == CAGE_XWAYLAND_VIEW) {
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
ever_been_mapped = xwayland_view->ever_been_mapped;
}
#endif
if (view->wlr_surface != NULL) {
view_unmap(view);
@ -259,38 +163,20 @@ view_destroy(struct cg_view *view)
if (!empty) {
struct cg_view *prev = wl_container_of(server->views.next, prev, link);
seat_set_focus(server->seat, prev);
} else if (ever_been_mapped) {
/* The list is empty and the last view has been
mapped, so we can safely exit. */
wl_display_terminate(server->wl_display);
}
}
void
view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type,
const struct cg_view_impl *impl)
view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type, const struct cg_view_impl *impl)
{
view->server = server;
view->type = type;
view->impl = impl;
wl_list_init(&view->children);
}
struct cg_view *
view_from_wlr_surface(struct cg_server *server, struct wlr_surface *surface)
view_from_wlr_surface(struct wlr_surface *surface)
{
struct cg_view *view;
wl_list_for_each(view, &server->views, link) {
if (view->wlr_surface == surface) {
return view;
}
}
return NULL;
}
struct wlr_surface *
view_wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y)
{
return view->impl->wlr_surface_at(view, sx, sy, sub_x, sub_y);
assert(surface);
return surface->data;
}

50
view.h
View file

@ -4,10 +4,10 @@
#include "config.h"
#include <stdbool.h>
#include <wayland-server.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_surface.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/box.h>
#if CAGE_HAS_XWAYLAND
#include <wlr/xwayland.h>
#endif
@ -24,14 +24,14 @@ enum cg_view_type {
struct cg_view {
struct cg_server *server;
struct wl_list link; // server::views
struct wl_list children; // cg_view_child::link
struct wlr_surface *wlr_surface;
int x, y;
struct wlr_scene_tree *scene_tree;
/* The view has a position in layout coordinates. */
int lx, ly;
enum cg_view_type type;
const struct cg_view_impl *impl;
struct wl_listener new_subsurface;
};
struct cg_view_impl {
@ -42,49 +42,19 @@ struct cg_view_impl {
void (*activate)(struct cg_view *view, bool activate);
void (*maximize)(struct cg_view *view, int output_width, int output_height);
void (*destroy)(struct cg_view *view);
void (*for_each_surface)(struct cg_view *view, wlr_surface_iterator_func_t iterator,
void *data);
struct wlr_surface *(*wlr_surface_at)(struct cg_view *view, double sx, double sy,
double *sub_x, double *sub_y);
};
struct cg_view_child {
struct cg_view *view;
struct wlr_surface *wlr_surface;
struct wl_list link;
struct wl_listener commit;
struct wl_listener new_subsurface;
void (*destroy)(struct cg_view_child *child);
};
struct cg_subsurface {
struct cg_view_child view_child;
struct wlr_subsurface *wlr_subsurface;
struct wl_listener destroy;
};
char *view_get_title(struct cg_view *view);
bool view_is_primary(struct cg_view *view);
bool view_is_transient_for(struct cg_view *child, struct cg_view *parent);
void view_damage_surface(struct cg_view *view);
void view_damage_whole(struct cg_view *view);
void view_activate(struct cg_view *view, bool activate);
void view_position(struct cg_view *view);
void view_for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data);
void view_position_all(struct cg_server *server);
void view_unmap(struct cg_view *view);
void view_map(struct cg_view *view, struct wlr_surface *surface);
void view_destroy(struct cg_view *view);
void view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type,
const struct cg_view_impl *impl);
void view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type, const struct cg_view_impl *impl);
struct cg_view *view_from_wlr_surface(struct cg_server *server, struct wlr_surface *surface);
struct wlr_surface *view_wlr_surface_at(struct cg_view *view, double sx, double sy,
double *sub_x, double *sub_y);
void view_child_finish(struct cg_view_child *child);
void view_child_init(struct cg_view_child *child, struct cg_view *view, struct wlr_surface *wlr_surface);
struct cg_view *view_from_wlr_surface(struct wlr_surface *surface);
#endif

View file

@ -6,10 +6,11 @@
* See the LICENSE file accompanying this file.
*/
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/types/wlr_box.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
@ -18,21 +19,9 @@
#include "xdg_shell.h"
static void
xdg_decoration_handle_destroy(struct wl_listener *listener, void *data)
xdg_decoration_set_mode(struct cg_xdg_decoration *xdg_decoration)
{
struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, destroy);
wl_list_remove(&xdg_decoration->destroy.link);
wl_list_remove(&xdg_decoration->request_mode.link);
free(xdg_decoration);
}
static void
xdg_decoration_handle_request_mode(struct wl_listener *listener, void *data)
{
struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, request_mode);
enum wlr_xdg_toplevel_decoration_v1_mode mode;
if (xdg_decoration->server->xdg_decoration) {
mode = WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
} else {
@ -42,101 +31,84 @@ xdg_decoration_handle_request_mode(struct wl_listener *listener, void *data)
}
static void
xdg_popup_destroy(struct cg_view_child *child)
xdg_decoration_handle_destroy(struct wl_listener *listener, void *data)
{
if (!child) {
struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, destroy);
wl_list_remove(&xdg_decoration->destroy.link);
wl_list_remove(&xdg_decoration->commit.link);
wl_list_remove(&xdg_decoration->request_mode.link);
free(xdg_decoration);
}
static void
xdg_decoration_handle_commit(struct wl_listener *listener, void *data)
{
struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, commit);
if (xdg_decoration->wlr_decoration->toplevel->base->initial_commit) {
xdg_decoration_set_mode(xdg_decoration);
}
}
static void
xdg_decoration_handle_request_mode(struct wl_listener *listener, void *data)
{
struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, request_mode);
if (xdg_decoration->wlr_decoration->toplevel->base->initialized) {
xdg_decoration_set_mode(xdg_decoration);
}
}
static struct cg_view *
popup_get_view(struct wlr_xdg_popup *popup)
{
while (true) {
if (popup->parent == NULL) {
return NULL;
}
struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(popup->parent);
if (xdg_surface == NULL) {
return NULL;
}
switch (xdg_surface->role) {
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
return xdg_surface->data;
case WLR_XDG_SURFACE_ROLE_POPUP:
popup = xdg_surface->popup;
break;
case WLR_XDG_SURFACE_ROLE_NONE:
return NULL;
}
}
}
static void
popup_unconstrain(struct wlr_xdg_popup *popup)
{
struct cg_view *view = popup_get_view(popup);
if (view == NULL) {
return;
}
struct cg_xdg_popup *popup = (struct cg_xdg_popup *) child;
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->map.link);
wl_list_remove(&popup->unmap.link);
wl_list_remove(&popup->new_popup.link);
view_child_finish(&popup->view_child);
free(popup);
}
struct cg_server *server = view->server;
struct wlr_box *popup_box = &popup->current.geometry;
static void
handle_xdg_popup_map(struct wl_listener *listener, void *data)
{
struct cg_xdg_popup *popup = wl_container_of(listener, popup, map);
view_damage_whole(popup->view_child.view);
}
static void
handle_xdg_popup_unmap(struct wl_listener *listener, void *data)
{
struct cg_xdg_popup *popup = wl_container_of(listener, popup, unmap);
view_damage_whole(popup->view_child.view);
}
static void
handle_xdg_popup_destroy(struct wl_listener *listener, void *data)
{
struct cg_xdg_popup *popup = wl_container_of(listener, popup, destroy);
struct cg_view_child *view_child = (struct cg_view_child *) popup;
xdg_popup_destroy(view_child);
}
static void xdg_popup_create(struct cg_view *view, struct wlr_xdg_popup *wlr_popup);
static void
popup_handle_new_xdg_popup(struct wl_listener *listener, void *data)
{
struct cg_xdg_popup *popup = wl_container_of(listener, popup, new_popup);
struct wlr_xdg_popup *wlr_popup = data;
xdg_popup_create(popup->view_child.view, wlr_popup);
}
static void
popup_unconstrain(struct cg_xdg_popup *popup)
{
struct cg_view *view = popup->view_child.view;
struct wlr_output *output = view->server->output->wlr_output;
int width, height;
wlr_output_effective_resolution(output, &width, &height);
struct wlr_output_layout *output_layout = server->output_layout;
struct wlr_output *wlr_output =
wlr_output_layout_output_at(output_layout, view->lx + popup_box->x, view->ly + popup_box->y);
struct wlr_box output_box;
wlr_output_layout_get_box(output_layout, wlr_output, &output_box);
struct wlr_box output_toplevel_box = {
.x = output->lx - view->x,
.y = output->ly - view->y,
.width = width,
.height = height
.x = output_box.x - view->lx,
.y = output_box.y - view->ly,
.width = output_box.width,
.height = output_box.height,
};
wlr_xdg_popup_unconstrain_from_box(popup->wlr_popup, &output_toplevel_box);
}
static void
xdg_popup_create(struct cg_view *view, struct wlr_xdg_popup *wlr_popup)
{
struct cg_xdg_popup *popup = calloc(1, sizeof(struct cg_xdg_popup));
if (!popup) {
return;
}
popup->wlr_popup = wlr_popup;
view_child_init(&popup->view_child, view, wlr_popup->base->surface);
popup->view_child.destroy = xdg_popup_destroy;
popup->destroy.notify = handle_xdg_popup_destroy;
wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
popup->map.notify = handle_xdg_popup_map;
wl_signal_add(&wlr_popup->base->events.map, &popup->map);
popup->unmap.notify = handle_xdg_popup_unmap;
wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
popup->new_popup.notify = popup_handle_new_xdg_popup;
wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
popup_unconstrain(popup);
}
static void
handle_new_xdg_popup(struct wl_listener *listener, void *data)
{
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, new_popup);
struct wlr_xdg_popup *wlr_popup = data;
xdg_popup_create(&xdg_shell_view->view, wlr_popup);
wlr_xdg_popup_unconstrain_from_box(popup, &output_toplevel_box);
}
static struct cg_xdg_shell_view *
@ -149,27 +121,26 @@ static char *
get_title(struct cg_view *view)
{
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
return xdg_shell_view->xdg_surface->toplevel->title;
return xdg_shell_view->xdg_toplevel->title;
}
static void
get_geometry(struct cg_view *view, int *width_out, int *height_out)
{
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
struct wlr_box geom;
struct wlr_xdg_surface *xdg_surface = xdg_shell_view->xdg_toplevel->base;
wlr_xdg_surface_get_geometry(xdg_shell_view->xdg_surface, &geom);
*width_out = geom.width;
*height_out = geom.height;
*width_out = xdg_surface->geometry.width;
*height_out = xdg_surface->geometry.height;
}
static bool
is_primary(struct cg_view *view)
{
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
struct wlr_xdg_surface *parent = xdg_shell_view->xdg_surface->toplevel->parent;
/* FIXME: role is 0? */
return parent == NULL; /*&& role == WLR_XDG_SURFACE_ROLE_TOPLEVEL */
struct wlr_xdg_toplevel *parent = xdg_shell_view->xdg_toplevel->parent;
return parent == NULL;
}
static bool
@ -179,14 +150,13 @@ is_transient_for(struct cg_view *child, struct cg_view *parent)
return false;
}
struct cg_xdg_shell_view *_child = xdg_shell_view_from_view(child);
struct wlr_xdg_surface *xdg_surface = _child->xdg_surface;
struct wlr_xdg_toplevel *xdg_toplevel = _child->xdg_toplevel;
struct cg_xdg_shell_view *_parent = xdg_shell_view_from_view(parent);
struct wlr_xdg_surface *parent_xdg_surface = _parent->xdg_surface;
while (xdg_surface) {
if (xdg_surface->toplevel->parent == parent_xdg_surface) {
while (xdg_toplevel) {
if (xdg_toplevel->parent == _parent->xdg_toplevel) {
return true;
}
xdg_surface = xdg_surface->toplevel->parent;
xdg_toplevel = xdg_toplevel->parent;
}
return false;
}
@ -195,15 +165,15 @@ static void
activate(struct cg_view *view, bool activate)
{
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
wlr_xdg_toplevel_set_activated(xdg_shell_view->xdg_surface, activate);
wlr_xdg_toplevel_set_activated(xdg_shell_view->xdg_toplevel, activate);
}
static void
maximize(struct cg_view *view, int output_width, int output_height)
{
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_surface, output_width, output_height);
wlr_xdg_toplevel_set_maximized(xdg_shell_view->xdg_surface, true);
wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_toplevel, output_width, output_height);
wlr_xdg_toplevel_set_maximized(xdg_shell_view->xdg_toplevel, true);
}
static void
@ -214,74 +184,68 @@ destroy(struct cg_view *view)
}
static void
for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data)
{
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
wlr_xdg_surface_for_each_surface(xdg_shell_view->xdg_surface, iterator, data);
}
static struct wlr_surface *
wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y)
{
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
return wlr_xdg_surface_surface_at(xdg_shell_view->xdg_surface, sx, sy, sub_x, sub_y);
}
static void
handle_xdg_shell_surface_request_fullscreen(struct wl_listener *listener, void *data)
handle_xdg_toplevel_request_fullscreen(struct wl_listener *listener, void *data)
{
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_fullscreen);
struct wlr_xdg_toplevel_set_fullscreen_event *event = data;
wlr_xdg_toplevel_set_fullscreen(xdg_shell_view->xdg_surface, event->fullscreen);
/**
* Certain clients do not like figuring out their own window geometry if they
* display in fullscreen mode, so we set it here.
*/
struct wlr_box layout_box;
wlr_output_layout_get_box(xdg_shell_view->view.server->output_layout, NULL, &layout_box);
wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_toplevel, layout_box.width, layout_box.height);
wlr_xdg_toplevel_set_fullscreen(xdg_shell_view->xdg_toplevel,
xdg_shell_view->xdg_toplevel->requested.fullscreen);
}
static void
handle_xdg_shell_surface_commit(struct wl_listener *listener, void *data)
{
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, commit);
struct cg_view *view = &xdg_shell_view->view;
view_damage_surface(view);
}
static void
handle_xdg_shell_surface_unmap(struct wl_listener *listener, void *data)
handle_xdg_toplevel_unmap(struct wl_listener *listener, void *data)
{
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, unmap);
struct cg_view *view = &xdg_shell_view->view;
view_damage_whole(view);
wl_list_remove(&xdg_shell_view->commit.link);
view_unmap(view);
}
static void
handle_xdg_shell_surface_map(struct wl_listener *listener, void *data)
handle_xdg_toplevel_map(struct wl_listener *listener, void *data)
{
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, map);
struct cg_view *view = &xdg_shell_view->view;
xdg_shell_view->commit.notify = handle_xdg_shell_surface_commit;
wl_signal_add(&xdg_shell_view->xdg_surface->surface->events.commit, &xdg_shell_view->commit);
view_map(view, xdg_shell_view->xdg_surface->surface);
view_damage_whole(view);
view_map(view, xdg_shell_view->xdg_toplevel->base->surface);
}
static void
handle_xdg_shell_surface_destroy(struct wl_listener *listener, void *data)
handle_xdg_toplevel_commit(struct wl_listener *listener, void *data)
{
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, commit);
if (!xdg_shell_view->xdg_toplevel->base->initial_commit) {
return;
}
wlr_xdg_toplevel_set_wm_capabilities(xdg_shell_view->xdg_toplevel, XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
/* When an xdg_surface performs an initial commit, the compositor must
* reply with a configure so the client can map the surface. */
view_position(&xdg_shell_view->view);
}
static void
handle_xdg_toplevel_destroy(struct wl_listener *listener, void *data)
{
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, destroy);
struct cg_view *view = &xdg_shell_view->view;
wl_list_remove(&xdg_shell_view->commit.link);
wl_list_remove(&xdg_shell_view->map.link);
wl_list_remove(&xdg_shell_view->unmap.link);
wl_list_remove(&xdg_shell_view->destroy.link);
wl_list_remove(&xdg_shell_view->request_fullscreen.link);
wl_list_remove(&xdg_shell_view->new_popup.link);
xdg_shell_view->xdg_surface = NULL;
xdg_shell_view->xdg_toplevel = NULL;
view_destroy(view);
}
@ -294,19 +258,13 @@ static const struct cg_view_impl xdg_shell_view_impl = {
.activate = activate,
.maximize = maximize,
.destroy = destroy,
.for_each_surface = for_each_surface,
.wlr_surface_at = wlr_surface_at,
};
void
handle_xdg_shell_surface_new(struct wl_listener *listener, void *data)
handle_new_xdg_toplevel(struct wl_listener *listener, void *data)
{
struct cg_server *server = wl_container_of(listener, server, new_xdg_shell_surface);
struct wlr_xdg_surface *xdg_surface = data;
if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
return;
}
struct cg_server *server = wl_container_of(listener, server, new_xdg_toplevel);
struct wlr_xdg_toplevel *toplevel = data;
struct cg_xdg_shell_view *xdg_shell_view = calloc(1, sizeof(struct cg_xdg_shell_view));
if (!xdg_shell_view) {
@ -315,18 +273,105 @@ handle_xdg_shell_surface_new(struct wl_listener *listener, void *data)
}
view_init(&xdg_shell_view->view, server, CAGE_XDG_SHELL_VIEW, &xdg_shell_view_impl);
xdg_shell_view->xdg_surface = xdg_surface;
xdg_shell_view->xdg_toplevel = toplevel;
xdg_shell_view->map.notify = handle_xdg_shell_surface_map;
wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map);
xdg_shell_view->unmap.notify = handle_xdg_shell_surface_unmap;
wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap);
xdg_shell_view->destroy.notify = handle_xdg_shell_surface_destroy;
wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy);
xdg_shell_view->request_fullscreen.notify = handle_xdg_shell_surface_request_fullscreen;
wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &xdg_shell_view->request_fullscreen);
xdg_shell_view->new_popup.notify = handle_new_xdg_popup;
wl_signal_add(&xdg_surface->events.new_popup, &xdg_shell_view->new_popup);
xdg_shell_view->commit.notify = handle_xdg_toplevel_commit;
wl_signal_add(&toplevel->base->surface->events.commit, &xdg_shell_view->commit);
xdg_shell_view->map.notify = handle_xdg_toplevel_map;
wl_signal_add(&toplevel->base->surface->events.map, &xdg_shell_view->map);
xdg_shell_view->unmap.notify = handle_xdg_toplevel_unmap;
wl_signal_add(&toplevel->base->surface->events.unmap, &xdg_shell_view->unmap);
xdg_shell_view->destroy.notify = handle_xdg_toplevel_destroy;
wl_signal_add(&toplevel->events.destroy, &xdg_shell_view->destroy);
xdg_shell_view->request_fullscreen.notify = handle_xdg_toplevel_request_fullscreen;
wl_signal_add(&toplevel->events.request_fullscreen, &xdg_shell_view->request_fullscreen);
toplevel->base->data = xdg_shell_view;
}
static void
popup_handle_destroy(struct wl_listener *listener, void *data)
{
struct cg_xdg_popup *popup = wl_container_of(listener, popup, destroy);
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->commit.link);
wl_list_remove(&popup->reposition.link);
free(popup);
}
static void
popup_handle_commit(struct wl_listener *listener, void *data)
{
struct cg_xdg_popup *popup = wl_container_of(listener, popup, commit);
if (popup->xdg_popup->base->initial_commit) {
popup_unconstrain(popup->xdg_popup);
}
}
static void
popup_handle_reposition(struct wl_listener *listener, void *data)
{
struct cg_xdg_popup *popup = wl_container_of(listener, popup, reposition);
popup_unconstrain(popup->xdg_popup);
}
void
handle_new_xdg_popup(struct wl_listener *listener, void *data)
{
struct cg_server *server = wl_container_of(listener, server, new_xdg_popup);
struct wlr_xdg_popup *wlr_popup = data;
struct cg_view *view = popup_get_view(wlr_popup);
if (view == NULL) {
return;
}
struct wlr_scene_tree *parent_scene_tree = NULL;
struct wlr_xdg_surface *parent = wlr_xdg_surface_try_from_wlr_surface(wlr_popup->parent);
if (parent == NULL) {
return;
}
switch (parent->role) {
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:;
parent_scene_tree = view->scene_tree;
break;
case WLR_XDG_SURFACE_ROLE_POPUP:
parent_scene_tree = parent->data;
break;
case WLR_XDG_SURFACE_ROLE_NONE:
break;
}
if (parent_scene_tree == NULL) {
return;
}
struct cg_xdg_popup *popup = calloc(1, sizeof(*popup));
if (popup == NULL) {
wlr_log(WLR_ERROR, "Failed to allocate popup");
return;
}
popup->xdg_popup = wlr_popup;
popup->destroy.notify = popup_handle_destroy;
wl_signal_add(&wlr_popup->events.destroy, &popup->destroy);
popup->commit.notify = popup_handle_commit;
wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
popup->reposition.notify = popup_handle_reposition;
wl_signal_add(&wlr_popup->events.reposition, &popup->reposition);
struct wlr_scene_tree *popup_scene_tree = wlr_scene_xdg_surface_create(parent_scene_tree, wlr_popup->base);
if (popup_scene_tree == NULL) {
wlr_log(WLR_ERROR, "Failed to allocate scene-graph node for XDG popup");
free(popup);
return;
}
wlr_popup->base->data = popup_scene_tree;
}
void
@ -345,8 +390,8 @@ handle_xdg_toplevel_decoration(struct wl_listener *listener, void *data)
xdg_decoration->destroy.notify = xdg_decoration_handle_destroy;
wl_signal_add(&wlr_decoration->events.destroy, &xdg_decoration->destroy);
xdg_decoration->commit.notify = xdg_decoration_handle_commit;
wl_signal_add(&wlr_decoration->toplevel->base->surface->events.commit, &xdg_decoration->commit);
xdg_decoration->request_mode.notify = xdg_decoration_handle_request_mode;
wl_signal_add(&wlr_decoration->events.request_mode, &xdg_decoration->request_mode);
xdg_decoration_handle_request_mode(&xdg_decoration->request_mode, wlr_decoration);
}

View file

@ -1,7 +1,7 @@
#ifndef CG_XDG_SHELL_H
#define CG_XDG_SHELL_H
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
@ -9,34 +9,33 @@
struct cg_xdg_shell_view {
struct cg_view view;
struct wlr_xdg_surface *xdg_surface;
struct wlr_xdg_toplevel *xdg_toplevel;
struct wl_listener destroy;
struct wl_listener unmap;
struct wl_listener map;
struct wl_listener commit;
struct wl_listener request_fullscreen;
struct wl_listener new_popup;
};
struct cg_xdg_popup {
struct cg_view_child view_child;
struct wlr_xdg_popup *wlr_popup;
struct wl_listener destroy;
struct wl_listener map;
struct wl_listener unmap;
struct wl_listener new_popup;
struct wl_listener map;
struct wl_listener request_fullscreen;
};
struct cg_xdg_decoration {
struct wlr_xdg_toplevel_decoration_v1 *wlr_decoration;
struct cg_server *server;
struct wl_listener destroy;
struct wl_listener commit;
struct wl_listener request_mode;
};
void handle_xdg_shell_surface_new(struct wl_listener *listener, void *data);
struct cg_xdg_popup {
struct wlr_xdg_popup *xdg_popup;
struct wl_listener destroy;
struct wl_listener commit;
struct wl_listener reposition;
};
void handle_new_xdg_toplevel(struct wl_listener *listener, void *data);
void handle_new_xdg_popup(struct wl_listener *listener, void *data);
void handle_xdg_toplevel_decoration(struct wl_listener *listener, void *data);

View file

@ -1,17 +1,16 @@
/*
* Cage: A Wayland kiosk.
*
* Copyright (C) 2018-2019 Jente Hidskes
* Copyright (C) 2018-2020 Jente Hidskes
*
* See the LICENSE file accompanying this file.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/types/wlr_box.h>
#include <wlr/xwayland.h>
#include <wayland-server-core.h>
#include <wlr/util/log.h>
#include <wlr/xwayland.h>
#include "server.h"
#include "view.h"
@ -42,8 +41,15 @@ static void
get_geometry(struct cg_view *view, int *width_out, int *height_out)
{
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
*width_out = xwayland_view->xwayland_surface->surface->current.width;
*height_out = xwayland_view->xwayland_surface->surface->current.height;
struct wlr_xwayland_surface *xsurface = xwayland_view->xwayland_surface;
if (xsurface->surface == NULL) {
*width_out = 0;
*height_out = 0;
return;
}
*width_out = xsurface->surface->current.width;
*height_out = xsurface->surface->current.height;
}
static bool
@ -84,8 +90,9 @@ static void
maximize(struct cg_view *view, int output_width, int output_height)
{
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
wlr_xwayland_surface_configure(xwayland_view->xwayland_surface, 0, 0, output_width, output_height);
wlr_xwayland_surface_set_maximized(xwayland_view->xwayland_surface, true);
wlr_xwayland_surface_configure(xwayland_view->xwayland_surface, view->lx, view->ly, output_width,
output_height);
wlr_xwayland_surface_set_maximized(xwayland_view->xwayland_surface, true, true);
}
static void
@ -95,18 +102,6 @@ destroy(struct cg_view *view)
free(xwayland_view);
}
static void
for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data)
{
wlr_surface_for_each_surface(view->wlr_surface, iterator, data);
}
static struct wlr_surface *
wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y)
{
return wlr_surface_surface_at(view->wlr_surface, sx, sy, sub_x, sub_y);
}
static void
handle_xwayland_surface_request_fullscreen(struct wl_listener *listener, void *data)
{
@ -115,24 +110,12 @@ handle_xwayland_surface_request_fullscreen(struct wl_listener *listener, void *d
wlr_xwayland_surface_set_fullscreen(xwayland_view->xwayland_surface, xwayland_surface->fullscreen);
}
static void
handle_xwayland_surface_commit(struct wl_listener *listener, void *data)
{
struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, commit);
struct cg_view *view = &xwayland_view->view;
view_damage_surface(view);
}
static void
handle_xwayland_surface_unmap(struct wl_listener *listener, void *data)
{
struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, unmap);
struct cg_view *view = &xwayland_view->view;
view_damage_whole(view);
wl_list_remove(&xwayland_view->commit.link);
view_unmap(view);
}
@ -143,17 +126,11 @@ handle_xwayland_surface_map(struct wl_listener *listener, void *data)
struct cg_view *view = &xwayland_view->view;
if (!xwayland_view_should_manage(view)) {
view->x = xwayland_view->xwayland_surface->x;
view->y = xwayland_view->xwayland_surface->y;
view->lx = xwayland_view->xwayland_surface->x;
view->ly = xwayland_view->xwayland_surface->y;
}
xwayland_view->commit.notify = handle_xwayland_surface_commit;
wl_signal_add(&xwayland_view->xwayland_surface->surface->events.commit, &xwayland_view->commit);
xwayland_view->ever_been_mapped = true;
view_map(view, xwayland_view->xwayland_surface->surface);
view_damage_whole(view);
}
static void
@ -162,8 +139,6 @@ handle_xwayland_surface_destroy(struct wl_listener *listener, void *data)
struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, destroy);
struct cg_view *view = &xwayland_view->view;
wl_list_remove(&xwayland_view->map.link);
wl_list_remove(&xwayland_view->unmap.link);
wl_list_remove(&xwayland_view->destroy.link);
wl_list_remove(&xwayland_view->request_fullscreen.link);
xwayland_view->xwayland_surface = NULL;
@ -179,10 +154,28 @@ static const struct cg_view_impl xwayland_view_impl = {
.activate = activate,
.maximize = maximize,
.destroy = destroy,
.for_each_surface = for_each_surface,
.wlr_surface_at = wlr_surface_at,
};
void
handle_xwayland_associate(struct wl_listener *listener, void *data)
{
struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, associate);
struct wlr_xwayland_surface *xsurface = xwayland_view->xwayland_surface;
xwayland_view->map.notify = handle_xwayland_surface_map;
wl_signal_add(&xsurface->surface->events.map, &xwayland_view->map);
xwayland_view->unmap.notify = handle_xwayland_surface_unmap;
wl_signal_add(&xsurface->surface->events.unmap, &xwayland_view->unmap);
}
void
handle_xwayland_dissociate(struct wl_listener *listener, void *data)
{
struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, dissociate);
wl_list_remove(&xwayland_view->map.link);
wl_list_remove(&xwayland_view->unmap.link);
}
void
handle_xwayland_surface_new(struct wl_listener *listener, void *data)
{
@ -198,10 +191,10 @@ handle_xwayland_surface_new(struct wl_listener *listener, void *data)
view_init(&xwayland_view->view, server, CAGE_XWAYLAND_VIEW, &xwayland_view_impl);
xwayland_view->xwayland_surface = xwayland_surface;
xwayland_view->map.notify = handle_xwayland_surface_map;
wl_signal_add(&xwayland_surface->events.map, &xwayland_view->map);
xwayland_view->unmap.notify = handle_xwayland_surface_unmap;
wl_signal_add(&xwayland_surface->events.unmap, &xwayland_view->unmap);
xwayland_view->associate.notify = handle_xwayland_associate;
wl_signal_add(&xwayland_surface->events.associate, &xwayland_view->associate);
xwayland_view->dissociate.notify = handle_xwayland_dissociate;
wl_signal_add(&xwayland_surface->events.dissociate, &xwayland_view->dissociate);
xwayland_view->destroy.notify = handle_xwayland_surface_destroy;
wl_signal_add(&xwayland_surface->events.destroy, &xwayland_view->destroy);
xwayland_view->request_fullscreen.notify = handle_xwayland_surface_request_fullscreen;

View file

@ -1,7 +1,7 @@
#ifndef CG_XWAYLAND_H
#define CG_XWAYLAND_H
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wlr/xwayland.h>
#include "view.h"
@ -9,25 +9,11 @@
struct cg_xwayland_view {
struct cg_view view;
struct wlr_xwayland_surface *xwayland_surface;
/* Some applications that aren't yet Wayland-native or
otherwise "special" (e.g. Firefox Nightly and Google
Chrome/Chromium) spawn an XWayland surface upon startup
that is almost immediately closed again. This makes Cage
think there are no views left, which results in it
exiting. However, after this initial (unmapped) surface,
the "real" application surface is opened. This leads to
these applications' startup sequences being interrupted by
Cage exiting. Hence, to work around this issue, Cage checks
whether an XWayland surface has ever been mapped and exits
only if 1) the XWayland surface has ever been mapped and 2)
this was the last surface Cage manages. */
bool ever_been_mapped;
struct wl_listener destroy;
struct wl_listener associate;
struct wl_listener dissociate;
struct wl_listener unmap;
struct wl_listener map;
struct wl_listener commit;
struct wl_listener request_fullscreen;
};