Compare commits

...

1727 Commits

Author SHA1 Message Date
blank X c5e3ca6249 wlroots 0.15.1
Alexander Orzechowski (1):
       xdg-foreign: Fix crash on destroy of degenerate surface
 
 Isaac Freund (3):
       wlr_texture: remove wlr_texture_from_wl_drm() from header
       foreign-toplevel: send enter if needed on output bind
       tinywl: fix check whether client is focused or not
 
 Kirill Primak (3):
       scene/subsurface_tree: fix handling subsurface destruction
       compositor: damage the whole buffer on viewport src change
       subsurface: unlock cached state on commit if desynced
 
 Simon Ser (4):
       backend: error out in autocreate without libinput support
       scene: schedule an output frame on wl_surface.frame
       scene: try to import buffers as textures before rendering
       build: bump version to 0.15.1
 
 Tadeo Kondrak (1):
       input_method_v2: improve mapping detection
 
 Thomas Hebb (1):
       render/gles2: don't constrain shm formats to ones that support reading
 
 nyorain (1):
       vulkan: Fix imported image layout
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEENP+VJs/vDpejQOLkD9574OiPXkgFAmH8RzEACgkQD9574OiP
 XkjPGw/+KdgxXeVJ1Typ8fp9ZfhGTW3sJVREpMpnLra/1lA4LQ1jUDFfvXEPAveI
 UjgGiyoBpBWcWgj0jS0GgcTE2ExnGlkd9Y6pImM0zUTQ1mQF8hPxyThQiJNDpBix
 42lqWtIqYPQObJnXa19SpebNCw/TbHoF/0sHR8I/a0o2PgVT9LGIsqI1nAYH7H/e
 lUR5paZU5G60lQdePhTIOv7MuXNt7fgtpizw/WNMqz0Cl+1weoIAtXv2UWZM39wW
 hhK2e02n8GncLYQn459xqf5UfkCnie9u6BCL4PMuEm+sOsYH5sQzsUFfQjBsNCqL
 ztgFssPwW6v/vImvWclvD0DAOgPK0kMEg0RWlBOKfTTxBewloKp4MIY/udKX9+5S
 b8ALOizzS0vL/lTFc+o4XBELE/X9f0S0Sv+mtWXD4xuOMREPUaijJ5LyOmDf6gG5
 K/CB5T2pdQqNC9UdTGMPlauSFVqliBO7hyQSHl3tr5/ZjQZ3tGFL+szp7b62Rb5r
 ChQCUC6NvDhcKzZaPwL9py005yk0LT5oEXVxRJId6ipWuGn+XBnBoWWckpy/3Zpu
 cnacvsxyKaQS3Uts13UlVW4ojr3wolDzBqDPQGU8xsqQcbgcbjGLxkPFDIWT++3D
 XzBx6bRBTKr1EJRoEROuSl0xDEaWJlZt1XUGPLP1Uxew4DSrXOg=
 =mFoc
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+M9eMyy2C5F27kiEzBX8gix/YfUFAmIphlUACgkQzBX8gix/
 YfVWfA/8C+GEw9/P2s+eKBdK6jIB9H0as/yjLCDQDmAX2iAIGdnJ+4ojd2C6xDMK
 mUDRU1UOufafXMdaFa7DXeWYGkHII62K3qxdVD737bCRApkBGLVC9h11QuWgcYYC
 BxeduZouZ1npRtTxINBCC5TXKDWkt0cvQDrD7pnL+AH+ny5WvOnRO1CaPXCgnc7v
 hqdrVAtHRdAkYBBHuu9WgMpvVzkS3KXr7NaWLpRdGa7nWpkKX9aoa0tNjCL86TPq
 QHDMhQnPxghxJl6xc9rhDWJYcYCUxJ4W4ABbyqkkP64wBtjnDJVh7LWRx4qnFNIO
 ky0E3JM6A5YZ/N0fdOIsxvJ2YOpqYYXhKS8NJa2a+Wnsw+QEHe8hnST4UWGcQERf
 qX0hghKhuYNxpm4vrB3rOPwbcQIdjGo2jlEH4GG3gENduekvvdGAr5qcXDH+5pol
 7iwrbARI3T6kTplx/iDvHk5wYGKwj5oatXy4AKwbBt+DNDakF6Vf9RGLKOtC2cty
 YS607osq9kUUX47RCSP11W3zb84cAtJNw5eHmQzVPJ+hAlGsaMdDVUbUGgJiruVe
 pyYeSKxKyFTao3SYSizZwJB0mwYXWsDsVdf4c6mbdr3w5MBWslrliCof9vzSQSq3
 KAiGawPiT+ar87kyQMaCaMl0ZLaXG7g/MXKNsRp/Ss437nhknqg=
 =6/3L
 -----END PGP SIGNATURE-----

Merge tag '0.15.1' into dkondor-upstream-pr-2551

wlroots 0.15.1

Alexander Orzechowski (1):
      xdg-foreign: Fix crash on destroy of degenerate surface

Isaac Freund (3):
      wlr_texture: remove wlr_texture_from_wl_drm() from header
      foreign-toplevel: send enter if needed on output bind
      tinywl: fix check whether client is focused or not

Kirill Primak (3):
      scene/subsurface_tree: fix handling subsurface destruction
      compositor: damage the whole buffer on viewport src change
      subsurface: unlock cached state on commit if desynced

Simon Ser (4):
      backend: error out in autocreate without libinput support
      scene: schedule an output frame on wl_surface.frame
      scene: try to import buffers as textures before rendering
      build: bump version to 0.15.1

Tadeo Kondrak (1):
      input_method_v2: improve mapping detection

Thomas Hebb (1):
      render/gles2: don't constrain shm formats to ones that support reading

nyorain (1):
      vulkan: Fix imported image layout
2022-03-10 12:02:12 +07:00
Simon Ser 29938b7425 build: bump version to 0.15.1 2022-02-03 22:19:54 +01:00
Isaac Freund eb1a451803 tinywl: fix check whether client is focused or not
Currently this check is too strict and denies the move/resize request
if a subsurface of the client has pointer focus.

(cherry picked from commit 89dc9a44968fbd3fe8a08a41858d1537ee145668)
2022-02-03 22:15:51 +01:00
Isaac Freund a819c512ec foreign-toplevel: send enter if needed on output bind
Currently the output enter event is never sent if the client has not
yet bound the output, which happens every time the compositor creates a
new output.

To fix this, listen for the output bind event and inform clients as
if needed.

(cherry picked from commit 1bd0ea3a809bdba092ef051120bb6d32f79c0ffb)
2022-02-03 22:15:39 +01:00
Alexander Orzechowski c4824b680a xdg-foreign: Fix crash on destroy of degenerate surface
I am running a custom compiled version of chromium with a patch to get
it up and running on sway git at the moment, and in that development
build I compiled there is a bug where the browser will crash if you
try to open a file select dialog. When this crash happens, chromium will
not close, but instead will remain open and impossible to close unless
you send a SIGKILL signal to the process. However, sway will crash to
tty when you send the SIGKILL.

I have a hunch that when chromium is opening the file select dialog
it is creating some sort of a xdg toplevel surface. But it freezes
before it fully initializes the surface. When the SIGKILL signal is
given, sway/wlroots will try to free the xdg_toplevel surface but
because it hasn't fully initialized due to the frozen window, it
segfaults.

Don't be fooled by the assert, the assert is not firing, the surface
pointer is indeed NULL here.

* thread #1, name = 'sway', stop reason = signal SIGSEGV: invalid address (fault address: 0x28)
    frame #0: 0x00007ffff78b9041 libwlroots.so.11`wlr_xdg_toplevel_set_parent(surface=0x0000000000000000, parent=0x0000000000000000) at wlr_xdg_toplevel.c:159:37
   156
   157 	void wlr_xdg_toplevel_set_parent(struct wlr_xdg_surface *surface,
   158 			struct wlr_xdg_surface *parent) {
-> 159 		assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
   160 		assert(!parent || parent->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
   161
   162 		if (surface->toplevel->parent) {
(lldb) up
error: sway {0x0003442a}: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x67) attribute, but range extraction failed (invalid range list offset 0x67), please file a bug and attach the file at the start of this error message
error: sway {0x0003442a}: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x67) attribute, but range extraction failed (invalid range list offset 0x67), please file a bug and attach the file at the start of this error message
frame #1: 0x00007ffff78e176e libwlroots.so.11`destroy_imported(imported=0x000055555626d570) at wlr_xdg_foreign_v1.c:154:3
   151 		wl_list_for_each_safe(child, child_tmp, &imported->children, link) {
   152 			struct wlr_xdg_surface *xdg_child =
   153 				wlr_xdg_surface_from_wlr_surface(child->surface);
-> 154 			wlr_xdg_toplevel_set_parent(xdg_child, NULL);
   155 		}
   156
   157 		wl_list_remove(&imported->exported_destroyed.link);
(lldb) up
frame #2: 0x00007ffff78e1b9d libwlroots.so.11`xdg_imported_handle_resource_destroy(resource=0x00005555562555a0) at wlr_xdg_foreign_v1.c:280:2
   277 			struct wl_resource *resource) {
   278 		struct wlr_xdg_imported_v1 *imported = xdg_imported_from_resource(resource);
   279 		if (!imported) {
-> 280 			return;
   281 		}
   282
   283 		destroy_imported(imported);
(lldb) up
frame #3: 0x00007ffff794989a libwayland-server.so.0`___lldb_unnamed_symbol211 + 154
libwayland-server.so.0`___lldb_unnamed_symbol211:
->  0x7ffff794989a <+154>: andl   $0x1, %r13d
    0x7ffff794989e <+158>: je     0x7ffff79498b0            ; <+176>
    0x7ffff79498a0 <+160>: addq   $0x8, %rsp
    0x7ffff79498a4 <+164>: movl   $0x1, %eax
(lldb) up
frame #4: 0x00007ffff794fec0 libwayland-server.so.0`___lldb_unnamed_symbol290 + 64
libwayland-server.so.0`___lldb_unnamed_symbol290:
->  0x7ffff794fec0 <+64>: cmpl   $0x1, %eax
    0x7ffff794fec3 <+67>: jne    0x7ffff794fed3            ; <+83>
    0x7ffff794fec5 <+69>: addq   $0x8, %rbx
    0x7ffff794fec9 <+73>: cmpq   %rbx, %r13
(lldb) up
frame #5: 0x00007ffff79503e0 libwayland-server.so.0`___lldb_unnamed_symbol300 + 32
libwayland-server.so.0`___lldb_unnamed_symbol300:
->  0x7ffff79503e0 <+32>: cmpl   $0x1, %eax
    0x7ffff79503e3 <+35>: je     0x7ffff79503f0            ; <+48>
    0x7ffff79503e5 <+37>: popq   %rbx
    0x7ffff79503e6 <+38>: popq   %r12
(lldb) up
frame #6: 0x00007ffff794a30e libwayland-server.so.0`wl_client_destroy + 126
libwayland-server.so.0`wl_client_destroy:
->  0x7ffff794a30e <+126>: movq   %r12, %rdi
    0x7ffff794a311 <+129>: callq  0x7ffff7950150            ; ___lldb_unnamed_symbol293
    0x7ffff794a317 <+135>: movq   0x8(%rbp), %rdi
    0x7ffff794a31b <+139>: callq  *0xdc77(%rip)
(lldb) up
frame #7: 0x00007ffff794a3f7 libwayland-server.so.0`___lldb_unnamed_symbol214 + 119
libwayland-server.so.0`___lldb_unnamed_symbol214:
->  0x7ffff794a3f7 <+119>: movq   0x28(%rsp), %rax
    0x7ffff794a3fc <+124>: subq   %fs:0x28, %rax
    0x7ffff794a405 <+133>: jne    0x7ffff794a727            ; <+935>
    0x7ffff794a40b <+139>: addq   $0x38, %rsp
(lldb) up
frame #8: 0x00007ffff794d1ca libwayland-server.so.0`wl_event_loop_dispatch + 202
libwayland-server.so.0`wl_event_loop_dispatch:
->  0x7ffff794d1ca <+202>: addq   $0xc, %r15
    0x7ffff794d1ce <+206>: cmpq   %r15, %rbp
    0x7ffff794d1d1 <+209>: jne    0x7ffff794d1b8            ; <+184>
    0x7ffff794d1d3 <+211>: movq   0x8(%rsp), %rcx
(lldb) up
frame #9: 0x00007ffff794ad37 libwayland-server.so.0`wl_display_run + 39
libwayland-server.so.0`wl_display_run:
->  0x7ffff794ad37 <+39>: movl   0x8(%rbx), %eax
    0x7ffff794ad3a <+42>: testl  %eax, %eax
    0x7ffff794ad3c <+44>: jne    0x7ffff794ad20            ; <+16>
    0x7ffff794ad3e <+46>: popq   %rbx
(lldb) up
frame #10: 0x000055555557689a sway`server_run(server=0x00005555555f26c0) at server.c:307:2
   304 			wlr_backend_destroy(server->backend);
   305 			return false;
   306 		}
-> 307
   308 		return true;
   309 	}
   310
(lldb) up
frame #11: 0x0000555555575a93 sway`main(argc=3, argv=0x00007fffffffe978) at main.c:431:2
   428 			swaynag_show(&config->swaynag_config_errors);
   429 		}
   430
-> 431 		server_run(&server);
   432
   433 	shutdown:
   434 		sway_log(SWAY_INFO, "Shutting down sway");

(cherry picked from commit cddc1c1bd9f796709c50f4bbb300788edd42fd4f)
2022-02-02 20:29:44 +01:00
Simon Ser ec3780e6ea scene: try to import buffers as textures before rendering
The wlroots APIs currently don't allow importing/uploading a buffer
during rendering operations. Scene-graph buffer nodes need to turn
their wlr_buffer into a wlr_texture at some point. It's not always
possible to do so at wlr_scene_buffer creation time because the
scene-graph may have zero outputs at this point, thus no way to
grab a wlr_renderer.

Instead, add scene-graph buffers to a pending list and try to import
them in wlr_scene_output_commit.

References: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3354
(cherry picked from commit 3db1bcbe641b407b9f5c9e5d0a012b45aa2c6cb7)
2022-02-02 20:28:27 +01:00
Kirill Primak 68c5fa340d subsurface: unlock cached state on commit if desynced
wl_subsurface::set_desync description states: "If cached state exists when
wl_surface.commit is called in desynchronized mode, the pending state is
added to the cached state, and applied as a whole."

This commit reintroduces an implementation of said behavior, previously
removed in 7daf6da9ac05be2cb74c0983e3caee0b21db75d4.

Strictly speaking, this logic isn't fully correct, as the cached state
and the pending state are applied individually instead, if the cached
state isn't locked by anything else. However, the end result is still
the same.

This commit fixes the issue with Firefox permission popups.

(cherry picked from commit 77951968dc9df7214c04c33f4905a9a7aa92f60c)
2022-02-02 20:27:49 +01:00
Kirill Primak 304c61307a compositor: damage the whole buffer on viewport src change
wp_viewporter protocol doesn't seem to say anything about damage, but
Firefox assumes that wp_viewport::set_source alone is enough to damage
the whole surface, and that assumption kinda makes sense, so let's do
that.

(cherry picked from commit da2491d4163e1d8e627d00c8ae594c7f8003472e)
2022-02-02 20:25:28 +01:00
Tadeo Kondrak b0fee56974 input_method_v2: improve mapping detection
Detect NULL commits before the surface is actually committed, allowing
the surface to be properly damaged on unmap.

(cherry picked from commit 5091118bed82394de5a151d658e895bb44059b61)
2022-02-02 20:25:24 +01:00
Kirill Primak 7dde2b66d6 scene/subsurface_tree: fix handling subsurface destruction
This commit renames map/unmap listeners to clarify that they handle
subsurface events, and ensures the node is always destroyed before
the subsurface.

Without this patch, wl_list_remove() would operate on listener links in
already freed memory. glibc is usually lenient to bugs like this, but
musl isn't.

(cherry picked from commit 83ab5055fd36bd0f8a0106257e45d8ed303636d8)
2022-02-02 20:08:20 +01:00
Thomas Hebb ea77cc5cb2 render/gles2: don't constrain shm formats to ones that support reading
commit 44e8451cd9 ("render/gles2: hide shm formats without GL
support") added the is_gles2_pixel_format_supported() function to
render/gles2/pixel_format.c, whose stated purpose is to "check whether
the renderer has the needed GL extensions to read a given pixel format."
It then used that function to filter the pixel formats returned by
get_gles2_shm_formats().

The result of this change is that RGB formats are no longer reported for
GL drivers that don't implement EXT_read_format_bgra, even when those
formats are supported for rendering (which they have to be for
wlr_gles2_renderer_create() to succeed). This is a pretty clear
regression, since wlr_renderer_init_wl_shm() fails when either of
WL_SHM_FORMAT_ARGB8888 or WL_SHM_FORMAT_XRGB8888 are missing.

To fix the regression, change is_gles2_pixel_format_supported() to
accept all pixel formats that support rendering, regardless of whether
we can read them or not, and move the check for EXT_read_format_bgra
back into gles2_read_pixels(). (There's already a check for this
extension in gles2_preferred_read_format(), so we're not breaking any
abstraction that wasn't already broken.)

Tested on the NVIDIA 495.46 proprietary driver, which doesn't support
EXT_read_format_bgra.

Fixes: 44e8451cd9 ("render/gles2: hide shm formats without GL support")
(cherry picked from commit 59b9518f072527ac59593e51df7f5d5331a34f0e)
2022-02-02 20:08:17 +01:00
nyorain a59a957f2b vulkan: Fix imported image layout
(cherry picked from commit 9988eb3378dbc3301059aa9b5e1ff476354cb92b)
2022-02-02 20:08:01 +01:00
Simon Ser df945b665c scene: schedule an output frame on wl_surface.frame
Some clients (e.g. mpv, Firefox) request a new wl_surface.frame
callback without damaging their surface. When this happens,
schedule a new output frame.

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3350
(cherry picked from commit 812951f5bc47f502429406e49f4e24f377b7799b)
2022-02-02 20:07:39 +01:00
Isaac Freund 4b358c2f91 wlr_texture: remove wlr_texture_from_wl_drm() from header
This function was already removed in e5b5592a but it was forgotten to
remove it from the header.

(cherry picked from commit 823476e76ed166762095330a8f51eabc825febff)
2022-02-02 20:07:21 +01:00
Simon Ser 7864f26d73 backend: error out in autocreate without libinput support
The libinput backend is now optional. However, this means that a
user building wlroots without the correct libinput dependencies
will end up with a compositor which doesn't respond to input events.

wlr_backend_autocreate is supposed to return a sensible setup, so in
this case let's just error out and explain what happened. Users can
suppress the check by setting WLR_LIBINPUT_NO_DEVICES=1 (already used
to suppress the zero input device case inside the libinput backend).

Compositors which really want to create a bare DRM backend can easily
create it manually instead of using wlr_backend_autocreate.

(cherry picked from commit ec2845750862cc0b175bef59de4305f6da91960a)
2022-02-02 20:07:07 +01:00
blank X b309e8fc39
Update README.md 2021-12-18 19:47:06 +07:00
Daniel Kondor 80d10b9c98 Alternative fix for #2543
No need for an additional enum value
2021-12-18 19:46:53 +07:00
Simon Ser 9f41627aa1 backend/wayland: add basic linux-dmabuf feedback support
This patch makes it so we bind to zwp_linux_dmabuf_v1 version 4 and
we use it to grab the main device. v4 sends supported formats via a
table so we need to handle this as well.

v4 allows wlroots to remove the requirement for Mesa's internal
wl_drm interface.
2021-12-15 14:34:08 +00:00
Stacy Harper 8e566f716c layer-shell: don't set committed flag if the property didn't change
This fixes configure loop in Sway when clients re-send same properties
on every configure event.

Original issue: https://todo.sr.ht/~mil/sxmo-tickets/413
2021-12-15 16:24:33 +03:00
Isaac Freund 07ccc6e0b3
scene: add wlr_scene_set_presentation()
This helper automates sending presentation feedback to clients based on
the primary output of scene surfaces.
2021-12-14 21:34:02 +01:00
Simon Ser c0b120a30c build: add subproject fallback for libdrm 2021-12-14 14:33:00 +01:00
Simon Ser bedfec94bb backend/drm: use drmCloseBufferHandle
This has been added in [1] and allows us to close buffer handles
without manually calling drmIoctl.

[1]: https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/192
2021-12-14 14:33:00 +01:00
Simon Ser a15c327718 backend/drm: use drmModeFormatModifierBlobIterNext
This avoids open-coding our own logic. The resulting code is more
readable.

References: https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/146
2021-12-14 13:21:09 +00:00
Simon Ser 4377b55292 util/global: remove wl_display arg from wlr_global_destroy_safe
Since [1], we can get the wl_display directly from the wl_global.

[1]: 2b22160fb6
2021-12-14 13:13:50 +00:00
Guido Günther 4c59f7d46a xdg-activation: Allow to submit tokens
Allows the compositor to submit tokens to the pool of
currently active tokens. This can be useful when the
launcher doesn't use or support xdg-activation-v1 by
itself - e.g. when it is X11 based or use gtk_shell1.
2021-12-14 12:01:46 +01:00
David Rosca 31914928d2
seat: Only resend keyboard/pointer enter to focused clients
Otherwise it will send enter events to clients that already
have keyboard/pointer focus.
Notably Qt applications warns about this.
2021-12-14 08:23:53 +01:00
Isaac Freund 1c3e0816f3
scene: fix wlr_scene_send_frame_done() API
This doesn't work if scene outputs are not used as the primary output of
scene surfaces will always be NULL.

Therefore, take a wlr_scene_output instead of separate wlr_scene and
wlr_output arguments and rename the function to
wlr_scene_output_send_frame_done().

The actual behavior of the function is unchanged.
2021-12-13 17:26:22 +01:00
Isaac Freund ad01cdf0b2 tinywl: use wlr_scene_send_frame_done() 2021-12-13 15:21:05 +00:00
Isaac Freund fecde72be3 scene: add wlr_scene_send_frame_done() 2021-12-13 15:21:05 +00:00
Isaac Freund fb1f613510 scene: add primary output to wlr_scene_surface
This allows compositors to avoid sending multiple frame done events
to a surface that is rendered on multiple outputs at once. This may
also be used in the same way for presentation feedback.
2021-12-13 15:21:05 +00:00
Isaac Freund 0215dffba5 scene: send surface enter/leave output events
Co-authored-by: Simon Ser <contact@emersion.fr>
2021-12-13 15:21:05 +00:00
Chris Chamberlain d8ca494558 backend/drm: add wlr_drm_backend_monitor
This helper is responsible for listening for new DRM devices and
create new child DRM backends as necessary.
2021-12-13 14:55:16 +01:00
Chris Chamberlain f6d3efbf4b backend: fix return value of attempt_drm_backend
The multi backend was returned instead of the primary DRM backend.
2021-12-13 14:53:41 +01:00
Simon Ser e3fefda023 output: add support for protocol interface version 4
Two new events are added: name and description. The name is
immutable. The description can be updated on-the-fly.
2021-12-13 12:06:16 +00:00
Kirill Primak 0fcc842291 subsurface: don't add to parent list immediately 2021-12-09 18:26:56 +00:00
Kirill Primak 7964bdae76 surface: fix non-buffer damage handling
This commit fixes the way the damage that doesn't come directly from the
client is handled.
2021-12-09 18:26:56 +00:00
Kirill Primak df7d280343 subsurface: apply position change at the right moment
Subsurface position is considered to be a part of the parent surface's
state, therefore it should be modified when the parent is committed.
2021-12-09 18:26:56 +00:00
Kirill Primak f463ca669a subsurface: simplify and fix parent commit handling 2021-12-09 18:26:56 +00:00
Simon Ser 818fc4a87b Fix incorrect %zd formatting directives
%zd is for ssize_t. For size_t we should use %zu.
2021-12-09 16:42:04 +01:00
Simon Ser 36a2b19485 output: introduce wlr_output_set_name
wlroots picks names for all outputs, but it might be desirable for
compositor to override it.

For instance, Sway will use a headless output as a fallback in
case no outputs are connected. Sway wants to clearly label the
fallback output as such and label "real" headless outputs starting
from HEADLESS-1.
2021-12-09 15:46:46 +01:00
Simon Ser 1fbd13ec79 examples: remove unnecessary partial_dependency() call
The definition of the "drm" dep already calls it.
2021-12-07 16:20:54 +01:00
Simon Ser 90e9d327dd examples: remove unnecessary wlroots deps for clients
These clients don't need wlroots.
2021-12-07 16:20:20 +01:00
Simon Ser 83bdb3ad07 examples/layer-shell: remove wlroots dependency
This is a client example, it shouldn't use a compositor library
like wlroots.
2021-12-07 16:19:26 +01:00
Simon Ser ad28490cf4 build: move wayland-client dep to backend/wayland/
wayland-client isn't really used by wlroots core, so let's move the
dep to where it's needed in the Wayland backend.
2021-12-07 16:11:29 +01:00
Simon Ser c50c4fc5cc linux-dmabuf-v1: add per-surface feedback 2021-12-07 15:18:19 +01:00
Simon Ser 1d8340754b linux-dmabuf-v1: implement v4
Implement a basic version of linux-dmabuf-unstable-v1 version 4.
Only default hints are implemented.

The new wlr_linux_dmabuf_feedback_v1 data structure will allow
compositors to define their own custom hints in the future. This
data structure makes it easy to describe feedback metadata.

It's converted to a "compiled" form suitable for marshalling over
the Wayland socket via feedback_compile.
2021-12-07 15:18:16 +01:00
Simon Ser 77d811a21b render: add wlr_renderer_init_wl_shm
This allows compositors to initialize wl_shm without initializing
other globals like linux-dmabuf.
2021-12-07 15:15:28 +01:00
Kirill Primak c9f3c2b4f7 surface: fix damage transformation 2021-12-06 15:37:36 +00:00
tiosgz ca1af8119c Fix wlr_scene_node_lower_to_bottom
Before this commit, it would keep the node at the top or make it second-
topmost.
2021-12-04 22:22:56 +00:00
Simon Ser efeb8346cf output: drop front_buffer
This lets backends immediately release committed buffers if they
want to.
2021-12-03 14:56:17 +00:00
Simon Ser 45069fb623 screencopy-v1: use wlr_output_event_commit.buffer 2021-12-03 14:56:17 +00:00
Simon Ser 60b7267e18 export-dmabuf-v1: use wlr_output_event_commit.buffer 2021-12-03 14:56:17 +00:00
Simon Ser f016eca97c output: add wlr_output_event_commit.buffer
This allows output commit listeners to access the newly committed
buffer. Currently wlr_output.front_buffer is used but it'll get
removed in the next commit.
2021-12-03 14:56:17 +00:00
Simon Ser 7201aae3d6 render/drm-format-set: add wlr_drm_format_set_intersect
This intersects two DRM format sets. This is useful for implementing
DMA-BUF feedback in compositors, see e.g. the Sway PR [1].

[1]: https://github.com/swaywm/sway/pull/6313
2021-12-03 14:42:41 +00:00
Simon Ser 92080b3a01 readme: update wrapper libraries link
Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3336
2021-12-03 11:45:57 +01:00
Simon Ser 0d32118a80 output: fix modifier stripping
DRM formats with an empty modifier list are invalid. Instead of
emptying the list, reduce it to { INVALID }.

Add a check to make sure the renderer and backend support implicit
modifiers, so that we don't fallback on e.g. Vulkan.

Closes: https://github.com/swaywm/sway/issues/6692
2021-12-02 14:31:16 +00:00
Simon Ser 1bf9676e87 render/egl: improve modifier support detection
Support for EXT_image_dma_buf_import_modifiers doesn't necessarily
indicate support for modifiers. For instance, Mesa will advertise
EXT_image_dma_buf_import_modifiers for all drivers. This is a trick
to allow EGL clients to enumerate supported formats (something
EXT_image_dma_buf_import is missing). For more information, see [1].

Add a new wlr_egl.has_modifiers flag which indicates whether
modifiers are supported. It's set to true if any
eglQueryDmaBufModifiersEXT query returned a non-empty list.

Use that flag to figure out whether the buffer modifier should be
passed to the EGL implementation on import.

[1]: https://github.com/KhronosGroup/EGL-Registry/issues/142
2021-12-02 14:21:51 +00:00
Simon Ser de0bc78319 render/pixman: advertise MOD_INVALID instead of MOD_LINEAR
The backends and allocators use INVALID, but the renderer uses
LINEAR. Running a compositor with WLR_RENDERER=pixman results in:

    00:00:00.744 [types/output/render.c:59] Failed to pick primary buffer format for output 'WL-1'
2021-12-02 14:12:14 +00:00
Simon Ser 051d1ce90e render/egl: add wlr_egl_create_with_context
This allows creating a wlr_egl from an already-existing EGL display
and context. This is useful to allow compositors to choose the exact
EGL initialization parameters.
2021-12-01 14:08:20 +01:00
Simon Ser ffd4a27714 render/egl: store IMG_context_priority in wlr_egl
The next commit will split extension lookup and context
initialization.
2021-12-01 14:03:37 +01:00
Quantum 812ab2e716 Fix uninitialized variable errors in release mode
When using `meson --buildtype=release`, `-Wextra -Werror` is passed.
This includes `-Werror=maybe-uninitialized`, which complains about
the instances fixed in this commit.
2021-12-01 02:39:14 -05:00
Moon Sungjoon 611b9ca843 backend/wayland: improve wayland input device name
Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3332
This makes input device names include it's type name
2021-11-30 22:06:14 +00:00
Isaac Freund a44b2af672
tinywl: use wlr_scene 2021-11-30 22:06:42 +01:00
Simon Ser ba974a4e9f scene: add wlr_scene_get_scene_output
This allows getting a wlr_scene_output from a wlr_output. Since an
output can only be added once to a scene-graph there's no ambiguity.

This is useful for compositors using wlr_scene_attach_output_layout:
the output layout integration automatically creates a scene-graph
output for each wlr_output added to the layout.
2021-11-30 20:16:24 +00:00
Simon Zeni dd84c5a1cc types/wlr_drm_lease_v1: add NULL check to drm lease resource destroy 2021-11-30 19:59:55 +00:00
Simon Ser 697a1cd0f5 output: add wlr_output_get_primary_formats
This allows compositors to get primary formats without manually
calling wlr_output_impl.get_primary_formats.

For example, the Sway patch for linux-dmabuf feedback [1] needs
this.

[1]: https://github.com/swaywm/sway/pull/6313
2021-11-30 20:49:33 +01:00
Simon Ser e93435016e output: fix typo in wlr_output_impl.get_primary_formats docs
The buffer capabilities indicate whether the formats returned are
for DMA-BUFs or shared memory buffers.
2021-11-30 20:49:33 +01:00
Simon Ser 2540de494e output: don't leave dangling cursor_front_buffer
Sometimes we were calling wlr_output_impl.set_cursor with a NULL
buffer, but we weren't clearing wlr_output.cursor_front_buffer.
Avoid leaving a dangling buffer behind.

Introduce a helper function output_set_hardware_cursor which calls
wlr_output_impl.set_cursor and keeps cursor_front_buffer in sync.
2021-11-30 18:32:48 +00:00
Simon Ser 456b971099 output: destroy swapchain when disabled
This avoids consuming GPU memory when an output is disabled.
2021-11-30 18:32:48 +00:00
Rouven Czerwinski 6bfb930aa7 linux-dmabuf-v1: fix implicit check
The implicit check to filter out LINEAR for dmabuf checked for INVALID
twice instead of checking for INVALID & LINEAR. Fix this.

Fixes: d37eb5c2ea ("linux-dmabuf-v1: filter out LINEAR if implicit")
Reported-by: Dawid Czeluśniak <czelusniakdawid@gmail.com>
2021-11-29 17:44:34 +01:00
Simon Ser fbaefd90fc backend/drm: poison buffers which cannot be scanned out
Rather than repeatedly trying to import DMA-BUFs which cannot be
scanned out, mark the failed ones with a special "poison" marker.
Inspired from [1].

[1]: https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/731
2021-11-29 14:32:29 +00:00
John Lindgren bff5b2c559 Insert new outputs at the end of the list
This prevents auto-configuring a new output from changing the
position of existing outputs.

(v2: simplify insert-at-end logic)
2021-11-29 13:14:31 +00:00
Jonathan Wong 0fb479ca61 Added whitespace between "output" and "(not" 2021-11-28 16:56:09 +00:00
Rouven Czerwinski d37eb5c2ea linux-dmabuf-v1: filter out LINEAR if implicit
If only INVALID and LINEAR are valid modifiers, we need to filter out
LINEAR since Xwayland won't be able to allocate a BO with the explicit
linear modifier on hardware that does not support explicit modifiers.
The addition of LINEAR is an internal implementation detail which
simplifies the wlroots architecture for now.

Evntually Xwayland should be fixed to filter out modifiers that are not
supported by the GBM implementation, see [1]. This could be done by
querying EGL for the supported modifiers.

[1]: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166
2021-11-27 19:32:25 +01:00
Simon Ser 254ab890e7 scene: add support for viewporter
If the surface has a source box set, use that.

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3172
2021-11-26 19:31:19 +00:00
Simon Ser 83d78f9fd4 render: add DMA-BUF docs
Document some of the assumptions for DMA-BUF buffer sharing and
modifiers.
2021-11-26 16:40:53 +00:00
Simon Ser ef1669d33e backend/drm: always add LINEAR to supported modifiers 2021-11-26 16:40:53 +00:00
Simon Ser 98f2efde98 render/drm_format_set: remove special LINEAR case
This was used to make the intersection of INVALID and LINEAR result
in LINEAR. We can now just require LINEAR to be in both lists.
2021-11-26 16:40:53 +00:00
Simon Ser d5df8d5cbf render/egl: always add LINEAR to supported modifiers 2021-11-26 16:40:53 +00:00
Simon Ser e163a7cf48 backend/drm: fail on explicit modifier in drmModeAddFB2
drmModeAddFB2 doesn't support explicit modifiers. Only accept INVALID
which indicates an implicit modifier and LINEAR which may indicate
that GBM_BO_USE_LINEAR has been used.
2021-11-26 16:40:53 +00:00
Simon Ser affe9eda57 Require INVALID for implicit format modifiers
See [1] for the motivation.

[1]: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/75
2021-11-26 16:40:53 +00:00
Simon Ser d78cb808b1 render/drm_format_set: add wlr_drm_format_has 2021-11-26 16:40:53 +00:00
Simon Ser 585a908a01 scene: add wlr_scene_xdg_surface_create
This allows compositors to easily add an xdg_surface to the
scene-graph while retaining the ability to unconstraint popups
and decide their final position.

Compositors can handle new popups with the wlr_xdg_shell.new_surface
event, get the parent scene-graph node via wlr_xdg_popup.parent.data,
create a new scene-graph node via wlr_scene_xdg_surface_tree_create,
and unconstraint the popup if they want to.
2021-11-25 18:53:12 +01:00
Simon Zeni 1d3dd7fc08 backend: remove noop backend 2021-11-25 16:49:05 +00:00
Simon Ser b234edcf58 backend/headless: drop wlr_headless_backend_create_with_renderer
The headless backend no longer needs a parent renderer: it no longer
needs to return it in wlr_backend_impl.get_renderer, nor does it
need to return its DRM FD in wlr_backend_impl.get_drm_fd. Drop this
function altogether since it now behaves exactly like
wlr_headless_backend_create.
2021-11-25 16:16:04 +01:00
Simon Ser 2e33139ef7 render: introduce WLR_RENDER_DRM_DEVICE
This env var allows to override the DRM node used by the GLES2 and
Vulkan renderers. It's especially useful to select a DRM node when
running with the headless backend.

References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/2656
2021-11-25 15:12:32 +00:00
Simon Ser f29abe4c77 backend/headless: stop picking a DRM FD
Sometimes the headless backend is used standalone with the Pixman
renderer, sometimes it's used together with another backend which
has already picked a DRM FD. In both of these cases it doesn't make
sense to pick a DRM FD.

Broadly speaking the headless backend doesn't really care which DRM
device is used for the buffers it receives. So it doesn't really
make sense to tie it to a particular DRM device.

Let the backend users (e.g. wlr_renderer_autocreate) open an arbitrary
DRM FD as needed instead.
2021-11-25 15:12:32 +00:00
Simon Ser e4f748c6e9 render/allocator: fallback to renderer DRM FD in autocreate
If the backend doesn't have a DRM FD, fallback to the renderer's.
This accomodates for the situation where the headless backend hasn't
picked a DRM FD in particular, but the renderer has picked one.
2021-11-25 15:12:32 +00:00
Simon Ser bf57825560 render: pick DRM FD in autocreate
If the backend hasn't picked a DRM FD but supports DMA-BUF, pick
an arbitrary render node. This will allow removing the DRM device
selection logic from the headless backend.
2021-11-25 15:12:32 +00:00
Simon Ser bcefb71cf6 docs/env_vars: drop WLR_DIRECT_TTY
The direct session is gone, so this env var isn't looked up
anymore.
2021-11-24 10:57:53 +01:00
Joshua Ashton f132d66816 render/vulkan: Optimize vertex shader
This ends up being a horrible global load:

  s_getpc_b64   s[4:5]                                  // 000000000000: BE841C80
  v_add_u32     v0, s2, v0                              // 000000000004: 68000002
  v_sub_co_u32  v1, vcc, 0, v0                          // 000000000008: 34020080
  v_max_i32     v1, v0, v1                              // 00000000000C: 1A020300
  v_and_b32     v1, 3, v1                               // 000000000010: 26020283
  v_cmp_lt_i32  s[0:1], v0, 0                           // 000000000014: D0C10000 00010100
  v_sub_co_u32  v0, vcc, 0, v1                          // 00000000001C: 34000280
  v_cndmask_b32  v0, v1, v0, s[0:1]                     // 000000000020: D1000000 00020101
  v_lshlrev_b32  v1, 3, v0                              // 000000000028: 24020083
  v_mad_u32_u24  v0, v0, 8, 4                           // 00000000002C: D1C30000 02111100
  v_min_u32     v1, 32, v1                              // 000000000034: 1C0202A0
  v_min_u32     v0, 32, v0                              // 000000000038: 1C0000A0
  s_getpc_b64   s[0:1]                                  // 00000000003C: BE801C00
  s_add_u32     s0, s0, 0x0000003c                      // 000000000040: 8000FF00 0000003C
  s_addc_u32    s1, s1, 0                               // 000000000048: 82018001
  global_load_dword  v1, v[1:2], s[0:1]                 // 00000000004C: DC508000 01000001
  global_load_dword  v0, v[0:1], s[0:1]                 // 000000000054: DC508000 00000000
  v_mov_b32     v2, 0                                   // 00000000005C: 7E040280
  v_mov_b32     v3, 1.0                                 // 000000000060: 7E0602F2
  s_waitcnt     vmcnt(0)                                // 000000000064: BF8C0F70
  exp           pos0, v1, v0, v2, v3 done               // 000000000068: C40008CF 03020001
  exp           param0, off, off, off, off              // 000000000070: C4000200 00000000
  s_endpgm                                              // 000000000078: BF810000
  v_cndmask_b32  v0, s0, v0, vcc                        // 00000000007C: 00000000
  v_cndmask_b32  v0, s0, v0, vcc                        // 000000000080: 00000000
  v_add_f16     v192, s0, v0                            // 000000000084: 3F800000
  v_cndmask_b32  v0, s0, v0, vcc                        // 000000000088: 00000000
  v_add_f16     v192, s0, v0                            // 00000000008C: 3F800000
  v_add_f16     v192, s0, v0                            // 000000000090: 3F800000
  v_cndmask_b32  v0, s0, v0, vcc                        // 000000000094: 00000000
  v_add_f16     v192, s0, v0                            // 000000000098: 3F800000
  v_cndmask_b32  v0, s0, v0, vcc                        // 00000000009C: 00000000

With some bit magic, we can get something much nicer:

  v_add_u32     v0, s2, v0                              // 000000000000: 68000002
  v_add_u32     v1, 1, v0                               // 000000000004: 68020081
  v_and_b32     v1, 2, v1                               // 000000000008: 26020282
  v_cvt_f32_i32  v1, v1                                 // 00000000000C: 7E020B01
  v_mul_f32     v1, 0.5, v1                             // 000000000010: 0A0202F0
  v_and_b32     v0, 2, v0                               // 000000000014: 26000082
  v_cvt_f32_i32  v0, v0                                 // 000000000018: 7E000B00
  v_mul_f32     v0, 0.5, v0                             // 00000000001C: 0A0000F0
  v_mov_b32     v2, 0                                   // 000000000020: 7E040280
  v_mov_b32     v3, 1.0                                 // 000000000024: 7E0602F2
  exp           pos0, v1, v0, v2, v3 done               // 000000000028: C40008CF 03020001
  exp           param0, off, off, off, off              // 000000000030: C4000200 00000000
  s_endpgm                                              // 000000000038: BF810000

The above output was based on just shoving it in ShaderPlayground -- I was not able to use pipeline feedback as I was unable to get RenderDoc working due to the EXT_physical_device_drm requirement.

I additionally considered using >> 1 instead of * 0.5, but AMD has dedicated modifiers to merge a * 0.5, * 2.0, etc in a single instruction. (Albeit, not taken advantage of in the code above, but might with ACO)

Signed-off-by: Joshua Ashton <joshua@froggi.es>
2021-11-23 15:46:24 +00:00
Simon Ser 5332935afc render/vulkan: quiet glslangValidator
This suppresses the output filename printed to stdout. Errors and
warnings should still be printed to stderr as usual.
2021-11-23 15:38:56 +00:00
Simon Ser 1d9c1bcea6 input-device: remove wlr_input_device.link
This field's ownership is unclear: it's in wlr_input_device, but
it's not managed by the common code, it's up to each individual
backend to use it and clean it up.

Since this is a backend implementation detail, move it to the
backend-specific structs.
2021-11-23 14:14:18 +00:00
Isaac Freund c9ba9e82b6
wlr_drag: emit destroy after wl_data_device.leave 2021-11-22 22:43:39 +01:00
Simon Ser 3b93da70a0 backend/wayland: report parent presentation clock
There's no guarantee that the parent Wayland compositor uses
CLOCK_MONOTONIC for reporting presentation timestamps, they could
be using e.g. CLOCK_MONOTONIC_RAW or another system-specific clock.

Forward the value via wlr_backend_impl.get_presentation_clock.

References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3254#note_1143061
2021-11-22 19:31:59 +01:00
Simon Ser 3d73b899ff linux-dmabuf-v1: hide wlr_linux_buffer_params_v1
The parameters are used when the client is in the process of
building a buffer. There's no reason why this internal
implementation detail should be exposed in our public header.
2021-11-22 10:32:55 +01:00
Simon Zeni d70d74ad4f ci/archlinux: enable address and undefined sanitizers 2021-11-19 16:42:14 +00:00
Simon Zeni 52c34e8253 tinywl: build with meson if examples option is enabled 2021-11-19 16:42:14 +00:00
Simon Ser e656697a7d backend/drm: scan leases on uevent
Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3181
2021-11-19 16:06:07 +00:00
Simon Ser 6bb8973309 drm-lease-v1: listen to lease destroy event 2021-11-19 16:06:07 +00:00
Simon Ser 86f5ecf468 backend/drm: introduce wlr_drm_lease
Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3183
2021-11-19 16:06:07 +00:00
Simon Ser a37f538ca0 Introduce WLR_DEVICE_LEASE events
This will allow the DRM backend to reload its lessee list.
2021-11-19 16:06:07 +00:00
Manuel Stoeckl d0bb7df630 output: remove XRGB8888 cursor fallback format
All graphics drivers supporting cursor planes support ARGB8888,
the default cursor format, so this fallback is almost certainly
unused.

Essentially all cursor themes use alpha transparency to make it
clearer where relative to the screen content the cursor hotspot is.
It is better to fall back to a slightly slower software cursor than
it is to fall back to the opaque square that is a hardware cursor
without an alpha channel.
2021-11-19 15:51:46 +00:00
Manuel Stoeckl e879d566bb output: Add function to set preferred render format
This change introduces new double buffered state to the wlr_output,
corresponding to the buffer format to render to.

The format being rendered to does not control the bit depth of colors
being sent to the display; it does generally determine the format with
which screenshot data is provided. The DRM backend _may_ sent higher
bit depths if the render format depth is increased, but hardware and
other limitations may apply.
2021-11-19 15:51:46 +00:00
Manuel Stoeckl 3d7d6ec06f output: use XRGB8888 format instead of ARGB8888
Most (and possibly all) compositors using wlroots only ever render
fully opaque content. To provide better performance, this change
switches the default format used by wlr_output buffers from
ARGB8888 to the opaque XRGB8888.

Compositors like mutter, kwin, and weston already default to
XRGB8888, so this change is unlikely to expose any new bugs in
underlying drivers and hardware.

This does not affect the hardware cursor's buffer format, which is
still ARGB8888 by default.

As part of this change, the X11 backend (which does not support
changing format at runtime) now picks a true color, 24 bit depth
visual (i.e. XRGB8888) instead of a 32 bit depth (ARGB8888) one.
2021-11-19 15:51:46 +00:00
Manuel Stoeckl 7508f87fcb output: lift up output format fallback logic
This makes it possible for the two functions using output_pick_format
(output_pick_cursor_format and output_create_swapchain) to select
different buffer formats.
2021-11-19 15:51:46 +00:00
Simon Zeni ee210758fc tinywl: init output render before commit 2021-11-19 10:44:51 -05:00
Simon Zeni c0fd60be63 backend: fix attempt_backend_by_name multi backend self insertion 2021-11-19 10:44:51 -05:00
Simon Zeni 25bb92faee backend/multi: add asserts in wlr_multi_backend_add 2021-11-19 10:44:45 -05:00
Simon Ser 33eba9080c output: fix renderer buffer cap sanity check in wlr_output_init_render
The backend and renderer don't directly interact together, so there's
no point in checking that their buffer caps intersect. What we want to
check is that:

- The backend and allocator buffer caps are compatible, because the
  backend consumes buffers to display them.
- The renderer and allocator buffer caps are compatible, because the
  renderer imports buffers to sample them or render to them.

For instance, when running with the DRM backend and the Pixman renderer,
the (backend & renderer) check will fail because backend = DMABUF and
renderer = DATA_PTR.
2021-11-19 15:24:07 +01:00
Érico Nogueira e736ebc63c docs: mention WLR_RENDERER=vulkan.
This option was added with commit
8e34692250.
2021-11-19 02:08:51 +00:00
Simon Zeni fdf3169b41 backend: remove wlr_backend_get_renderer 2021-11-18 09:37:57 -05:00
Simon Zeni d1ebd52ab2 backend/multi: remove backend_get_renderer 2021-11-18 09:37:57 -05:00
Simon Zeni 42549a1c9a backend/drm: stop initializing backend renderer 2021-11-18 09:37:57 -05:00
Simon Zeni a143093339 backend/headless: don't store the parent renderer 2021-11-18 09:37:57 -05:00
Simon Zeni 5f11198605 backend/x11: get renderer from wlr_x11_output 2021-11-18 09:37:57 -05:00
Simon Zeni 5a98eae0dc types/wlr_scene: use renderer from wlr_output 2021-11-18 09:37:57 -05:00
Simon Zeni d07c87f668 types/wlr_screencopy_v1: use renderer from output 2021-11-18 09:37:57 -05:00
Simon Zeni 6dc6af1534 backend: remove backend_get_allocator 2021-11-18 09:37:57 -05:00
Simon Zeni 0c76aef202 backend: remove backend ensure renderer and allocator check 2021-11-18 09:37:57 -05:00
Simon Zeni a6538ced35 tinywl: autocreate allocator and init output 2021-11-18 09:37:57 -05:00
Simon Zeni 6d6e70b9e0 examples: init wlr_output with allocator and renderer 2021-11-18 09:37:57 -05:00
Simon Ser 142d10e591 output: add wlr_output_init_render
Co-authored-by: Simon Zeni <simon@bl4ckb0ne.ca>
2021-11-18 09:37:32 -05:00
Demi Marie Obenour b5d4bc3c62 Improve wlr_drm_format documentation
A wlroots user can easily get confused and think that `cap` refers to
wlroots buffer capabilities, not array capacity.
2021-11-17 16:35:20 +00:00
Simon Ser a04cfca4da Remove support for DMA-BUF flags
They are never used in practice, which makes all of our flag
handling effectively dead code. Also, APIs such as KMS don't
provide a good way to deal with the flags. Let's just fail the
DMA-BUF import when clients provide flags.
2021-11-17 16:12:59 +00:00
Simon Ser 9a4e1095ca linux-dmabuf-v1: properly validate flags
We were send a protocol error if INTERLACED or BOTTOM_FIRST was
set. This is incorrect for the zwp_linux_dmabuf_params.create
code-path because this kills the client without allowing it to
gracefully handle the error.

We should only send a protocol error if the client provides a bit
not listed in the protocol definition.
2021-11-17 16:12:59 +00:00
Roman Gilg 8274c85d21 backend/headless: unlink input device on destroy
Removing an input device requires unlinking it from the list of all headless
input devices. For that implement a destroy function.
2021-11-15 12:49:26 +01:00
Raphael Robatsch 4a8e681a5f util/token: don't leak /dev/urandom fd to children
Closes #3324.
2021-11-14 12:30:03 +01:00
Cole Mickens 3a685b10b6
egl: use alts for EGL_EXT_device_enum, if missing 2021-11-09 16:47:32 -08:00
Simon Zeni 02a1ae169e render/allocator: make wlr_allocator part of the public API 2021-11-09 15:26:36 +00:00
Isaac Freund ab16861e86 text-input: fix type of send_preedit_string() args
The protocol uses a signed integer here, which is also what the
wlr_input_method_v2_preedit_string struct provides to compositors from
the input method protocol. Sway currently just passes those int32_t
values directly to this function leading to an implicit conversion.
2021-11-08 19:56:22 +00:00
Simon Ser 76bab68e70 output: drop wlr_output_export_dmabuf
Callers can access output->front_buffer instead.
2021-11-08 15:57:19 +00:00
Simon Ser fa77aeb80e screencopy-v1: stop using wlr_output_export_dmabuf 2021-11-08 15:57:19 +00:00
Simon Ser f20c49d78a export-dmabuf-v1: stop using wlr_output_export_dmabuf 2021-11-08 15:57:19 +00:00
Isaac Freund e326b76959
text-input/input-method: handle strdup() failure 2021-11-07 21:01:24 +01:00
Simon Ser eb5f23d6d0 scene: fix calloc size mismatch 2021-11-07 13:28:23 +01:00
Kirill Primak fc1ed72bdc CONTRIBUTING.md: add CoC section 2021-11-06 17:01:53 +03:00
Isaac Freund 8634dd3e6a
output: fix leak of wlr_drm_format 2021-11-06 13:35:26 +01:00
Simon Ser e13f3f8608 backend/drm: handle per-connector hotplug events
When a connector ID is specified in a hotplug event, don't scan all
connectors. Only scan the connector that has changed.
2021-11-02 13:36:43 +01:00
Simon Ser 2ff4e113e2 backend/session: introduce wlr_device_change_event
This struct contains additional information for session device
change events, such as the DRM connector ID that has changed.
2021-11-02 13:30:51 +01:00
Simon Ser 3e801d68f2 xwayland: add support for -noTouchPointerEmulation
This allows compositors to handle touch pointer emulation manually,
instead of having Xwayland do it [1].

[1]: https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/691
2021-11-02 12:02:51 +01:00
Jan Beich 760e166578 render: completely disable gles2 if requested but libEGL is found
For `required` to disable search the value needs to be of `feature` type.
Checking `gles2` via `in` keyword returns a `bool` but `required: false`
makes the dependency optional instead of disabled.
2021-11-02 09:36:38 +00:00
Simon Ser 8bc1086cac s/GitHub/GitLab/ 2021-11-01 18:54:26 +00:00
Simon Ser d1b75674d4 ci: add .gitlab-ci.yml
This is a glue file to allow integration with builds.sr.ht.
2021-11-01 16:51:18 +01:00
tiosgz cc2ebd9fc0 scene/subsurface_tree: hide unmapped subsurfaces 2021-10-31 17:07:13 +01:00
Ronan Pigott 8e225261f0 backend/wayland: use xdga client activation 2021-10-31 10:33:14 +01:00
Ronan Pigott e2aff8a9b0 xdg-activation-v1: add data field and emit token destroy events
The data field is useful to track metadata about a token. The destroy
events are useful for compositors that track application startup to
let them know they can stop doing that.
2021-10-31 10:33:14 +01:00
Ronan Pigott 6ad0f819e2 xdg-activation-v1: enable compositors to request their own tokens
These new functions allow a compositor to request new managed tokens
without participating in the xdg-activation procedure as a wayland
client.

This enables the compositor itself to behave as a launcher
application.
2021-10-31 10:33:14 +01:00
Simon Ser 83090de034 backend/drm: avoid creating empty FB_DAMAGE_CLIPS prop
drmModeCreatePropertyBlob cannot create zero-sized blobs, that
fails with EINVAL.

Closes: https://github.com/swaywm/wlroots/issues/3297
2021-10-29 15:03:17 +02:00
Simon Ser b2f6ff45c2 output: fix stack variable lifetime in wlr_output_send_present
Variables on the stack are released when the parent block is closed.
Here, `now` is used outside of the `if` block, causing the following
crash when starting Sway with the headless backend:

    ==49606==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7fff94645f90 at pc 0x5558aeae9e29 bp 0x7fff94645df0 sp 0x7fff94645de0
    READ of size 16 at 0x7fff94645f90 thread T0
        #0 0x5558aeae9e28 in handle_present ../sway/desktop/output.c:834
        #1 0x7fdc8d6792fb in wlr_signal_emit_safe ../subprojects/wlroots/util/signal.c:29
        #2 0x7fdc8d54f77f in wlr_output_send_present ../subprojects/wlroots/types/output/output.c:766
        #3 0x7fdc8d524a28 in output_commit ../subprojects/wlroots/backend/headless/output.c:71
        #4 0x7fdc8d54d2db in wlr_output_commit ../subprojects/wlroots/types/output/output.c:629
        #5 0x5558aeb013cb in output_render ../sway/desktop/render.c:1157
        #6 0x5558aeae549e in output_repaint_timer_handler ../sway/desktop/output.c:544
        #7 0x5558aeae5f8a in damage_handle_frame ../sway/desktop/output.c:606
        #8 0x7fdc8d6792fb in wlr_signal_emit_safe ../subprojects/wlroots/util/signal.c:29
        #9 0x7fdc8d6007d5 in output_handle_frame ../subprojects/wlroots/types/wlr_output_damage.c:44
        #10 0x7fdc8d6792fb in wlr_signal_emit_safe ../subprojects/wlroots/util/signal.c:29
        #11 0x7fdc8d54ee84 in wlr_output_send_frame ../subprojects/wlroots/types/output/output.c:720
        #12 0x7fdc8d54efc3 in schedule_frame_handle_idle_timer ../subprojects/wlroots/types/output/output.c:728
        #13 0x7fdc8c9dcf5a in wl_event_loop_dispatch_idle (/usr/lib/libwayland-server.so.0+0xaf5a)
        #14 0x7fdc8c9dcfb4 in wl_event_loop_dispatch (/usr/lib/libwayland-server.so.0+0xafb4)
        #15 0x7fdc8c9dabc6 in wl_display_run (/usr/lib/libwayland-server.so.0+0x8bc6)
        #16 0x5558aeac8e30 in server_run ../sway/server.c:285
        #17 0x5558aeac3c7d in main ../sway/main.c:396
        #18 0x7fdc8be35b24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24)
        #19 0x5558aea8686d in _start (/home/simon/src/sway/build/sway/sway+0x33f86d)
2021-10-29 15:03:29 +03:00
Simon Ser 0817c52a21 backend/drm: get rid of BO handle table
The BO handle table exists to avoid double-closing a BO handle,
which aren't reference-counted by the kernel. But if we can
guarantee that there is only ever a single ref for each BO handle,
then we don't need the BO handle table anymore.

This is possible if we create the handle right before the ADDFB2
IOCTL, and close the handle right after. The handles are very
short-lived and we don't need to track their lifetime.

Because of multi-planar FBs, we need to be a bit careful: some
FB planes might share the same handle. But with a small check, it's
easy to avoid double-closing the same handle (which wouldn't be a
big deal anyways).

There's one gotcha though: drmModeSetCursor2 takes a BO handle as
input. Saving the handles until drmModeSetCursor2 time would require
us to track BO handle lifetimes, so we wouldn't be able to get rid
of the BO handle table. As a workaround, use drmModeGetFB to turn the
FB ID back to a BO handle, call drmModeSetCursor2 and then immediately
close the BO handle. The overhead should be minimal since these IOCTLs
are pretty cheap.

Closes: https://github.com/swaywm/wlroots/issues/3164
2021-10-29 11:38:37 +02:00
Simon Ser 3b96aa04db sceeencopy-v1: listen to output destroy in capture_output
If the output is destroyed after capture_output but before
frame_handle_copy, it'll have a dangling output pointer. Add the
output destroy listener in capture_output.

Closes: https://github.com/swaywm/wlroots/issues/3284
2021-10-29 11:37:59 +02:00
Simon Ser a80f2b2816 scene: inline subsurface_tree_destroy
This is only called from one function.

To destroy the wlr_scene_subsurface_tree from elsewhere, callers
can destroy the scene-graph node returned by
wlr_scene_subsurface_tree_create instead (just like a compositor
would do). subsurface_tree_handle_surface_destroy does exactly this.

Inlining avoids calling subsurface_tree_destroy by mistake.
2021-10-27 16:18:10 +02:00
Haelwenn (lanodan) Monnier a92293a15a backend/drm/legacy.c: Fix memory leak in drm_legacy_crtc_set_gamma
Found via scan-build
2021-10-27 07:30:36 -06:00
Haelwenn (lanodan) Monnier 6666604f17 render/egl.c: Fix memory leaks in egl_create
calloc is moved to right before egl is called to avoid requiring to free()
unused memory.

Found via scan-build
2021-10-27 07:30:36 -06:00
Haelwenn (lanodan) Monnier 4fb652c27f render/pixman/renderer.c: Fix memory-leak in create_buffer
Found via scan-build
2021-10-27 07:30:36 -06:00
MarkusVolk ebe3cfaceb backend.c: do not try to explicitly clean up the libinput backend
Since libinput is an optional dependency the libinput backend is possibly undeclared.
wlr_backend_destroy(backend) below will clean up the child libinput backend if any.
2021-10-27 14:54:01 +02:00
Simon Ser cbedbd0fc0 backend/x11: fix code style in get_touchpoint_from_x11_touch_id 2021-10-27 12:08:09 +02:00
Simon Ser 5619cf368b backend/drm: add entry for Valve EDID vendor
As found in e.g. the Steam Deck.
2021-10-26 08:45:30 -06:00
Simon Ser c43130cb89 readme: refresh dependencies 2021-10-26 15:31:38 +02:00
Simon Ser bf42630d32 output: refuse to enable with zero mode
This can happen if the compositor enables an output without
picking a mode, or performs a modeset with a zero width/height.
2021-10-26 07:01:34 -06:00
Simon Ser 3d6ca9942d xdg-foreign-v2: use error enum
A wayland-protocols patch [1] has added error codes for invalid
surfaces.

[1]: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/57

Closes: https://github.com/swaywm/wlroots/issues/2600
2021-10-25 18:22:11 +03:00
Simon Ser fb393ddf84 output: split into multiple files
This organizes the wlr_output implementation into separate files.
This avoids having a single mega-file with lots of unrelated parts
and makes it more obvious what the interactions between all the
parts are.

No functional changes, just moving code around.
2021-10-23 00:49:24 +02:00
Simon Ser a4ccca0834 scene: add support for direct scan-out
Check if only a single node intersects with the output viewport
and all of its properties match. In this case, attempt direct
scan-out.
2021-10-23 00:49:07 +02:00
Simon Ser db4c93028d scene: add wlr_scene_output_for_each_surface
This allows compositors to more easily implement sending
wl_surface.frame callback done events.
2021-10-22 22:29:35 +02:00
Simon Zeni 70e8277175 backend/multi: implement get_buffer_caps 2021-10-22 22:19:47 +02:00
Simon Ser 7c10a77e0a presentation-time: remove unused field 2021-10-22 06:44:39 -06:00
Kirill Primak 6c3a71d9f6 presentation-time: use a surface addon 2021-10-22 11:57:19 +02:00
buffet 3dc99ed281 Update loc count in readme 2021-10-21 16:37:53 +02:00
Simon Ser 36cf387427 render/vulkan: check vulkan-headers dependency
There's no pkg-config file we can check for sadly, so check
vulkan/vulkan.h as a fallback.

Closes: https://github.com/swaywm/wlroots/issues/3272
2021-10-19 06:51:14 -06:00
Simon Ser f7ea33da0a scene: remove surface commit listener when node is destroyed 2021-10-18 16:25:19 +02:00
Joshua Ashton b62ce3c3c8 render/vulkan: Use image view swizzles instead of shader hack
Signed-off-by: Joshua Ashton <joshua@froggi.es>
2021-10-18 15:57:46 +02:00
Anthony Super e22a386319 Add error handling to backend creation
This commit adds two error-handling cases to the function
attempt_dmr_backend. Specifically:

- In the case where the number of found GPUs is zero, we now
  print a log message indicating this and return a NULL pointer
- In the case where we could not successfully create a backend
  on any GPU, we now log a message indicating this and return
  a NULL pointer

This allows us to provide more descriptive error messages,
as well as avoid a SEGFAULT (the function
`ensure_primary_backend_renderer_and_allocator` dereferences the pointer
given, which could be NULL until this patch) when these cases arise.
2021-10-18 14:36:04 +02:00
nyorain 8e34692250 render/vulkan: add Vulkan renderer
This new renderer is implemented with the existing wlr_renderer API
(which is known to be sub-optimal for some operations). It's not
used by default, but users can opt-in by setting WLR_RENDERER=vulkan.

The renderer depends on VK_EXT_image_drm_format_modifier and
VK_EXT_physical_device_drm.

Co-authored-by: Simon Ser <contact@emersion.fr>
Co-authored-by: Jan Beich <jbeich@FreeBSD.org>
2021-10-18 11:51:13 +02:00
Kirill Primak 2edf468aeb presentation-time: don't send presented on discard 2021-10-15 09:38:58 +02:00
Kirill Primak 2af8cc769a output: add presented flag to presentation event 2021-10-15 09:38:58 +02:00
Kirill Primak 1089b7b8d6 output: disallow NULL event for wlr_output_send_present() 2021-10-15 09:38:58 +02:00
Simon Ser 1b65a80e9d render/allocator: use empty DRM lease to re-open node
This allows us to obtain a new DRM file description without relying
on filesystem permissions.
2021-10-14 21:23:41 +02:00
Isaac Freund 4fae8f7be3 scene: add functions to place node on top/bottom
These are very common operations for compositors (including tinywl)
to perform.
2021-10-14 21:10:03 +02:00
Isaac Freund 2a8d385386 scene: assert that node != sibling in place above/below
Currently these functions remove the node from the scene if the sibling
argument is the same node as the node. To prevent confusion when
misusing this API, assert that the nodes are distinct and document this.
2021-10-14 21:10:03 +02:00
fwsmit dc22a06184 examples/foreign-toplevel: fix toplevel not being freed 2021-10-07 20:36:21 +02:00
Kirill Primak c3e54021f8 xdg-decoration: refactor configure/state flow
The same logic/motivation as xdg-toplevel.
2021-10-07 07:27:51 -06:00
Kirill Primak cdaab82020 layer-shell: move NULL buffer check to role precommit handler
This will allow compositor to access the current buffer before
unmapping.
2021-10-06 10:15:49 +02:00
Kirill Primak 28248dd83b xdg-shell: remove redundant NULL buffer check
This is already checked in role precommit handler, and if the buffer is
NULL due to failed upload, that means the surface was already unmapped.
2021-10-06 10:15:49 +02:00
Jan Beich 31af2b67b0 backend: drop unconditional and unused <libinput.h>
After 70fb21c35b made libinput optional the include prevents
building without libinput package installed.

backend/backend.c:4:10: fatal error: 'libinput.h' file not found
 #include <libinput.h>
          ^~~~~~~~~~~~
2021-10-05 09:11:44 +02:00
Simon Ser 13cdb84ee8 render/allocator: use render node if available in reopen_drm_node
If we aren't trying to create a dumb buffer allocator, and if the
DRM device has a render node (ie, not a split render/display SoC),
then we can use the render node instead of the primary node. This
should allow wlroots to run under seatd when the current user
doesn't have the permission to open primary nodes (logind has a
quirk to allow physically logged in users to open primary nodes).
2021-10-04 12:25:27 +02:00
tiosgz ce66244fd2 surface_at: check if surfaces are mapped 2021-10-02 11:38:40 +02:00
tiosgz 893434b2d4 for_each_surface: only iterate mapped surfaces
These functions are used mostly for rendering, where including unmapped
surfaces is undesired.

This is a breaking change. However, few to no usages will have to be
updated.
2021-10-02 11:38:40 +02:00
Elyes HAOUAS dc3d1530bf Fix spelling errors
Signed-off-by: Elyes HAOUAS <ehaouas@noos.fr>
2021-10-02 10:22:13 +02:00
Simon Ser 323b8498ad Revert "render/drm_format_set: add wlr_drm_format_has"
This reverts commit 833437d592.
2021-10-01 09:26:05 -06:00
Simon Ser 1d7e438d8a Revert "Require INVALID for implicit format modifiers"
This reverts commit ea7357b703.
2021-10-01 09:26:05 -06:00
Simon Ser 61b83441a1 Revert "backend/drm: fail on explicit modifier in drmModeAddFB2"
This reverts commit d6be1d68b7.
2021-10-01 09:26:05 -06:00
Simon Ser 62be833aef Revert "render/egl: always add LINEAR to supported modifiers"
This reverts commit 780052d4da.
2021-10-01 09:26:05 -06:00
Simon Ser 42138a073b Revert "render/drm_format_set: remove special LINEAR case"
This reverts commit 6d281d96cb.
2021-10-01 09:26:05 -06:00
Simon Ser 6d281d96cb render/drm_format_set: remove special LINEAR case
This was used to make the intersection of INVALID and LINEAR result
in LINEAR. We can now just require LINEAR to be in both lists.
2021-10-01 09:21:50 -06:00
Simon Ser 780052d4da render/egl: always add LINEAR to supported modifiers 2021-10-01 09:21:50 -06:00
Simon Ser d6be1d68b7 backend/drm: fail on explicit modifier in drmModeAddFB2
drmModeAddFB2 doesn't support explicit modifiers. Only accept INVALID
which indicates an implicit modifier and LINEAR which may indicate
that GBM_BO_USE_LINEAR has been used.
2021-10-01 09:21:50 -06:00
Simon Ser ea7357b703 Require INVALID for implicit format modifiers
See [1] for the motivation.

[1]: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/75
2021-10-01 09:21:50 -06:00
Simon Ser 833437d592 render/drm_format_set: add wlr_drm_format_has 2021-10-01 09:21:50 -06:00
Simon Ser 744a5c2fef xdg-shell: stop clearing wlr_xdg_surface state on unmap
The protocol doesn't say we should, so let's not.

Also it's pointless to reset scheduled_serial, since 0 is a valid
serial.
2021-09-30 23:17:32 +03:00
Simon Ser 665a164f27 xdg-shell: rename wlr_xdg_surface.next_configure_serial
Rename it to scheduled_serial for consistency with the rest of
wlroots.
2021-09-30 23:17:32 +03:00
Simon Ser 0e34208344 xdg-shell: introduce wlr_xdg_surface.current
This holds the current state, and avoids having ad-hoc fields in
wlr_xdg_surface.
2021-09-30 23:17:32 +03:00
Kirill Primak db4afc2408 xdg-surface: add pending state
struct wlr_xdg_surface_state is introduced to hold the geometry
and configure serial to be applied on next wl_surface.commit.

This commit fixes our handling for ack_configure: instead of making
the request mutate our current state, it mutates the pending state
only.

Co-authored-by: Simon Ser <contact@emersion.fr>
2021-09-30 23:17:32 +03:00
Simon Ser 3d0848daae backend: create renderer and allocator in wlr_backend_autocreate
Instead of ensuring the renderer and allocator are initialized in each
backend, do it in wlr_backend_autocreate. This allows compositors to
create backends without any renderer/allocator if they side-step
wlr_backend_autocreate.

Since the wlr_backend_get_renderer and backend_get_allocator end up
calling wlr_renderer_autocreate and wlr_allocator_autocreate, it sounds
like a good idea to centralize all of the opimionated bits in one place.
2021-09-30 08:50:43 -06:00
José Expósito 20d9448257 wlr_pointer_gestures: hold gestures (protocol v3)
Update the pointer gestures protocol to version 3 allowing to send hold
gestures to clients.
2021-09-27 15:30:31 +02:00
José Expósito 5f3e490c80 wlr_pointer_gestures: update protocol to version 2 2021-09-27 15:30:31 +02:00
José Expósito 4c3e307ec8 cursor: emit hold gesture events
Recevie the hold gesture events from the libinput or Wayland backends,
abstracted as pointer signals, and re-emit them from the cursor
interface.
2021-09-27 15:30:31 +02:00
José Expósito 62e62b6942 backend/wayland: send hold gesture events
Receive hold gesture events using a Wayland listiner and emit the
appropiate wlr_pointer signal.
2021-09-27 15:30:31 +02:00
José Expósito 52d2491931 backend/libinput: send hold gesture events
Receive hold gesture events from libinput and emit the appropiate
wlr_pointer signal.
2021-09-27 15:30:31 +02:00
José Expósito 95970b3619 build: check if libinput supports hold gestures
Add a project argument (LIBINPUT_HAS_HOLD_GESTURES) to allow building
against old versions of libinput.
2021-09-27 15:30:31 +02:00
José Expósito d069a783bc pointer: add hold pointer event definition
As touchpad touches are generally fully abstracted, a client cannot
currently know when a user is interacting with the touchpad without
moving. This is solved by hold gestures.

Hold gestures are notifications about one or more fingers being held
down on the touchpad without significant movement.

Hold gestures are primarily designed for two interactions:

 - Hold to interact: where a hold gesture is active for some time a
   menu could pop up, some object could be selected, etc.
 - Hold to cancel: where e.g. kinetic scrolling is currently active,
   the start of a hold gesture can be used to stop the scroll.

Unlike swipe and pinch, hold gestures, by definition, do not have
movement, so there is no need for an "update" stage in the gesture.

Create two structs, wlr_event_pointer_hold_begin and
wlr_event_pointer_hold_end, to represent hold gesture events and the
signals to emit them: wlr_pointer->pointer.hold_begin/hold_end.
2021-09-27 15:30:31 +02:00
José Expósito fb15538247 protocol/meson: bump wayland-protocols requirement to 1.23 2021-09-27 15:30:31 +02:00
Hubert Hirtz d96d2f5f23 Fix a typo in CONTRIBUTING.md 2021-09-24 16:33:26 +02:00
Kirill Primak 754f40f9cb layer-shell: add `committed` bitmask 2021-09-24 14:38:32 +02:00
Kirill Primak 59fa3637c3 layer-shell: refactor configure/state flow
Same logic as xdg-toplevel.
2021-09-23 21:22:41 +02:00
Simon Ser 3c26244340 scene: add wlr_scene_buffer_set_transform 2021-09-22 10:45:39 -06:00
Simon Ser 43833fba64 scene: add wlr_scene_buffer_set_dest_size 2021-09-22 10:45:39 -06:00
Simon Ser 3d4afbe945 scene: use scene_node_get_size in wlr_scene_node_at
This allows to unify the RECT and BUFFER code-paths. The BUFFER one
will become more complicated with destination size and transforms.
2021-09-22 10:45:39 -06:00
Simon Ser 27b529f8a0 scene: add scene_node_get_size helper 2021-09-22 10:45:39 -06:00
Simon Ser 63040d6744 scene: add wlr_scene_buffer_set_source_box 2021-09-22 10:45:39 -06:00
Simon Ser fdc22449d6 util/box: introduce wlr_fbox_empty
Same as wlr_box_empty, but for wlr_fbox.
2021-09-22 10:45:39 -06:00
Simon Ser 7939bf8cc6 scene: add wlr_scene_buffer
This new scene-graph node displays a wlr_buffer.

Closes: https://github.com/swaywm/wlroots/issues/3161
2021-09-22 10:45:39 -06:00
Simon Ser f6f0e010d1 scene: unify intersection logic in wlr_scene_node_at
Let's extract the common bits.
2021-09-22 10:45:39 -06:00
Simon Ser b25759cd20 scene: drop default case in wlr_scene_node_at
This allows the compiler to error out if we haven't enumerated all
of the cases. This is useful to avoid a missing implementation when
adding a new node type.
2021-09-22 10:45:39 -06:00
Tadeo Kondrak 30d3c76817 Implement input_method_v2 popups 2021-09-22 09:42:14 +02:00
Tadeo Kondrak e0daa65aa6 input-method-unstable-v2: Add error for surface with existing role 2021-09-22 09:42:14 +02:00
Simon Ser 2e12de96ca backend/drm: add support for panel orientation
Expose the panel orientation with wlr_drm_connector_get_panel_orientation.
Leave it to the compositor to consume this information and configure the
output accordingly.

Closes: https://github.com/swaywm/wlroots/issues/1581
2021-09-21 11:40:37 -06:00
Simon Ser 0c5ff5efab build: simplify get_variable calls
With recent-ish Meson we can stop repeating the variable name for
each provider.
2021-09-21 11:35:08 -06:00
Simon Ser 2e590026e9 scene: add wlr_scene_subsurface_tree_create 2021-09-21 16:48:31 +02:00
Simon Ser 597ba2b932 surface: add addon set 2021-09-21 16:48:31 +02:00
Simon Ser 211b3b760e scene: add wlr_scene_tree 2021-09-21 16:48:31 +02:00
Kirill Primak ccc84f11a4 xdg surface: check adding configure_idle for NULL 2021-09-21 10:09:09 +02:00
Kirill Primak 0e2d369106 xdg-surface: simplify configure mechanism
This commit removes any checks whether a configure will change anything
and makes configures be sent unconditionally. Additionally, configures
are scheduled on xdg_toplevel.{un,}set_{maximized,fullscreen} events.
2021-09-21 10:09:09 +02:00
Kirill Primak b72a217fcc xdg-toplevel: refactor configure/state flow
Previously, `wlr_xdg_toplevel` didn't follow the usual "current state +
pending state" pattern and instead had confusingly named
`client_pending` and `server_pending`. This commit removes them, and
instead introduces `wlr_xdg_toplevel.scheduled` to store the properties
that are yet to be sent to a client, and `wlr_xdg_toplevel.requested`
to store the properties that a client has requested. They have different
types to emphasize that they aren't actual states.
2021-09-21 10:09:09 +02:00
Simon Zeni 9579d62a16 types/buffer: make {begin,end}_data_ptr_access part of the public API 2021-09-15 11:50:44 +02:00
Simon Ser 6cb25ebad7 ci: switch to seatd-launch
This removes the need for the ad-hoc loop.

Because udev creates the symlinks in /dev/dri/by-path/, we need to
wait for it to consume all pending events before the chmod call.
Previously the delay needed for seatd to come up was enough to let
udev create the symlinks in time (by chance).
2021-09-14 12:47:19 +02:00
Kirill Primak 52da68b591 xdg popup: move function to a file it belongs to 2021-09-11 12:11:55 +02:00
Guido Günther e479dc1ef0 xwayland: Allow to retrieve startup-id via _NET_STARTUP_INFO
A launchee notifies with a "remove"¹ message when done starting up.
Catch these and forward to the compositor. This allows the compositor to
end the startup sequence that might have been started by another
protocol like xdg-activation.

We don't handle other messages since we expect the launcher to use a
wayland protocol like xdg-activation.

While `_NET_STARTUP_ID` helps to associate toplevels with startup-ids
this signals the end of the startup sequence.

1) https://specifications.freedesktop.org/startup-notification-spec/startup-notification-latest.txt
2021-09-11 09:53:23 +02:00
Simon Ser 4e7a8707cc buffer: add data_ptr access flags
This allows callers to specify the operations they'll perform on
the returned data pointer. The motivations for this are:

- The upcoming Linux MAP_NOSIGBUS flag may only be usable on
  read-only mappings.
- gbm_bo_map with GBM_BO_TRANSFER_READ hurts performance.
2021-09-10 13:16:10 -04:00
Andri Yngvason 105fdecd0c screencopy: Handle shm copy in commit event handler
This simplifies the implementation considerably.
2021-09-10 10:37:18 +02:00
Andri Yngvason 04d234bac1 screencopy: Use output->front_buffer instead of wlr_output_export_dmabuf 2021-09-10 10:37:18 +02:00
Simon Ser a181a37b12 scene: add wlr_scene_attach_output_layout
This is a helper to integrate wlr_scene with wlr_output_layout.
2021-09-09 12:04:35 +02:00
Simon Ser 7832005a1f scene: move source to subdir
This will allow more scene-graph extensions to be added without
cluttering wlr_scene.c, for instance for sub-surface handling and
wlr_output_layout integration.
2021-09-09 12:04:35 +02:00
Quantum 679f5ed966 scene: fix compile error in release builds
On release builds, gcc detects a variable uninitialized error, and generates
a warning, which gets converted to an error due to -Werror.
2021-09-09 10:01:48 +02:00
Simon Ser e05c884891 examples/scene-graph: use wlr_scene_output
This allows us to get damage tracking for free™.
2021-09-08 09:50:08 -04:00
Simon Ser 44f0f7a0a7 scene: add damage tracking support 2021-09-08 09:50:08 -04:00
Simon Ser 9195b77e14 scene: add wlr_scene_node_coords 2021-09-08 09:50:08 -04:00
Simon Ser 04d105760d scene: add wlr_scene_output_commit 2021-09-08 09:50:08 -04:00
Simon Ser 968c1df7e9 scene: add scene outputs
These allow describing an output's viewport inside the scene-graph.
2021-09-08 09:50:08 -04:00
Simon Ser 872993f95d scene: fix wlr_scene_render_output offset
When providing non-zero layout-local coordinates to
wlr_scene_render_output, the viewport should be translated by the
given values. However the viewport was translated by the opposite
values: when giving 42,42 the viewport's position would be set to
-42,-42.
2021-09-08 09:50:08 -04:00
Simon Zeni 3984c81faa types: introduce wlr_drm_lease_v1 2021-09-08 11:09:07 +02:00
Simon Zeni c67e3fe3b7 protocol/meson: bump wayland-protocols requirement to 1.22 2021-09-08 11:09:07 +02:00
Simon Zeni 94ed8f9496 backend/drm: introduce wlr_drm_backend_get_non_master_fd 2021-09-08 11:09:07 +02:00
Simon Zeni e5a949a955 backend/drm: implement drm lease function 2021-09-08 11:09:07 +02:00
Simon Ser 42dba9dc90 backend/drm: drop wlr_drm_connector.desired_mode
Previously used by attempt_enable_needs_modeset, but this has been
dropped in the previous commit.
2021-09-07 22:33:40 +02:00
Simon Ser b01d97a38f backend/drm: drop attempt_enable_needs_modeset
Modesets require a buffer. The DRM backend tried to auto-enable
outputs when a CRTC becomes available in the past, but now that
fails because no buffer is available.

Instead of having this magic inside the DRM backend, a better
approach is to do it in the compositor or in an optional helper.
2021-09-07 22:33:40 +02:00
Simon Ser 04304c322e output-damage: fix damage on modeset
On modeset wlr_output will internally allocate a buffer. The
backend will emit a "mode" output event, then wlr_output will
emit a "commit" event.

wlr_output_damage handles the "mode" event by damaging the whole
output, and then handles the "commit" event. However the commit
event has a buffer, so wlr_output_damage rotates the damage in its
ring buffer, thinking the compositor has rendered a frame. The
compositor hasn't rendered a frame, what wlr_output_damage sees is
the internal wlr_output black buffer used for the modeset.

Let's fix this by damaging the whole output in the "commit" event
handler if the mode has changed. Additionally, damage the whole
output after rotating the damage ring buffer.
2021-09-07 22:27:31 +02:00
muradm 35f0a0d570 backend: wait for session to become active 2021-09-07 20:28:02 +02:00
Simon Ser 0c8fba1a2f backend/drm: drop drm_connector_init_renderer
drm_connector_set_pending_fb already takes care of this.
2021-09-07 15:16:30 +02:00
Simon Ser de1c73021c backend/drm: drop unused arg from get_possible_crtcs
Constify drmModeConnector while at it.
2021-09-07 08:45:08 -04:00
Simon Ser 274c8189d4 backend/drm: fix crash on VT switch
Don't set the MODE flag when disabling a CRTC. This fixes a NULL
pointer dereference in drm_connector_state_init.
2021-09-07 11:34:53 +02:00
Simon Ser 3c74bd0c91 backend/drm: introduce wlr_drm_connector_state
Previously, we were copying wlr_output_state on the stack and
patching it up to be guaranteed to have a proper drmModeModeInfo
stored in it (and not a custom mode). Also, we had a bunch of
helpers deriving DRM-specific information from the generic
wlr_output_state.

Copying the wlr_output_state worked fine so far, but with output
layers we'll be getting a wl_list in there. An empty wl_list stores
two pointers to itself, copying it on the stack blindly results in
infinite loops in wl_list_for_each.

To fix this, rework our DRM backend to stop copying wlr_output_state,
instead add a new struct wlr_drm_connector_state which holds both
the wlr_output_state and additional DRM-specific information.
2021-09-07 11:18:18 +02:00
Simon Ser 3fbf6e02a3 backend/drm: rename enum wlr_drm_connector_state to status
"state" is easily confused with wlr_output_state.
2021-09-07 11:18:18 +02:00
Simon Ser 88919464ef surface: fix abort on NULL buffer attach
Fixes: ba0525c5c0 ("surface: drop wlr_surface_state.buffer_resource")
Closes: https://github.com/swaywm/wlroots/issues/3173
2021-09-07 10:27:46 +02:00
Simon Ser ba0525c5c0 surface: drop wlr_surface_state.buffer_resource
Instead, use wlr_surface_state.buffer only.
2021-09-06 14:21:23 -04:00
Simon Ser 0978a702d7 surface: ensure buffer is reset to NULL in surface_state_move
When surface_state_move processes a NULL commit, make sure to
reset state->buffer to NULL instead of leaving behind an old
wlr_buffer.
2021-09-06 14:21:23 -04:00
Simon Ser 0fe3b45361 viewporter: hide wlr_viewport
This is an internal struct.
2021-09-06 19:19:45 +02:00
Simon Ser 24c397dbf8 viewporter: add doc comment explaining compositor requirements 2021-09-06 19:19:45 +02:00
Simon Ser bb82b6dada buffer: make wlr_client_buffer_apply_damage return a bool
We always return the same wlr_client_buffer as the one passed in,
so no need to return anything.
2021-09-06 18:10:26 +02:00
Simon Ser cbe099dcc7 buffer: take a wlr_buffer in wlr_client_buffer_apply_damage
Instead of taking a wl_resource as argument, take a wlr_buffer.
2021-09-06 18:10:26 +02:00
Kirill Primak 610f0c0805 surface: inline surface_commit_pending() 2021-09-06 17:37:08 +02:00
Kirill Primak cf56596565 surface: change surface_finalize_state() to surface_finalize_pending() 2021-09-06 17:37:08 +02:00
Kirill Primak ba55c7c4ff surface: rename impl and its functions
This makes the naming consistent with other resource implementations.
2021-09-06 17:37:08 +02:00
Kirill Primak 90e62390d9 surface: move subsurface lists to state 2021-09-06 17:37:08 +02:00
Simon Ser 3ac99fa4dc subsurface: move parent link to state
Move the wlr_subsurface parent link to the subsurface state.

This is a dumb find/replace operation. This shouldn't result in
any behavior change.
2021-09-06 17:37:08 +02:00
Simon Ser 56b6b80b9a subsurface: rename wlr_subsurface_state to wlr_subsurface_parent_state
Add a comment to explain the difference.
2021-09-06 17:37:08 +02:00
Kirill Primak 242c23743f surface: cache frame callback lists again
Caching frame callback lists is actually the correct behavior, because
if a surface is locked because of e.g. subsurface synchronization,
clients would expect to receive frame done events only after the
pending state is actually committed.
2021-09-06 17:37:08 +02:00
Simon Ser d290b13871 seat: avoid copying the keymap for each client
We can just send a read-only file descriptor instead.
2021-09-05 22:06:25 +02:00
Simon Ser 62924cc523 keyboard: add wlr_keyboard.keymap_fd
This exposes a read-only FD with the keymap.
2021-09-05 22:06:25 +02:00
Simon Ser 55ca93469c util/shm: add allocate_shm_file_pair
This function behaves like allocate_shm_file, except it also
returns a read-only FD. This is useful to share the same segment
of memory with many Wayland clients.
2021-09-05 22:06:25 +02:00
Simon Ser 38cd1b4f4f render/allocator/gbm: add log message for gbm_bo_get_fd_for_plane
Makes it easier to find out which branch is taken when debugging
issues like [1].

[1]: https://github.com/swaywm/wlroots/issues/3156
2021-09-03 22:13:38 +02:00
Simon Ser 5aa5137fae backend/drm: handle drm_surface_blit errors
drm_surface_blit returns NULL on error. This can happen e.g. when
the source buffer cannot be imported into EGL.

Closes: https://github.com/swaywm/wlroots/issues/3154
2021-09-03 17:57:38 +02:00
Simon Ser 7df2ae88fa render/allocator: use legacy authentication for primary nodes
Closes: https://github.com/swaywm/wlroots/issues/3156
2021-09-03 11:44:12 -04:00
Devin J. Pohly 00c2bae1d3 scene: remove redundant empty-region check in render_texture() 2021-09-02 19:05:02 +02:00
Devin J. Pohly e2e68ff680 examples/scene-graph: demonstrate scene_rect node type
Add RECT nodes to the scene-graph demo to illustrate how they are used.
Here, we add a solid rectangle behind each surface as a quick-and-dirty
border, handling surface.commit in order to size it appropriately.
2021-09-02 19:05:02 +02:00
Devin J. Pohly 9ed16e39fa scene: replace surface_at() with node_at()
With the addition of a non-surface node type, it was unclear how such
nodes should interact with scene_node_surface_at().  For example, if the
topmost node at the given point is a RECT, should the function treat
that node as transparent and continue searching, or as opaque and return
(probably) NULL?

Instead, replace the function with one returning a scene_node, which
will allow for more consistent behavior across different node types.
Compositors can downcast scene_surface nodes via the now-public
wlr_scene_surface_from_node() if they need access to the surface itself.
2021-09-02 19:05:02 +02:00
Devin J. Pohly b7cd06e8fa scene: add RECT node type
RECT is a solid-colored rectangle, useful for simple borders or other
decoration.  This can be rendered directly using the wlr_renderer,
without needing to create a surface.
2021-09-02 19:05:02 +02:00
Devin J. Pohly 526652a554 scene: iterate nodes instead of surfaces when rendering
This will allow us to create node types which are rendered but not
surface-based, such as a solid color or image.
2021-09-02 19:05:02 +02:00
Simon Ser b0972a94c3 contributing: don't reference issues in commit first line
Commits named "Fix #XXX" make it pretty complicated to figure out
exactly what the commit is doing from the shortlog. A better place
for issue references is in the extended commit message.
2021-09-02 09:31:53 -04:00
Simon Ser 267eb02c31 build: add subproject fallback for wayland-protocols
Depends on [1].

[1]: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/84
2021-09-01 15:51:03 -04:00
Simon Ser d9523faa76 build: add subproject fallback for wayland 2021-09-01 15:51:03 -04:00
Simon Ser ee6c841d47 render/allocator/gbm: fix create() docs for FD ownership
Fixes: d9d8fc1ab9 ("render/allocator: re-open GBM FD")
2021-09-01 21:34:34 +02:00
Simon Ser d9d8fc1ab9 render/allocator: re-open GBM FD
Using the same DRM file description for the DRM backend and for the
GBM allocator will result in GEM handle ref'counting issues [1].
Re-open the DRM FD to fix these issues.

[1]: https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/110
2021-09-01 15:17:05 -04:00
Simon Ser c8d97e2791 Link to gamja for web chat
gamja offers a better experience than Kiwi.
2021-08-30 11:10:11 -04:00
Devin J. Pohly 7ec9523ea3 scene: stricter assertions on reparent
For consistency with the rest of the scene-graph API, prevent detaching
a subtree by giving NULL for the new parent, and don't allow ROOT nodes
to be grafted into another tree.
2021-08-30 16:43:18 +02:00
Devin J. Pohly d5263be355 scene: make graph loops fatal when debugging 2021-08-30 16:43:18 +02:00
Devin J. Pohly 0f534e32e4 scene: ensure node cannot be reparented below itself 2021-08-30 16:43:18 +02:00
Devin J. Pohly a1d462fa81 scene: add node reparent function
If nodes are arranged in a tree rather than at a single level, then it
makes sense that there should be a way to move them to a completely
different parent in addition to moving up or down among siblings.
2021-08-30 16:43:18 +02:00
Simon Ser b18c254e5f contributing: turn remaining links into refs 2021-08-30 16:36:14 +02:00
Simon Ser 1ad3cd7f36 contributing: clone wlroots fork with SSH
HTTPS URLs aren't writable.
2021-08-30 10:31:51 -04:00
Simon Ser ea800b7418 contributing: use references for links
This avoids cluttering the prose with URLs.
2021-08-30 10:31:51 -04:00
Simon Ser 2ddd8e8036 contributing: add new section about commit log
This is shamelessly stolen from Weston [1].

It's been a while we've transitioned away from merge commits and
work-style commit history, so it'd be nice to mention this in the
docs.

[1]: https://gitlab.freedesktop.org/wayland/weston/-/blob/main/CONTRIBUTING.md#formatting-and-separating-commits
2021-08-30 10:29:33 -04:00
Tudor Brindus bfc69decdd xwm: do not restack surfaces on activation
Currently, upon activating a surface, wlroots restacks it on top of all
others.

This may not necessarily be correct from the calling compositor's point
of view, where having focus may not imply being top-of-stack (e.g.,
focusing a window under an always-on-top window).

In Sway's case, this means that focused tiling windows will always be on
top of floating windows, at least in the order communicated to X11 apps.
This breaks drag-and-drop from a focused tiling X11 window to a floating
X11 window which partially obscures the former.

This is a breaking change; to retain the previous behavior, users that
were calling

  wlr_xwayland_surface_activate(xsurface, true);

should now be calling

  wlr_xwayland_surface_activate(xsurface, true);
  wlr_xwayland_surface_restack(xsurface, NULL, XCB_STACK_MODE_ABOVE);
2021-08-28 22:04:01 +02:00
Simon Ser e4d0ec9ee1 scene: add wlr_scene_node_surface_at 2021-08-26 21:12:28 +02:00
Simon Ser 501b29db03 scene: add user data pointer to wlr_scene_node
This allows compositors to attach arbitrary data to the scene-graph
nodes.
2021-08-26 21:12:28 +02:00
Simon Ser 97954154bc scene: allow nodes to have arbitrary parents
For instance, allow a surface node to be a child of another surface
node. This allows easier xdg-popup handling in compositors.
2021-08-26 21:12:28 +02:00
Simon Ser 86e9309808 scene: add wlr_scene_node_toggle
This allows compositors to easily enable or disable a scene-graph node.
This can be used to show/hide a surface when the xdg_surface is
mapped/unmapped.
2021-08-26 21:12:28 +02:00
Simon Ser c41bd320be examples/scene-graph: new example 2021-08-26 21:12:28 +02:00
Simon Ser c7d489b5b6 Introduce new scene-graph API
A new wlr_scene API has been added, following the design ideas from [1].
The new API contains the minimal set of features required to make the
API useful. The goal is to design a solid fundation and add more
features in the future.

[1]: https://github.com/swaywm/wlroots/issues/1826#issuecomment-564601757
2021-08-26 21:12:28 +02:00
Kirill Primak 5f645598d8 subsurface: unlock surface on destroy 2021-08-25 19:40:25 +02:00
Simon Ser 5dfaf5ea9c backend/drm: introduce wlr_drm_bo_handle_table
Using GBM to import DRM dumb buffers tends to not work well. By
using GBM we're calling some driver-specific functions in Mesa.
These functions check whether Mesa can work with the buffer.
Sometimes Mesa has requirements which differ from DRM dumb buffers
and the GBM import will fail (e.g. on amdgpu).

Instead, drop GBM and use drmPrimeFDToHandle directly. But there's
a twist: BO handles are not ref'counted by the kernel and need to
be ref'counted in user-space [1]. libdrm usually performs this
bookkeeping and is used under-the-hood by Mesa.

We can't re-use libdrm for this task without using driver-specific
APIs. So let's just re-implement the ref'counting logic in wlroots.
The wlroots implementation is inspired from amdgpu's in libdrm [2].

Closes: https://github.com/swaywm/wlroots/issues/2916

[1]: https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/110
[2]: 1a4c0ec9ae/amdgpu/handle_table.c
2021-08-25 10:05:37 -04:00
Simon Ser 749b3c00f0 render/egl: reopen DRM node for GBM
This will be necessary for the next patch, to avoid messing up BO
handles shared between the GL implementation and the DRM backend.
2021-08-25 10:05:37 -04:00
Simon Ser 3ce2ea9e16 Move allocator stuff into new directory
Add render/allocator/ and include/render/allocator/ to hold
everything allocator-related.
2021-08-25 09:57:20 -04:00
Simon Ser b37731cdbb backend/{drm,libinput}: exclude headers when disabled 2021-08-25 09:50:27 -04:00
Simon Ser 65c0ab00b6 backend/drm: generate CVT reduced modes
The Coordinated Video Timings (CVT) spec [1] defines two types of
timings: the "CVT standard CRT" timings and the "CVT reduced blanking"
timings (see section 3.6).

The standard CRT timings include pauses in the video stream to allow
CRT displays to reposition their electron beam at the end of each
horizontal scan line [2]. While this was desirable a few decades ago,
nowadays we can just generate a CVT reduced blanking timing by default.
wlroots users can still set full custom DRM modes via
wlr_drm_connector_add_mode.

[1]: https://glenwing.github.io/docs/VESA-CVT-1.2.pdf
[2]: https://en.wikipedia.org/wiki/Coordinated_Video_Timings#Reduced_blanking
2021-08-25 08:37:35 +02:00
Kirill Primak 72a156b18a surface: fix wlr_surface_get_buffer_source_box() 2021-08-24 10:36:06 +02:00
Kirill Primak 664307f968 util/box: introduce wlr_fbox_transform()
A floating-point version of wlr_box_transform().
2021-08-24 10:36:06 +02:00
Isaac Freund f2f3df9fb1 tinywl: simplify logic for sending pointer events 2021-08-20 16:32:28 +02:00
Guido Günther de1522aeee xwayland: Allow to retrieve _NET_STARTUP_ID
This is use for startup notifications per startup-notifiation spec

https://specifications.freedesktop.org/startup-notification-spec/startup-notification-latest.txt
2021-08-20 10:44:22 +02:00
Rouven Czerwinski 9b7803a9b3 backend/drm: try to allocate crtc for formats
To retrieve the formats, an allocated crtc is required. If there is no
currently no crtc available, try to allocate it. This reproducable by
having a disabled output and going through a suspend cycle with amdgpu.
On start CRTCs look like this:

  00:00:00.588 [DEBUG] [wlr] [backend/drm/drm.c:1099] Reallocating CRTCs
  00:00:00.588 [DEBUG] [wlr] [backend/drm/drm.c:1110] State before reallocation:
  00:00:00.588 [DEBUG] [wlr] [backend/drm/drm.c:1116]   'DP-1' crtc=0 state=1 desired_enabled=1
  00:00:00.588 [DEBUG] [wlr] [backend/drm/drm.c:1116]   'DP-2' crtc=1 state=1 desired_enabled=1
  00:00:00.588 [DEBUG] [wlr] [backend/drm/drm.c:1116]   'HDMI-A-1' crtc=-1 state=0 desired_enabled=0
  00:00:00.588 [DEBUG] [wlr] [backend/drm/drm.c:1116]   'HDMI-A-2' crtc=-1 state=0 desired_enabled=0
  00:00:00.588 [DEBUG] [wlr] [backend/drm/drm.c:1116]   'DVI-D-1' crtc=-1 state=0 desired_enabled=0
  00:00:00.588 [DEBUG] [wlr] [backend/drm/drm.c:1167] State after reallocation:
  00:00:00.588 [DEBUG] [wlr] [backend/drm/drm.c:1174]   'DP-1' crtc=0 state=1 desired_enabled=1
  00:00:00.588 [DEBUG] [wlr] [backend/drm/drm.c:1174]   'DP-2' crtc=1 state=1 desired_enabled=1
  00:00:00.588 [DEBUG] [wlr] [backend/drm/drm.c:1174]   'HDMI-A-1' crtc=-1 state=0 desired_enabled=0
  00:00:00.588 [DEBUG] [wlr] [backend/drm/drm.c:1174]   'HDMI-A-2' crtc=-1 state=0 desired_enabled=0
  00:00:00.588 [DEBUG] [wlr] [backend/drm/drm.c:1174]   'DVI-D-1' crtc=-1 state=0 desired_enabled=0

where DP-1 is than disabled. After suspend/resume, allocation turns into:

  00:30:22.680 [DEBUG] [wlr] [backend/drm/drm.c:1099] Reallocating CRTCs
  00:30:22.680 [DEBUG] [wlr] [backend/drm/drm.c:1110] State before reallocation:
  00:30:22.680 [DEBUG] [wlr] [backend/drm/drm.c:1116]   'DP-1' crtc=-1 state=1 desired_enabled=0
  00:30:22.680 [DEBUG] [wlr] [backend/drm/drm.c:1116]   'DP-2' crtc=1 state=3 desired_enabled=1
  00:30:22.680 [DEBUG] [wlr] [backend/drm/drm.c:1116]   'HDMI-A-1' crtc=-1 state=0 desired_enabled=0
  00:30:22.680 [DEBUG] [wlr] [backend/drm/drm.c:1116]   'HDMI-A-2' crtc=-1 state=0 desired_enabled=0
  00:30:22.680 [DEBUG] [wlr] [backend/drm/drm.c:1116]   'DVI-D-1' crtc=-1 state=0 desired_enabled=0
  00:30:22.680 [DEBUG] [wlr] [backend/drm/drm.c:1167] State after reallocation:
  00:30:22.680 [DEBUG] [wlr] [backend/drm/drm.c:1174]   'DP-1' crtc=-1 state=1 desired_enabled=0
  00:30:22.680 [DEBUG] [wlr] [backend/drm/drm.c:1174]   'DP-2' crtc=1 state=3 desired_enabled=1
  00:30:22.680 [DEBUG] [wlr] [backend/drm/drm.c:1174]   'HDMI-A-1' crtc=-1 state=0 desired_enabled=0
  00:30:22.680 [DEBUG] [wlr] [backend/drm/drm.c:1174]   'HDMI-A-2' crtc=-1 state=0 desired_enabled=0
  00:30:22.680 [DEBUG] [wlr] [backend/drm/drm.c:1174]   'DVI-D-1' crtc=-1 state=0 desired_enabled=0

where the crtc for DP-1 is now NULL. Trying to enable the output results
in:

  10:43:36.906 [DEBUG] [sway/config/output.c:351] Turning on output DP-1
  10:43:36.906 [DEBUG] [sway/config/output.c:360] Set preferred mode
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [sway/config/output.c:366] Preferred mode rejected, falling back to another mode
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.906 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.906 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.906 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [DEBUG] [sway/config/output.c:400] Auto-detected output scale: 1.000000
  10:43:36.907 [DEBUG] [sway/config/output.c:430] Committing output DP-1
  10:43:36.907 [DEBUG] [wlr] [backend/drm/drm.c:464] connector DP-1: Can't enable an output without a buffer
  10:43:36.907 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
  10:43:36.907 [ERROR] [wlr] [types/wlr_output.c:512] Failed to get primary display formats
  10:43:36.907 [ERROR] [sway/config/output.c:435] Failed to commit output DP-1

where the primary format can't be queried since there is no crtc
allocated for the connector. Allocating the connector inside
drm_connector_get_primary_formats() fixes this issue. This is possible
since the only user of get_primary_formats() is the swapchain allocation
function, which is only called on output enable. Do the same thing for
the cursor formats in case the user queries them before the output is
enabled.
2021-08-20 10:24:30 +02:00
Simon Ser 18c2dce65e backend: unify startup messages 2021-08-19 20:47:36 +02:00
Simon Ser 46c42e55c6 backend/drm: add support for FB_DAMAGE_CLIPS
This allows the kernel to access our buffer damage. Some drivers
can take advantage of this, e.g. for PSR2 panels (Panel Self
Refresh) or for transfer over USB.

Closes: https://github.com/swaywm/wlroots/issues/1267
2021-08-18 20:59:25 +02:00
Kirill Primak 109405729b surface: fix place_below handling
If a subsurface is being placed below a subsurface right above it, this
should be a noop. However, `node` pointed to the subsurface that was
moved, which resulted in `subsurface->parent_pending_link` being
inserted into itself, breaking parent's pending subsurface list.

This commit separates finding the requested node and getting it's `prev`
field, fixing the issue.
2021-08-18 16:01:18 +02:00
Rouven Czerwinski cdd9a60f72 Revert "backend/drm: Check if output is enabled before sending frame event"
This reverts commit 85757665e6.

We now check if the output is enabled within wlr_output_send_frame, no
need to handle this explicitly in the DRM backend. This also fixes a
race which was introduced with this commit: if we schedule the flip,
disable and commit the output before the flip happens,
output.frame_pending will not be reset to false. We than always fail to
enable the output subsequently:

      00:07:13.276 [INFO] [sway/commands.c:257] Handling command 'output DP-2 enable'
      00:07:13.276 [DEBUG] [sway/commands.c:428] Subcommand: enable
      00:07:13.276 [DEBUG] [sway/config/output.c:204] Merging on top of existing output config
      00:07:13.276 [DEBUG] [sway/config/output.c:227] Config stored for output DP-2 (enabled: 1) (-1x-1@-1.000000Hz position 0,0 scale -1.000000 subpixel unknown transform -1) (bg /home/phoenix/Pictures/Wallpapers/mine/oper.jpg fill) (dpms 1) (max render time: -1)
      00:07:13.276 [DEBUG] [sway/config/output.c:351] Turning on output DP-2
      00:07:13.276 [DEBUG] [sway/config/output.c:360] Set preferred mode
      00:07:13.276 [DEBUG] [wlr] [backend/drm/drm.c:465] connector DP-2: Can't enable an output without a buffer
      00:07:13.276 [DEBUG] [wlr] [types/wlr_output.c:689] Attaching empty buffer to output for modeset
      00:07:13.277 [DEBUG] [sway/config/output.c:329] Output DPI: 162.560000x161.364706
      00:07:13.277 [DEBUG] [sway/config/output.c:400] Auto-detected output scale: 1.000000
      00:07:13.277 [DEBUG] [sway/config/output.c:430] Committing output DP-2
      00:07:13.277 [DEBUG] [wlr] [types/wlr_output.c:729] Tried to commit a buffer while a frame is pending

since the basic_output_test will always fail.
Reset frame_pending to false even if the output has been disabled in the
meantime.

Fixes https://github.com/swaywm/wlroots/issues/3109
2021-08-17 21:07:43 +02:00
Rouven Czerwinski aa78c50bf1 output: check output enabled before sending frame
Similar to commit 85757665e6 ("backend/drm: Check if output is enabled
before sending frame event"), check if the output is still enabled
before sending the frame event. This fixes the bug not only for the DRM
backend, but for wayland and X11 as well.
2021-08-17 21:07:43 +02:00
Rouven Czerwinski 59b292b691 backend/drm: return true on test if no crtc
This should fix the following backtrace, seen on my desktop with one
output disabled:

  #0  atomic_crtc_commit (conn=0x270f5c0, state=0x270f6d0, flags=0, test_only=<optimized out>) at ../backend/drm/atomic.c:178
          drm = 0x1ae9c10
          output = 0x270f5c0
          crtc = 0x0
          modeset = false
          active = false
          mode_id = 43989232
          gamma_lut = 0
          prev_vrr_enabled = <optimized out>
          vrr_enabled = <optimized out>
          atom = {req = 0x270f5c0, failed = 48}
          ok = <optimized out>
  #1  0x00007f1104f33128 in drm_crtc_commit (conn=conn@entry=0x270f5c0, state=state@entry=0x270f6d0, flags=flags@entry=0, test_only=test_only@entry=true) at ../backend/drm/drm.c:339
          __PRETTY_FUNCTION__ = "drm_crtc_commit"
          drm = <optimized out>
          crtc = 0x0
          ok = <optimized out>
  #2  0x00007f1104f34e6c in drm_connector_test (output=output@entry=0x270f5c0) at ../backend/drm/drm.c:488
          conn = 0x270f5c0
          unsupported = <optimized out>
  #3  0x00007f1104f35424 in drm_connector_commit (output=0x270f5c0) at ../backend/drm/drm.c:578
          conn = 0x270f5c0
  #4  0x00007f1104f600b7 in wlr_output_commit (output=output@entry=0x270f5c0) at ../types/wlr_output.c:837
          now = {tv_sec = 7732, tv_nsec = 623813006}
          pre_event = {output = 0x270f5c0, when = 0x7ffecc1be570}
          back_buffer = 0x0
          scale_updated = <optimized out>
          geometry_updated = <optimized out>
          committed = <optimized out>
          event = {output = 0x0, committed = 4401048, when = 0x29f38f0}
  #5  0x0000000000433047 in apply_output_config (oc=oc@entry=0x29f38f0, output=output@entry=0x2710720) at ../sway/config/output.c:431
          wlr_output = 0x270f5c0
          output_box = <optimized out>
  #6  0x0000000000433aaf in apply_output_config_to_outputs (oc=0x2308400) at ../sway/config/output.c:649
          current = 0x29f38f0
          name = <optimized out>
          wildcard = true
          id = "Dell Inc. DELL U2410 F525M9AK0MML\000\060\060\060ACD7\000\000\000\000\000\000\000\220\063\240\002\000\000\000\000L5C\000\000\000\000\000\377\377\377\377\000\000\000\000\377\377\377\377\000\000\000\000\377\377\377\377\000\000\000\000\377\377\377\377\000\000\000\000\355\240E\000\000\000\000\000\377\377\377\377\000\000\000\000@\206+\002\000\000\000\000`\260.\002\000\000\000"
          sway_output = 0x2710720
          tmp = 0x2242030
          seat = <optimized out>
  #7  0x000000000043df6b in cmd_output (argc=<optimized out>, argv=0x2a03390) at ../sway/commands/output.c:108
          error = <optimized out>
          output = <optimized out>
          background = false
  #8  0x0000000000410304 in execute_command (_exec=_exec@entry=0x2975d20 "output * dpms off", seat=0x22a3280, seat@entry=0x0, con=con@entry=0x0) at ../sway/commands.c:291
          res = <optimized out>
          argc = 4
          argv = 0x2a03370
          handler = 0x479230 <handlers+560>
          cmd = <optimized out>
          matched_delim = 0 '\000'
          containers = 0x0
          using_criteria = false
          __PRETTY_FUNCTION__ = "execute_command"
          exec = 0x28f63c0 "output * dpms off"
          head = 0x0
          res_list = 0x2a2e9d0
  #9  0x0000000000418b65 in ipc_client_handle_command (client=client@entry=0x2a6ac80, payload_length=<optimized out>, payload_type=IPC_COMMAND) at ../sway/ipc-server.c:645
          line = <optimized out>
          res_list = <optimized out>
          json = <optimized out>
          length = <optimized out>
          __PRETTY_FUNCTION__ = "ipc_client_handle_command"
          buf = 0x2975d20 "output * dpms off"
  #10 0x000000000041964c in ipc_client_handle_readable (client_fd=<optimized out>, mask=<optimized out>, data=0x2a6ac80) at ../sway/ipc-server.c:267
          pending_length = <optimized out>
          pending_type = <optimized out>
          client = 0x2a6ac80
          read_available = 31
          buf = "i3-ipc\021\000\000\000\000\000\000"
          received = 14
  #11 0x00007f1104fc3492 in wl_event_loop_dispatch () from /nix/store/ridk7k2ka6dbk4ly7qqjgmc523s4fj89-wayland-1.19.0/lib/libwayland-server.so.0
  No symbol table info available.
  #12 0x00007f1104fc1135 in wl_display_run () from /nix/store/ridk7k2ka6dbk4ly7qqjgmc523s4fj89-wayland-1.19.0/lib/libwayland-server.so.0
  No symbol table info available.
  #13 0x000000000041ac10 in server_run (server=server@entry=0x47b0c0 <server>) at ../sway/server.c:261
  No locals.
  #14 0x000000000041a3fc in main (argc=<optimized out>, argv=0x7ffecc1bec68) at ../sway/main.c:395
          verbose = 0
          debug = 0
          validate = 0
          allow_unsupported_gpu = 0
          long_options = {{name = 0x45b516 "help", has_arg = 0, flag = 0x0, val = 104}, {name = 0x45ee69 "config", has_arg = 1, flag = 0x0, val = 99}, {name = 0x45b51b "validate", has_arg = 0, flag = 0x0, val = 67}, {
              name = 0x45b524 "debug", has_arg = 0, flag = 0x0, val = 100}, {name = 0x45b3ac "version", has_arg = 0, flag = 0x0, val = 118}, {name = 0x45a55c "verbose", has_arg = 0, flag = 0x0, val = 86}, {name = 0x45b52a "get-socketpath",
              has_arg = 0, flag = 0x0, val = 112}, {name = 0x45b539 "unsupported-gpu", has_arg = 0, flag = 0x0, val = 117}, {name = 0x45b549 "my-next-gpu-wont-be-nvidia", has_arg = 0, flag = 0x0, val = 117}, {name = 0x0, has_arg = 0,
              flag = 0x0, val = 0}}
          config_path = 0x0
          usage = 0x45b830 "Usage: sway [options] [command]\n\n  -h, --help", ' ' <repeats 13 times>, "Show help message and quit.\n  -c, --config <config>  Specify a config file.\n  -C, --validate         Check the validity of the config file, th"...
          c = <optimized out>

where the second output is not enabled:

  (gdb) frame 4
  #4  0x00007f1104f600b7 in wlr_output_commit (output=output@entry=0x270f5c0) at ../types/wlr_output.c:837
  837	in ../types/wlr_output.c
  (gdb) p output->enabled
  $3 = false
  (gdb)

The culprit being that since 604674dc54 we
always try to perform a commit, even on a disabled output.
2021-08-17 14:07:51 +02:00
Simon Ser 7544b7abf9 backend/drm: use addon for wlr_drm_fb 2021-08-17 11:08:45 +02:00
Michele Sorcinelli cae7b98136 xwayland: do not free cursor in handle_server_ready()
If XWayland terminates for any reason, xwm_set_cursor() has to
to be called again, so the cursor has to stick around.
2021-08-17 09:17:59 +02:00
Tudor Brindus 0c19a28266 input/tablet: fix `wl_array_for_each` usage on tablet proximity
Looks like this instance was missed in
e035f2b9c4.

Fixes #3110.
2021-08-15 23:26:51 +02:00
Isaac Freund 3364eec07e layer-shell: replace close() with destroy()
The protocol specifies that all requests (aside from destroy) are
ignored after the compositor sends the closed event. Therefore,
destroying the wlroots object and rendering the resource inert
when sending the closed event keeps things simpler for wlroots and
compositors.
2021-08-14 20:25:46 +02:00
Simon Ser ad7651a370 render/gles2: make wlr_gles2_texture a wlr_buffer addon 2021-08-12 10:22:19 +02:00
Simon Ser ee1156b62b render/gles2: make wlr_gles2_buffer an addon
Saves us from walking a list.
2021-08-12 10:22:19 +02:00
Simon Ser 93964012e6 buffer: add addon set
This allows wlr_buffer users to extend it with tjeir own state.
2021-08-12 10:22:19 +02:00
Simon Ser 20404ed8bb surface: drop surface_state_copy
This function was weird because it copied some fields but not all.
2021-08-11 18:13:12 +02:00
Simon Ser 3f9e4f7a44 surface: kill wlr_surface.previous
This wlr_surface_state field was a special case because we don't
want to save the whole current state: for instance, the wlr_buffer
must not be saved or else wouldn't get released soon enough.

Let's just inline the state fields we need instead.
2021-08-11 18:13:12 +02:00
Kirill Primak 111d4eafd7 util/addon: find both by owner and impl
This allows to have multiple addons of different types with the same
owner.
2021-08-11 18:12:57 +02:00
Kirill Primak debd6c5f0b wlr_output_layout: use wlr_addons 2021-08-10 19:15:48 +02:00
Kirill Primak 11f799e88e wlr_outputs: add wlr_addon_set 2021-08-10 19:15:48 +02:00
Kirill Primak a6a80850b7 util: add wlr_addon 2021-08-10 19:15:48 +02:00
Simon Ser 604674dc54 backend/drm: always perform a test commit in drm_connector_test
This allows callers to use wlr_output_test to check whether a mode
can be enabled, for instance.

Closes: https://github.com/swaywm/wlroots/issues/2250
2021-08-10 16:38:22 +02:00
Simon Ser eb0ce659cf backend/drm: add proper error handling to wlr_drm_backend_create
Some listeners weren't removed and caused a use-after-free with
e.g. vkms when used as a secondary GPU.
2021-08-10 15:42:36 +02:00
Simon Ser 88f65db87f ci: add smoke test
Add a very basic smoke test which uses VKMS to fire up the DRM
backend.
2021-08-08 22:13:33 +02:00
Tudor Brindus 033c9cab74 input/pointer: try harder to not send duplicate motion events
wl_fixed_t is a 32-bit data type, but our doubles are 64-bit. This meant
that two doubles that would map to the same wl_fixed_t could compare
unequal, and send a duplicate motion event.

Refs swaywm/sway#4632.
2021-08-07 09:04:12 +02:00
Simon Ser c27263c105 seat: allow compositors to not load a keymap
The protocol allows compositors to not send any keymap to Wayland
clients. Handle a keymap-less keyboard correctly by sending
WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP instead of erroring out in the
mmap call.
2021-08-06 23:06:29 +02:00
Simon Ser d48ffac56b build: remove "." from include dirs 2021-08-06 19:44:35 +02:00
Simon Ser ca0b19fc9c examples/fullscreen-shell: remove unused render_data.view field 2021-08-06 18:27:31 +02:00
Simon Ser 1936e136df backend/drm: require buffer on modeset in drm_connector_test
When testing a modeset, make sure the caller has also provided a
buffer. This allows df0e75ba05 ("output: try skipping buffer
allocation if the backend allows it") to work as expected with the
DRM backend.

Closes: https://github.com/swaywm/wlroots/issues/3086
2021-08-04 22:51:40 +02:00
Simon Ser df0e75ba05 output: try skipping buffer allocation if the backend allows it
When enabling an output, skip the empty buffer allocation if the
backend accepts modesets without a buffer.

This fixes mode-setting with the noop backend.
2021-08-04 10:18:59 -04:00
Simon Ser 8a3cd28973 render/pixman/pixel_format: add more formats
Add a bunch of new formats for Pixman: a few missing 32-bit ones,
some 16-bit and 32-bit formats as well.

Mostly based on a Weston patch [1].

[1]: https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/664
2021-08-03 02:53:03 -04:00
Simon Ser b913e64f95 render/pixel_format: add more formats for Pixman
These will be added to Pixman in the next commit.
2021-08-03 02:53:03 -04:00
Simon Ser 923258b0be backend/drm: preserve mode order from kernel
The kernel orders the mode list from highest to lowest. Preserve
this ordering in the wlr_output.modes list.
2021-08-02 09:28:21 -04:00
Kirill Primak f12bacf4b4 surface: don't cache frame callback lists 2021-08-02 09:10:10 +02:00
Quantum 456c6e2279 viewporter: remove crop and scale state upon destruction
According to the viewport protocol, upon wp_viewport::destroy():

> The associated wl_surface's crop and scale state is removed.
> The change is applied on the next wl_surface.commit.

Therefore, wp_viewport_destroy(viewport) should remove all viewport state.

Currently, wlroots does not remove the crop and scale state. Instead, a
client must do:

    wl_fixed_t clear = wl_fixed_from_int(-1);
    wp_viewport_set_source(viewport, clear, clear, clear, clear);
    wp_viewport_set_destination(viewport, -1, -1);
    wp_viewport_destroy(viewport);

This commit adds the necessary logic into viewport_destroy and makes
wlroots comply with the protocol.
2021-08-02 09:08:03 +02:00
Manuel Stoeckl f5df956c18 render/gles2: add a few 10-bit and FP16 formats
The half-float formats depend on GL_OES_texture_half_float_linear,
not just the GL_OES_texture_half_float extension, because the latter
does not include support for linear magni/minification filters.

The new 2101010 and 16161616F formats are only available on little-
endian builds, since their gl_types are larger than a byte and thus
endianness dependent.
2021-07-30 08:29:13 +02:00
Manuel Stoeckl 44e8451cd9 render/gles2: hide shm formats without GL support
This change introduces a new function to check whether the renderer
has the needed GL extensions to read a given pixel format.
2021-07-30 08:29:13 +02:00
Manuel Stoeckl 4dc52bcb6c render/pixel-format: add a few 10-bit and FP16 formats 2021-07-30 08:29:13 +02:00
Simon Ser f76960388f render/gles2: add support for some 24 and 16-bit formats
On little-endian, we can enable pixel formats which don't use
gl_type = GL_UNSIGNED_BYTE. See [1].

[1]: https://afrantzis.com/pixel-format-guide/
2021-07-29 10:47:34 -04:00
Simon Ser 6973361d60 render/pixel-format: add some 24 and 16-bit formats 2021-07-29 10:47:34 -04:00
Simon Ser 3132c0ab10 output: drop wlr_output_state.buffer_type
This is now unconditionally set to WLR_OUTPUT_STATE_BUFFER_SCANOUT.
2021-07-29 09:48:33 -04:00
Simon Ser f211bc983a output-damage: stop using enum wlr_output_state_buffer_type
This enum will get dropped in the next commit.
2021-07-29 09:48:33 -04:00
Simon Ser 4ddde1a7bd output: drop wlr_output_impl.{attach,rollback}_render
No backend uses these anymore.
2021-07-29 09:48:33 -04:00
Simon Ser d17a009062 backend/noop: drop attach_render/rollback_render
We no longer require wlr_output_impl.{attach,rollback}_render to
be populated.
2021-07-29 09:48:33 -04:00
Simon Ser 55ac7e335a backend/drm: fix wrong type for get_cursor_format return values
These are bools but should be pointers.
2021-07-28 16:59:21 -04:00
Simon Ser c55f70c8b7 backend/drm: stop initializing renderer for parent backend
Unless we're dealing with a multi-GPU setup and the backend being
initialized is secondary, we don't need a renderer nor an allocator.
Stop initializing these.
2021-07-28 22:52:35 +02:00
Simon Ser c74dc45bb6 backend/drm: drop get_renderer implementation
We can now just rely on the common code for this.
2021-07-28 22:52:35 +02:00
Simon Ser 9b99570869 backend/drm: drop drm_surface_{make,unset}_current 2021-07-28 22:52:35 +02:00
Simon Ser ebb661532c backend/drm: remove SCANOUT check in drm_connector_test
We only accept SCANOUT, the buffer type should never be set to RENDER.
2021-07-28 22:52:35 +02:00
Simon Ser f5900c1f00 backend/drm: remove primary swapchain
We can't nuke it completely, we still need it for multi-GPU.
2021-07-28 22:52:35 +02:00
Simon Ser 85d7ad2eef backend/drm: allow legacy scan-out if FB props match
Historically we haven't allowed direct scan-out for legacy KMS,
because legacy misses the functionality to make sure a buffer can
be scanned out. However with renderer v6 the backend can't figure
out anymore whether the buffer comes from its internal swap-chain,
because the backend doesn't have an internal swap-chain.

The legacy KMS API guarantees that the driver won't reject a buffer
as long as it's been allocated with the same parameters as the
previous one. Let's check this in legacy_crtc_test.
2021-07-28 22:52:35 +02:00
Simon Ser 6aadf811aa output: fallback to modifier-less allocation on modeset test failure
Sometimes we allocate a buffer with modifiers but then fail to
perform a modeset with it. This can happen on Intel because of
bandwidth limitations. To mitigate this issue, it's possible to
re-allocate the buffer with modifiers.

Add the logic to do so in wlr_output.
2021-07-28 22:52:35 +02:00
Simon Ser 0fb55c76d0 output: allocate and attach empty buffer on modeset
Some backends need a buffer in order to be able to perform a
modeset.
2021-07-28 22:52:35 +02:00
Simon Ser 1a5b6722a8 output: use pending resolution when allocating swapchain
This allows the swapchain to be created with the correct resolution
during a mode change.
2021-07-28 22:52:35 +02:00
Simon Ser d6f0fc251e backend/drm: implement get_primary_formats 2021-07-28 22:52:35 +02:00
Simon Ser d1c931cbe8 output: fallback to XRGB in output_pick_format
This will be necessary for the primary buffer.
2021-07-28 22:52:35 +02:00
Dylan Araps e5063ef3a3 util/time: make NSEC_PER_SEC static
This fixes static linking with libseat.

Closes #3072
2021-07-28 09:01:59 +02:00
Simon Zeni 6f19295647 render/egl: initialize wlr_egl with EGL_PLATFORM_DEVICE_EXT
Uses the EXT_device_query extension to get the EGL device matching the
requested DRM file descriptor. If the extension is not supported or no device
is found, the EGL device will be retrieved using GBM.

Depends on the EGL_EXT_device_enumeration to get the list of EGL devices.
2021-07-27 20:45:53 +02:00
yuiiio 7667ab73bd
render/egl: fix typo 2021-07-27 00:35:36 +02:00
Simon Ser 770a561bce xwayland: embed wlr_xwayland_server_options in server struct
As more options are added, more fields will be duplicated. Let's
just embed the struct in wlr_xwayland_server so that we don't need
to keep both in sync.
2021-07-27 00:18:11 +02:00
Simon Ser 4b316a3823 xwayland: simplify argv filling logic
Remove fill_arg and replace it with stack-allocations and simple
array-filling.
2021-07-27 00:18:11 +02:00
Simon Ser 3cf2535c23 render/egl: add support for EGL_EXT_device_drm_render_node
This EGL extension has been added in [1]. The upsides are:

- We directly get a render node, instead of having to convert the
  primary node name to a render node name.
- If EGL_DRM_RENDER_NODE_FILE_EXT returns NULL, that means there is
  no render node being used by the driver.

[1]: https://github.com/KhronosGroup/EGL-Registry/pull/127
2021-07-26 11:44:16 -04:00
Simon Ser ace2eda073 render/egl: set EGL_IMAGE_PRESERVED_KHR
Without setting this the EGL implementation is allowed to perform
destructive actions on the buffer when imported: its contents
become undefined.

This is mostly a pedantic change, because Mesa processes the attrib
and does absolutely nothing with it.
2021-07-23 11:27:17 -04:00
Simon Zeni 04d4fb536d render/wlr_texture: put wlr_texture_from_buffer into the public API 2021-07-22 22:28:24 +02:00
Simon Zeni 0778151f94 types/wlr_buffer: create custom wlr_buffer from wl_resource 2021-07-22 22:28:24 +02:00
Simon Zeni 646a25667e types/wlr_buffer: introduce wlr_buffer_resource_interface
Adds `wlr_buffer_resource_interface` and `wlr_buffer_register_resource_interface`,
which allows a user to register a way to create a wlr_buffer from a specific
wl_resource.
2021-07-22 22:28:24 +02:00
Simon Zeni f09c88c1b7 types/wlr_buffer: remove wlr_renderer argument from wlr_buffer_from_resource 2021-07-22 22:28:24 +02:00
Simon Ser 2fa47c1837 render: drop wlr_renderer_impl.init_wl_display
Now that we have our own wl_drm implementation, there's no reason
to provide custom renderer hooks to init a wl_display in the
interface. We can just initialize the wl_display generically,
depending on the renderer capabilities.
2021-07-22 14:10:26 -04:00
ayaka 70fb21c35b backend: make DRM and libinput backends optional
Co-authored-by: Simon Ser <contact@emersion.fr>
2021-07-22 09:56:38 -04:00
Simon Ser 66c42f4fcb backend/drm: add DRM_MODE_CONNECTOR_USB to conn_get_name
See 757e26712337 ("drm/uapi: Add USB connector type") in the kernel
tree.
2021-07-21 08:47:35 +02:00
Simon Ser cc8bc0db20 backend/drm: stop restoring CRTCs on exit
This is the cause of the spurious "drmHandleEvent failed" messages
at exit. restore_drm_outputs calls handle_drm_event in a loop without
checking whether the FD is readable, so drmHandleEvent ends up with a
short read (0 bytes) and returns an error.

The loop's goal is to wait for all queued page-flip events to complete,
to allow drmModeSetCrtc calls to succeed without EBUSY. The
drmModeSetCrtc calls are supposed to restore whatever KMS state we were
started with. But it's not clear from my PoV that restoring the KMS
state on exit is desirable.

KMS clients are supposed to save and restore the (full) KMS state on VT
switch, but not on exit. Leaving our KMS state on exit avoids unnecessary
modesets and allows flicker-free transitions between clients. See [1]
for more details, and note that with Pekka we've concluded that a new
flag to reset some KMS props to their default value on compositor
start-up is the best way forward. As a side note, Weston doesn't restore
the CRTC by does disable the cursor plane on exit (see
drm_output_deinit_planes, I still think disabling the cursor plane
shouldn't be necessary on exit).

Additionally, restore_drm_outputs only a subset of the KMS state.
Gamma and other atomic properties aren't accounted for. If the previous
KMS client had some outputs disabled, restore_drm_outputs would restore
a garbage mode.

[1]: https://blog.ffwll.ch/2016/01/vt-switching-with-atomic-modeset.html
2021-07-20 15:33:26 +02:00
Simon Ser 8afb4d8bf0 buffer: re-use wlr_shm_client_buffer
The first time wlr_buffer_from_resource is called with a wl_buffer
resource that originates from wl_shm, create a new
wlr_shm_client_buffer as usual. If wlr_buffer_from_resource is called
multiple times, re-use the existing wlr_shm_client_buffer.

This commit changes how the wlr_shm_client_buffer lifetime is managed:
previously it was destroyed as soon as the wlr_buffer was released.
With this commit it's destroyed when the wl_buffer resource is.

Apart from de-duplicating wlr_shm_client_buffer creations, this allows
to easily track when a wlr_shm_client_buffer is re-used. This is useful
for the renderer and the backends, e.g. the Pixman renderer can keep
using the same Pixman image if the buffer is re-used. In the future,
this will also allow to re-use resources in the Wayland and X11 backends
(remote wl_buffer objects for Wayland, pixmaps for X11).
2021-07-19 13:13:10 -04:00
Simon Ser f94eb174c7 backend/drm: fix NULL data in handle_drm_event
wl_event_loop_add_fd was called with a NULL data argument, but the
function expects the data argument to be set to the wlr_drm_backend.

Fixes: 053ebe7c27 ("backend/drm: terminate display on drmHandleEvent failure")
2021-07-19 09:01:21 -04:00
Vyivel a93b18dbd5 input/pointer: send axis source once per frame
Only one wl_pointer.axis_source event is permitted per frame, according
to the Wayland specification.

Fixes https://github.com/swaywm/wlroots/issues/2973
2021-07-19 12:00:31 +02:00
Simon Ser a47f89cf7c backend/wayland: properly cleanup wlr_wl_pointer
We were missing destroy calls for gestures, and we were only
destroying the relative pointer on output destroy.
2021-07-13 09:20:25 -04:00
Simon Ser bcd5d8504c output: remove wlr_output_impl.export_dmabuf
No backend is using it anymore.
2021-07-12 13:29:03 -04:00
Simon Ser 709190c4c8 backend/drm: remove wlr_output_impl.export_dmabuf
This is now provided by the generic wlr_output implementation.
2021-07-12 13:29:03 -04:00
Simon Ser aec062d0d3 backend/headless: remove wlr_output_impl.export_dmabuf
This is now provided by the generic wlr_output implementation.
2021-07-12 13:29:03 -04:00
Simon Ser 87e8c60faf output: add generic wlr_output_export_dmabuf implementation
When wlr_output manages its own swap-chain, there's no need to
hook into the backend to grab DMA-BUFs. Instead, maintain a
wlr_output.front_buffer field with the latest committed buffer.
2021-07-12 13:29:03 -04:00
Simon Ser 28aa803916 buffer: drop resource arg from wlr_client_buffer_create
This function doesn't need the wl_resource anymore.

In the failure paths, wlr_buffer_unlock in surface_apply_damage
will take care of sending wl_buffer.release.
2021-07-12 09:19:18 -04:00
Simon Ser 5544973814 render/gles2: disable blending opportunistically
We don't always need to enable blending: when the texture doesn't
have alpha or when the color is opaque, we can disable it.
2021-07-12 09:16:09 -04:00
Simon Ser 9dba176e8d render/gles2: set has_alpha for DMA-BUFs
Use our internal pixel format table to figure out whether an
imported DMA-BUF has alpha.
2021-07-12 09:16:09 -04:00
Simon Ser 9b70eab194 render/gles2: rename wlr_egl.exts to better match Khronos
Khronos refers to extensions with their namespace as a prefix in
uppercase. Change our naming to align with Khronos conventions.
This also makes grepping easier.
2021-07-12 09:13:49 -04:00
Simon Ser 4c51a0f6eb render/egl: rename wlr_egl.exts to better match Khronos
Khronos refers to extensions with their namespace as a prefix in
uppercase. Change our naming to align with Khronos conventions.
This also makes grepping easier.
2021-07-12 09:13:49 -04:00
Simon Ser 4554f17377 buffer: drop wlr_client_buffer.resource 2021-07-09 17:25:02 -04:00
Simon Ser d7c68ce632 buffer: stop using resource in client_buffer_get_dmabuf
Instead, delegate to the source wlr_buffer.
2021-07-09 17:25:02 -04:00
Simon Ser a0baba4fa0 buffer: add wlr_client_buffer.source
This stores a weak reference to the source buffer.
2021-07-09 17:25:02 -04:00
Simon Ser 0abb67c478 buffer: stop using source resource in wlr_client_buffer_apply_damage
This is the first step towards dropping wlr_client_buffer.resource.
2021-07-09 17:25:02 -04:00
Simon Ser 7b25b0ff88 buffer: rename wlr_client_buffer variables to client_buffer
We often juggle between wlr_buffer and wlr_client_buffer variables.
Use a consistent naming: "buffer" for wlr_buffer and "client_buffer"
for wlr_client_buffer.
2021-07-09 17:25:02 -04:00
Simon Zeni 60f4d8f409 types/wlr_buffer: remove wlr_resource_get_buffer_size 2021-07-09 23:16:39 +02:00
Simon Zeni d086501fba types/wlr_surface: get buffer size from wlr_buffer_import 2021-07-09 23:16:39 +02:00
Simon Zeni 6d8029b07e types/wlr_buffer: split wlr_client_buffer_import function
`wlr_client_buffer_import` is splitted in two distincts function:
	- wlr_buffer_from_resource, which transforms a wl_resource into
	  a wlr_buffer
	- wlr_client_buffer_create, which creates a wlr_client_buffer
	  from a wlr_buffer by creating a texture from it and copying its
	  wl_resource
2021-07-09 23:16:39 +02:00
Simon Ser f67cfb2ce2 backend/drm: remove backend arg from wlr_drm_interface.crtc_commit
The callee can just get it from the wlr_drm_connector.
2021-07-09 15:31:19 -04:00
Simon Ser fde56c20b4 backend/drm: move legacy-specific checks to legacy.c
Now that we have a test_only arg in crtc_commit, we can move the
legacy checks to legacy.c.
2021-07-09 15:31:19 -04:00
Simon Ser 017555651b backend/drm: add test_only arg to wlr_drm_interface.crtc_commit
Right now callers of drm_crtc_commit need to check whether the
interface is legacy or atomic before passing the TEST_ONLY flag.
Additionally, the fallbacks for legacy are in-place in the common
code.

Add a test_only arg to the crtc_commit hook. This way, there's no
risk to pass atomic-only flags to the legacy function (add an assert
to ensure this) and all of the legacy-specific logic can be put back
into legacy.c (done in next commit).
2021-07-09 15:31:19 -04:00
Vyivel a362d21d6b render/pixman: fix texture_is_opaque()
A texture is opaque when it does *not* have alpha.

Fixes https://github.com/swaywm/wlroots/issues/2907
2021-07-09 08:23:45 +02:00
Simon Ser c1b27cc499 backend/drm: stop using drm_surface_make_current in drm_surface_blit
drm_surface_make_current and drm_surface_unset_current set implicit
state and are an unnecessary mid-layer. Prefer to use directly
wlr_renderer_begin_with_buffer, which automatically unsets the back
buffer on wlr_renderer_end.

I'd like to get rid of drm_surface_make_current once we stop using
it for the primary swapchain.
2021-07-08 14:44:42 -04:00
Simon Ser d71ed635b9 backend/drm: force linear layout for multi-GPU buffers
Some buffers need to be copied across GPUs. Such buffers need to be
allocated with a format and modifier suitable for both the source
and the destination.

When explicit modifiers aren't supported, we were forcing the buffers
to be allocated with a linear layout, because implicit modifiers
aren't portable across GPUs. All is well with this case.

When explicit modifiers are supported, we were advertising the whole
list of destination modifiers, in the hope that the source might
have some in common and might be able to allocate a buffer with a
more optimized layout. This works well if the source supports explicit
modifiers. However, if the source doesn't, then wlr_drm_format_intersect
will fallback to implicit modifiers, and everything goes boom: the
source uses a GPU-specific tiling and the destination interprets it
as linear.

To avoid this, just force linear unconditionally. We'll be able to
revert this once we have a good way to indicate that an implicit modifier
isn't supported in wlr_drm_format_set, see [1].

[1]: https://github.com/swaywm/wlroots/pull/2815

Closes: https://github.com/swaywm/wlroots/issues/3030
2021-07-08 17:06:20 +02:00
Simon Ser e035f2b9c4 Fix invalid uses of wl_array_for_each
[1] and [2] have introduced new wl_array usage in wlroots, but
contains a mistake: wl_array_for_each iterates over pointers to
the wl_array entries, not over entries themselves.

Fix all wl_array_for_each call sites. Name the variables "ptr"
to avoid confusion.

Found via ASan:

    ==148752==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0x602000214111 in thread T0
        #0 0x7f6ff2235f19 in __interceptor_free /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cpp:127
        #1 0x7f6ff1c04004 in wlr_tablet_destroy ../subprojects/wlroots/types/wlr_tablet_tool.c:24
        #2 0x7f6ff1b8463c in wlr_input_device_destroy ../subprojects/wlroots/types/wlr_input_device.c:51
        #3 0x7f6ff1ab9941 in backend_destroy ../subprojects/wlroots/backend/wayland/backend.c:306
        #4 0x7f6ff1a68323 in wlr_backend_destroy ../subprojects/wlroots/backend/backend.c:57
        #5 0x7f6ff1ab36b4 in multi_backend_destroy ../subprojects/wlroots/backend/multi/backend.c:57
        #6 0x7f6ff1ab417c in handle_display_destroy ../subprojects/wlroots/backend/multi/backend.c:124
        #7 0x7f6ff106184e in wl_display_destroy (/usr/lib/libwayland-server.so.0+0x884e)
        #8 0x55cd1a77c9e5 in server_fini ../sway/server.c:218
        #9 0x55cd1a77893f in main ../sway/main.c:400
        #10 0x7f6ff04bdb24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24)
        #11 0x55cd1a73a7ad in _start (/home/simon/src/sway/build/sway/sway+0x33a7ad)

    0x602000214111 is located 1 bytes inside of 16-byte region [0x602000214110,0x602000214120)
    freed by thread T0 here:
        #0 0x7f6ff2235f19 in __interceptor_free /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cpp:127
        #1 0x7f6ff1c04004 in wlr_tablet_destroy ../subprojects/wlroots/types/wlr_tablet_tool.c:24
        #2 0x7f6ff1b8463c in wlr_input_device_destroy ../subprojects/wlroots/types/wlr_input_device.c:51
        #3 0x7f6ff1ab9941 in backend_destroy ../subprojects/wlroots/backend/wayland/backend.c:306
        #4 0x7f6ff1a68323 in wlr_backend_destroy ../subprojects/wlroots/backend/backend.c:57
        #5 0x7f6ff1ab36b4 in multi_backend_destroy ../subprojects/wlroots/backend/multi/backend.c:57
        #6 0x7f6ff1ab417c in handle_display_destroy ../subprojects/wlroots/backend/multi/backend.c:124
        #7 0x7f6ff106184e in wl_display_destroy (/usr/lib/libwayland-server.so.0+0x884e)

    previously allocated by thread T0 here:
        #0 0x7f6ff2236279 in __interceptor_malloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cpp:145
        #1 0x7f6ff1066d03 in wl_array_add (/usr/lib/libwayland-server.so.0+0xdd03)

[1]: https://github.com/swaywm/wlroots/pull/3002
[2]: https://github.com/swaywm/wlroots/pull/3004
2021-07-08 10:08:47 -04:00
Simon Ser b934fbaf04 seat: add wlr_seat_touch_{send,notify}_frame
The wl_touch.frame event is used to group multiple touch events
together. Instead of sending it immediately after each touch event,
rely on the backend to send it (and on the compositor to relay it).

This is a breaking change because compositors now need to manually
send touch frame events instead of relying on wlr_seat to do it.
2021-07-08 09:12:17 +02:00
Simon Ser 22fd411bc3 cursor: add touch frame event 2021-07-08 09:12:17 +02:00
Simon Ser c1902cdb3f backend/x11: send touch frame events 2021-07-08 09:12:17 +02:00
Simon Ser 8eef6a8843 backend/wayland: send touch frame events 2021-07-08 09:12:17 +02:00
Simon Ser 2d36d7fb67 backend/libinput: send touch frame events 2021-07-08 09:12:17 +02:00
Simon Ser 84906a832f touch: add frame event 2021-07-08 09:12:17 +02:00
Simon Ser a48e569d38 output-damage: fix output swapchain handling
When wlr_output.swapchain is used instead of the backend's, the
buffer_type will be set to SCANOUT even if wlr_output_attach_render
has been called. This tricks wlr_output_damage into thinking the
whole output needs to be repainted.

Workaround this issue by forcing buffer_type to RENDER when the
output has a back-buffer set.

Will clean all of that up when removing the precommit event handler
altogether.

This commit fixes damage tracking on the Wayland, X11 and headless
backends.
2021-07-07 12:02:24 -04:00
Simon Ser c2bd63c186 output: detach buffer from renderer before commit
Right now we rely entirely on implicit sync for synchronizing
access to GPU buffers. Implicit sync works by setting
synchronization points on the buffer in writers, and letting
readers wait on these sync points before accessing the buffer.

With OpenGL, sync points are created using functions such as
eglSwapBuffers or glFlush. If none of these special functions
are called, no sync point will be created and readers will
potentially access a buffer that hasn't finished rendering yet.

In the context of wlroots, OpenGL is the writer and the backend
(KMS or parent Wayland/X11 session) is the reader. After we're
done rendering a frame, and before passing that frame to the
backend, we need to call glFlush.

glFlush is called when the buffer is detached from the renderer.
This is a task done by output_clear_back_buffer. So let's call
this function before invoking the impl->commit hook, instead of
calling it after.

All of this is maybe a little tricky to get right with the
current renderer_bind_buffer API. The new
wlr_renderer_begin_with_buffer API is much better, because glFlush
is called on wlr_renderer_end, so it's more intuitive.

Closes: https://github.com/swaywm/wlroots/issues/3020
2021-07-07 12:00:43 -04:00
Simon Zeni 4c7657ee62 util/box: stabilize interface 2021-07-06 21:43:17 +02:00
Simon Zeni e192d87731 move wlr_box from /types to /util 2021-07-06 21:43:17 +02:00
Simon Zeni d975f35bba types/wlr_box: remove unused wlr_box_from_pixman_box32 and wlr_box_rotated_bounds functions 2021-07-06 21:43:17 +02:00
Simon Ser 3fdf8cf07e buffer: unify texture creation in wlr_client_buffer_import
All code-paths now invoke wlr_texture_from_buffer. De-duplicate
the calls.
2021-07-05 11:13:41 -04:00
Simon Ser 9a8097682b buffer: stop sending wl_buffer.release events from wlr_client_buffer
The specialized client buffer implementations take care of this.
2021-07-05 11:13:41 -04:00
Simon Ser d3d1c69aca buffer: remove renderer param from wlr_resource_get_buffer_size
The only reason we had this was because of EGL_WL_bind_wayland_display
support, which has been dropped.
2021-07-05 11:13:41 -04:00
Simon Ser 8a4957570f render/egl: remove EGL_WL_bind_wayland_display support
Our GLES2 renderer doesn't use it anymore, so we can drop it.
2021-07-05 11:13:41 -04:00
Simon Ser e5b5592a95 render: remove wl_drm support from wlr_renderer
Everything needs to go through the unified wlr_buffer interface
now.

If necessary, there are two ways support for
EGL_WL_bind_wayland_display could be restored by compositors:

- Either by using GBM to convert back EGL Wayland buffers to
  DMA-BUFs, then wrap the DMA-BUF into a wlr_buffer.
- Or by wrapping the EGL Wayland buffer into a special wlr_buffer
  that doesn't implement any wlr_buffer_impl hook, and special-case
  that buffer type in the renderer.
2021-07-05 11:13:41 -04:00
Simon Ser 4e07d4cbf9 render/gles2: use wlr_drm for wl_drm implementation
This allows use to remove all of our special wl_drm support code.
2021-07-05 11:13:41 -04:00
Simon Ser d7b19fb294 buffer: handle wl_drm buffers
This allows renderers to choose between implementing the old
wlr_renderer_impl.texture_from_wl_drm hook, or opt for the new
wlr_drm stub. The stub has the advantage of not requiring any
special support code: stubbed wl_drm buffers look exactly like
DMA-BUFs from linux-dmabuf-unstable-v1.
2021-07-05 11:13:41 -04:00
Simon Ser c868e509b7 drm: add support for DMA-BUFs
Mesa's Vulkan WSI still uses wl_drm when modifiers aren't supported.
This has been fixed in [1] but will take some time to be propagated
to users. In the meantime, add a fallback.

[1]: https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/4942
2021-07-05 11:13:41 -04:00
Simon Ser f7e3d325fe Add a stub wl_drm implementation
This will allow us to remove all of our EGL wl_drm support code
and remove some weird stuff we need just for wl_drm support. In
particular, wl_drm buffers coming from the EGL implementation
can't easily be wrapped into a wlr_buffer properly.
2021-07-05 11:13:41 -04:00
Simon Ser 78121ad2d8 xcursor: improve documentation 2021-07-05 11:00:15 -04:00
Simon Ser 4dee7a2f6f backend/drm: don't clear pending cursor FB on failed commit
The previous fix tried to side-step cursor->pending_fb completely.
However that messes up our buffer locking mechanism.

Instead, stop clearing the pending cursor FB on a failed commit. The
pending cursor FB will remain for the next commit.

Fixes: 6c3d080e25 ("backend/drm: populate cursor plane's current_fb")
2021-07-05 10:55:41 -04:00
Simon Ser 5f26360bd8 Revert "backend/drm: populate cursor plane's current_fb"
This reverts commit 6c3d080e25.

Populating wlr_drm_plane.current_fb messes up the buffer's locking.
The previous buffer is released while it's still being displayed
on-screen.
2021-07-05 10:55:41 -04:00
Simon Ser e8c408b31b region: drop leftover reference to the mailing list
As per [1], the mailing list isn't used anymore.

[1]: https://github.com/swaywm/wlroots/pull/3016

Fixes: 82af6e7208 ("region: stabilize interface")
2021-07-05 10:32:50 -04:00
Simon Ser 82af6e7208 region: stabilize interface
References: https://github.com/swaywm/wlroots/issues/1008
2021-07-05 09:25:25 -04:00
Simon Ser 475d9701e2 Remove reference to the announce mailing list
The mailing list has never been used.

I think listing the deprecated functions in the release notes is
enough. I'd rather not add the burden of maintaining a separate
communication medium.
2021-07-05 09:24:24 -04:00
Simon Ser c7018a45b7 surface: allow locking pending state in commit handler
We were bumping the pending sequence number after emitting the
commit event, so commit handlers were seeing inconsistent state
where current.seq == pending.seq. This prevents commit handlers
from immediately locking the pending state.

Fix this by bumping the pending sequence number before firing the
commit event.
2021-07-05 10:20:19 +02:00
Simon Ser 0a522cb798 backend/libinput: use wl_array for wlr_libinput_tablet.tools
Instead of using a single-field wl_list, let's just use a wl_array.
2021-07-05 10:05:06 +02:00
Simon Ser a38baec1f8 buffer: make enum wlr_buffer_cap public
Custom backends and renderers need to implement
wlr_backend_impl.get_buffer_caps and
wlr_renderer_impl.get_render_buffer_caps. They can't if enum
wlr_buffer_cap isn't made public.
2021-07-01 16:40:19 -04:00
Simon Ser 29be2d47e4 render: drop wlr_renderer_impl.texture_from_dmabuf 2021-07-01 14:57:52 -04:00
Simon Ser 7ad44051a2 render: use wlr_dmabuf_buffer in wlr_texture_from_dmabuf 2021-07-01 14:57:52 -04:00
Simon Ser 08e5b909f9 buffer: add wlr_dmabuf_buffer 2021-07-01 14:57:52 -04:00
Simon Ser 18adb43a44 render: drop wlr_renderer_impl.texture_from_pixels 2021-07-01 14:57:52 -04:00
Simon Ser 29c8df7e0a render: use wlr_readonly_data_buffer in wlr_texture_from_pixels 2021-07-01 14:57:52 -04:00
Simon Ser 7ec66a9990 buffer: introduce wlr_readonly_data_buffer 2021-07-01 14:57:52 -04:00
Simon Ser ea585dba0f tablet-v2: fix pad and tool object versions
These were hardcoded to 1. Instead, create the resource with the
version of the parent object.
2021-07-01 10:52:58 -04:00
Simon Ser 57b70a478c Drop wlr_list 2021-07-01 10:35:39 -04:00
Simon Ser 5888c96da8 tablet: stop using wlr_list 2021-07-01 10:35:39 -04:00
Simon Ser e6cb11d882 backend/libinput: stop using wlr_list internally 2021-07-01 10:35:39 -04:00
Simon Ser a6ed4ae308 util/array: add array_remove_at 2021-07-01 10:35:39 -04:00
Simon Ser dbb0e2f75b Remove unused wlr_list.h includes 2021-07-01 10:35:39 -04:00
Simon Ser 1db976cecb render/egl: replace wlr_egl_create with wlr_egl_create_with_drm_fd
We never create an EGL context with the platform set to something
other than EGL_PLATFORM_GBM_KHR. Let's simplify wlr_egl_create by
taking a DRM FD instead of a (platform, remote_display) tuple.

This hides the internal details of creating an EGL context for a
specific device. This will allow us to transparently use the device
platform [1] when the time comes.

[1]: https://github.com/swaywm/wlroots/pull/2671
2021-06-30 14:02:26 -04:00
Simon Ser 1c4b5bcab3 surface: accept commits with buffer size not divisible by scale
There are still many situations where the buffer scale is not
divisible by scale. The fix will require a tad more work, so
let's just log the client error for now and continue handling
the surface commit as usual.

Closes: https://github.com/swaywm/sway/issues/6352
2021-06-30 13:27:42 -04:00
Simon Ser 1b4fb4b537 touch: document event data types 2021-06-30 11:24:08 +02:00
Simon Ser bcbdee43f7 pointer: document event data types 2021-06-30 11:23:50 +02:00
Simon Ser 7cbcc65ad0 surface: make wlr_subsurface_create private
Same as [1], but for wlr_subsurface.

[1]: https://github.com/swaywm/wlroots/pull/2814
2021-06-29 10:32:17 -04:00
Simon Ser ddc98bf593 surface: remove SURFACE_VERSION
surface_create is now private, so no need to worry about the version
being too high anymore.
2021-06-29 13:34:30 +02:00
Simon Ser 634a20d89c Drop WLR_VERSION_API_*
These weren't set in the build, thus Meson was just dropping them
in the generated file.
2021-06-29 12:22:55 +02:00
Simon Ser f6ae028e99 ci: make Meson warnings fatal
New warnings can be hard to notice in CI, since CI will just pass in
that case. Meson sometimes uses warnings for important mistakes, e.g.
invalid option.

Let's turn warnings into errors so that we can spot these more easily.
2021-06-25 10:01:25 -04:00
Simon Ser 787842c459 ci: remove -Dlibseat from Alpine build
This build option doesn't exist anymore.
2021-06-25 10:01:25 -04:00
Simon Ser 31db232704 build: use meson.global_build_root()
meson.build_root() is deprecated.

References: https://github.com/mesonbuild/meson/pull/8629
2021-06-25 10:01:25 -04:00
Simon Ser d2b6b570ea xwayland: improve startup log message
Logging the raw Xwayland command-line was incomplete, uninformative
and confusing for end-users. Instead, print a proper message in
English.
2021-06-25 10:54:10 +02:00
Simon Ser a2419eb4ea render/egl: make most functions private
The wlr_egl functions are mostly used internally by the GLES2
renderer. Let's reduce our API surface a bit by hiding them. If
there are good use-cases for one of these, we can always make them
public again.

The functions mutating the current EGL context are not made private
because e.g. Wayfire uses them.
2021-06-24 13:53:05 -04:00
Simon Ser b69db15da6 render/egl: remove stale wlr_egl_export_image_to_dmabuf decl
This function has been removed, but we forgot to drop it from the
header.
2021-06-24 11:57:14 +02:00
Simon Ser 264d4e2bce backend/drm: rename page_flip_handler to handle_page_flip
This is more consistent with the rest of the wlroots naming.
2021-06-24 11:56:45 +02:00
Simon Ser 0467a7523a build: bump version to 0.15.0 2021-06-23 14:30:57 +02:00
Simon Ser 8810e95082 Revert "build: workaround for meson disabler object not working with if not"
This reverts commit 9796abcced.

This Meson issue has been fixed upstream for a while. We require
0.56.0 so we should never hit an unpatched Meson.
2021-06-22 21:20:20 +02:00
Simon Ser 2f615468b6 backend: add output state allow-lists
Right now, when a new output state field is added, all backends by
default won't reject it. This means we need to add new checks to
each and every backend when we introduce a new state field.

Instead, introduce a bitmask of supported output state fields in
each backend, and error out if the user has submitted an unknown
field.

Some fields don't need any backend involvment to work. These are
listed in WLR_OUTPUT_STATE_BACKEND_OPTIONAL as a convenience.
2021-06-20 23:17:08 +02:00
Kenny Levinsen 15c8453ba1 Revert "meson: Make private static library symbols local"
This reverts commit 28d23ba6bda4f799b8d6689555cd33a40adda17e.

The prelinking and symbol filtering pass breaks builds with link-time
optimization enabled.
2021-06-20 21:04:23 +02:00
zccrs 3c03639cd5 render: add get native paint target of renderer
Add wlr_pixman_buffer_get_current_image for wlr_pixman_renderer.
Add wlr_gles2_buffer_get_current_fbo for wlr_gles2_renderer.

Allow get the FBO/pixman_image_t, the compositor can be add some
action for FBO(for eg, attach a depth buffer), or without pixman
render to pixman_image_t(for eg, use QPainter of Qt instead of pixman).
2021-06-19 10:04:35 +02:00
zccrs dc17ecd236 render/pixman: add wlr_*_is_pixman and wlr_pixman_texture_get_image
Add the following functions:

- wlr_renderer_is_pixman
- wlr_texture_is_pixman
- wlr_pixman_texture_get_image
2021-06-19 10:04:35 +02:00
zccrs fdc40e071e render/gles2: add wlr_renderer_is_gles2
Export the interface used to determine whether the wlr_renderer object
is gles2.
2021-06-19 10:04:35 +02:00
Simon Ser 103edde481 xdg-activation-v1: fix wlr_xdg_activation_v1_create error path 2021-06-17 14:48:28 +02:00
Simon Ser f6e680ef94 build: simplify version script
Let's expose all of our prefixed symbols. Instead of trying to have
fine-grained rules and only expose our public API, let's just expose
all symbols that won't cause a conflict.

Users won't be able to use the symbols without a proper header
declaration anyways. If they go through the process of re-defining
wlr_ symbols manually, that's on them if their build breaks.

This aligns the rules with [1].

[1]: https://github.com/swaywm/wlroots/pull/2969
2021-06-17 11:03:21 +02:00
Kenny Levinsen 8d2a94b0df ci: Build both static and shared
This allows us to validate the objcopy trick in CI. It's basically free
to build both static and shared libraries anyway.

We only enable this for GCC builds right now, as Meson currently lacks
support for prelinking with other compilers.
2021-06-17 11:02:30 +02:00
Kenny Levinsen cb6db86a28 meson: Make private static library symbols local
Static libraries are not affected by our symbol file, so private symbols
are globally visible by default.

Use objcopy to make symbols that we do not want to expose local.

Closes: https://github.com/swaywm/wlroots/issues/1892
Closes: https://github.com/swaywm/wlroots/issues/2952
2021-06-17 11:02:30 +02:00
Simon Ser 72ee196efa backend/session: use DRM_PRIMARY_MINOR_NAME
Instead of hardcoding the string "card", use DRM_PRIMARY_MINOR_NAME.
Some systems may use another prefix, e.g. OpenBSD uses "drm" instead.
2021-06-17 00:28:04 +02:00
Simon Ser fb933d3204 backend/session: use drmIsKMS
This moves the magic incantation into libdrm and is clearer. See
[1] for details.

While at it, fixup the doc comment and improve logging.

[1]: 523b3658aa
2021-06-17 00:27:12 +02:00
Simon Ser 6c3d080e25 backend/drm: populate cursor plane's current_fb
The set_cursor() hook is a little bit special: it's not really
synchronized to commit() or test(). Once set_cursor() returns true,
the new cursor is part of the current state.

This fixes a state where wlr_drm_connector.cursor_enabled is true
but there is no FB available. This is triggered by set_cursor()
followed by a failed commit(), which resets pending_fb.

We should definitely fix the output interface to make the cursor part
of the pending state, but that's a more involved change.
2021-06-17 00:25:27 +02:00
Simon Ser 6259fd23fb output: reset back buffer on failed commit
On commit failure, we need to unbind the back buffer from the
renderer.

This fixes assertions triggered on commits following a failed commit
where the compositor called wlr_output_attach_render.
2021-06-13 13:17:30 +02:00
Simon Ser 3345eaca89 backend/drm: remove test_buffer
Instead, call drm_connector_set_pending_fb.
2021-06-13 10:57:17 +02:00
Simon Ser eca5d2f37f backend/drm: move session check from test_buffer to drm_connector_test
The other caller (drm_connector_commit_state) already checks this.
2021-06-13 10:57:17 +02:00
Simon Ser 758f117442 backend/drm: move drm_connector_set_pending_fb up
This will be used in drm_connector_test shortly.
2021-06-13 10:57:17 +02:00
Simon Ser f55b43ddd6 backend/drm: allocate a CRTC in drm_connector_test
We can't perform a test-only atomic commit if the connector is
missing a CRTC.
2021-06-13 10:57:17 +02:00
Simon Ser cb378600e4 backend/drm: allocate a CRTC in drm_connector_commit_state
drm_connector_set_pending_fb needs a CRTC to import the buffer.
2021-06-13 10:57:17 +02:00
Simon Ser b180d3482f backend/drm: introduce drm_connector_alloc_crtc
This function allocates a CRTC for a connector if necessary.
2021-06-13 10:57:17 +02:00
Simon Ser 63f891e393 backend/drm: allow committing a buffer and a mode together
Set the plane's pending FB before calling drm_connector_set_mode.
2021-06-13 10:57:17 +02:00
Simon Ser e89cf5f047 backend/drm: use atomic test-only commits for modifier fallback
Instead of trying to perform a real modeset in init_renderer,
perform an atomic test-only commit to find out whether disabling
modifiers is necessary because of bandwidth limitations.

This decouples init_renderer from the actual commit, making it
possible to modeset an output with a user-supplied buffer instead
of a black frame.

We loose the ability to make sure the buffers coming from the
swapchain will work fine when using the legacy interface. This
can break i915 when atomic is disabled and modifiers enabled.
But i915 always has atomic (so the user must explicitly disable it
to run into potential bandwidth limitations) and is the only known
problematic driver.
2021-06-13 10:57:17 +02:00
Simon Ser 2806154900 render: add missing arg to wlr_renderer_impl.get_buffer_caps
The types of buffers supported by the renderer might depend on the
renderer's instance. For instance, a renderer might only support
DMA-BUFs if the necessary EGL extensions are available.

Pass the wlr_renderer to get_buffer_caps so that the renderer can
perform such checks.

Fixes: 982498fab3 ("render: introduce renderer_get_render_buffer_caps")
2021-06-09 16:41:03 +02:00
Simon Ser b2f6db3533 render: drop wlr_ prefix from wlr_renderer_bind_buffer
Make it clear this function is a private wlroots API and will stay
that way.
2021-06-09 10:26:09 +02:00
Simon Ser c87c849ec6 backend/x11: use wlr_renderer_begin_with_buffer for cursor 2021-06-09 10:26:09 +02:00
Simon Ser fbadadf36f output: use wlr_renderer_begin_with_buffer for cursor 2021-06-09 10:26:09 +02:00
Simon Ser a667175ec7 screencopy-v1: use wlr_renderer_begin_with_buffer 2021-06-09 10:26:09 +02:00
Simon Ser 69477051cc matrix: deprecate wlr_matrix_projection
This function has baked-in GL assumptions. Compositors shouldn't
need to use it.
2021-06-07 22:55:46 +02:00
Simon Ser 543f5b35d0 backend/wayland: remove swapchain
Rely on wlr_output's generic swapchain handling.
2021-06-07 15:42:38 +02:00
Simon Ser 5f8092b045 backend/wayland: implement get_dmabuf_primary_formats 2021-06-07 15:42:38 +02:00
Simon Ser a670ee7940 backend/x11: remove swapchain
Rely on wlr_output's generic swapchain handling.

We still need a renderer for cursor readback, sadly.
2021-06-07 15:42:38 +02:00
Simon Ser 68c4f15958 backend/x11: implement get_dmabuf_primary_formats 2021-06-07 15:42:38 +02:00
Simon Ser 44feb832f9 backend/headless: remove swapchain
Rely on wlr_output's generic swapchain support instead of creating our
own. The headless output now simply keeps a reference to the front buffer
and does nothing else.
2021-06-07 15:42:38 +02:00
Simon Ser 1a06ea7750 output: make attach_render and rollback_render optional
If these aren't provided by the backend, allocate a swapchain for the
output.
2021-06-07 15:42:38 +02:00
Simon Ser 233a2617cf output: split output_pick_cursor_format
Introduce output_pick_format that can be re-used for the primary
buffer too.
2021-06-07 15:42:38 +02:00
Simon Ser 4d603826c8 output: add get_primary_formats to interface
This function returns the set of formats the backend can use for the
primary buffer. It can be used to allocate a buffer suitable for
scan-out.
2021-06-07 15:42:38 +02:00
Simon Ser 534615cd55 buffer: use wlr_texture_from_buffer for wl_shm_buffer 2021-06-07 09:22:56 -04:00
Simon Ser 7c26345826 buffer: introduce wlr_shm_client_buffer
Introduce wlr_shm_client_buffer, which provides a wlr_buffer wrapper
around wl_shm_buffer.

Because the client can destroy the wl_buffer while we still are using
it, we need to do some libwayland tricks to still be able to continue
accessing its underlying storage. We need to reference the wl_shm_pool
and save the data pointer.
2021-06-07 09:22:56 -04:00
Simon Ser 625c66ef75 render/pixman: implement texture_from_buffer 2021-06-07 09:22:56 -04:00
Simon Ser 6e43d642b2 render/gles2: add support for DATA_PTR buffers in texture_from_buffer 2021-06-07 09:22:56 -04:00
Simon Ser 38ba5881a0 buffer: replace get_data_ptr with {begin,end}_data_ptr_access
This new API allows buffer implementations to know when a user is
actively accessing the buffer's underlying storage. This is
important for the upcoming client-backed wlr_buffer implementation.
2021-06-07 09:22:56 -04:00
Simon Ser 9e58301df7 surface: allow placing subsurfaces below parent
Prior to this commit, subsurfaces could only be placed above their
parent. Any place_{above,below} request involving the parent would
fail with a protocol error.

However the Wayland protocol allows using the parent surface in the
place_{above,below} requests, and allows subsurfaces to be placed
below their parent.

Weston's implementation adds a dummy wl_list node in the subsurface
list. However this is potentially dangerous: iterating the list
requires making sure the dummy wl_list node is checked for, otherwise
memory corruption will happen.

Instead, split the list in two: one for subsurfaces above the parent,
the other for subsurfaces below.

Tested with wleird's subsurfaces demo client.

Closes: https://github.com/swaywm/wlroots/issues/1865
2021-06-03 14:04:07 +02:00
zccrs 11040d4942 Make the xdg_popup_get_position to public
Rename the xdg_popup_get_position to
wlr_xdg_popup_get_position
2021-06-03 09:50:54 +02:00
Simon Ser 76f51a949f xdg-activation-v1: add token timeout
There isn't always a good time to prune old tokens. Compositors
which only implement a "give focus on activation" logic can prune
tokens on focus change. However other compositors might want to
implement other semantics, e.g. "mark urgent on activation". In this
case a focus change shouldn't invalidate other tokens.

Additionally, some tokens aren't necessarily tied to a seat.

To avoid ending up with an ever-growing list of tokens, add a timeout.
2021-06-02 11:18:25 +02:00
Simon Ser 8ff435831f xdg-activation-v1: new protocol implementation
This implements the new xdg-activation-v1 protocol [1].

[1]: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/50
2021-06-02 11:18:25 +02:00
Simon Ser b86a0c8d8f backend/drm: move cursor fields to wlr_drm_connector
Doesn't make a lot of sense to split the cursor fields between
wlr_drm_plane and wlr_drm_connector. Let's just move everything to
wlr_drm_connector.
2021-06-02 11:08:52 +02:00
Simon Ser 2b0a1aeed5 output: take a wlr_buffer in set_cursor
Instead of passing a wlr_texture to the backend, directly pass a
wlr_buffer. Use get_cursor_size and get_cursor_formats to create
a wlr_buffer that can be used as a cursor.

We don't want to pass a wlr_texture because we want to remove as
many rendering bits from the backend as possible.
2021-06-02 11:08:52 +02:00
Simon Ser 01e0f51fad backend/drm: introduce drm_plane_pick_render_format
This is a new helper function to pick a render format suitable for
a plane.

The next commit will use it to initialize the cursor multi-GPU
surface.
2021-06-02 11:08:52 +02:00
Simon Ser e06ea4e84a backend/drm: remove format arg from drm_plane_init_surface
This was always set to ARGB8888.
2021-06-02 11:08:52 +02:00
Simon Ser 9e9be83a58 backend/drm: implement get_cursor_formats and get_cursor_size 2021-06-02 11:08:52 +02:00
Simon Ser de51df2770 backend/wayland: implement get_cursor_formats 2021-06-02 11:08:52 +02:00
Simon Ser 91ee33e956 output: add get_cursor_formats and get_cursor_size to interface 2021-06-02 11:08:52 +02:00
Simon Ser 6430230d1f render: add wlr_renderer_begin_with_buffer
This allows compositors to choose a wlr_buffer to render to. This
is a less awkward interface than having to call bind_buffer() before
and after begin() and end().

Closes: https://github.com/swaywm/wlroots/issues/2618
2021-06-01 13:12:28 -04:00
Simon Ser b732f094c6 render: disallow wlr_renderer_destroy while rendering
This probably already felt apart, but let's make it explicit that
this is not allowed.
2021-06-01 13:11:24 -04:00
Simon Ser ce3e819b33 backend: stop using renderer to get the buffer type
When picking a format, the backend needs to know whether the
buffers allocated by the allocator will be DMA-BUFs or shared
memory. So far, the backend used the renderer's supported
buffer types to guess this information.

This is pretty fragile: renderers in general don't care about the
SHM cap (they only care about the DATA_PTR one). Additionally,
nothing stops a renderer from supporting both DMA-BUFs and shared
memory, but this would break the backend's guess.

Instead, use wlr_allocator.buffer_caps. This is more reliable since
the buffers created with the allocator are guaranteed to have these
caps.
2021-05-31 15:50:13 -04:00
Simon Ser 766a24fa77 render/allocator: add wlr_allocator.buffer_caps
This allows users to know the capabilities of the buffers that
will be allocated. The buffer capability is important to
know when negotiating buffer formats.
2021-05-31 15:50:13 -04:00
Simon Ser 5c30cf3d94 render/drm_dumb_allocator: check for DRM master
If we aren't the DRM master, allocating dumb buffers will fail with
EPERM.
2021-05-31 15:50:13 -04:00
Tudor Brindus 6605d7c390 xwm: prevent X11 clients from blowing our stack by opening too many windows
Allocate window arrays for list property updates on the heap instead.
2021-05-31 10:41:29 +02:00
Tudor Brindus ae2f3ecb68 xwm: implement _NET_CLIENT_LIST_STACKING
This property is present on all modern X11 instances. The nonpresence of
it requires applications to fall back to XQueryTree-based logic to
determine stacking logic (e.g., to determine what surface should get
Xdnd events).

These code paths are effectively untested nowadays, so this makes it
more likely for wlroots to "break" applications. For instance, the
XQueryTree fallback path has been broken in Chromium for the last 10
years.

It's easy enough to maintain this property, so let's just do it.

Fixes #2889.
2021-05-31 10:41:29 +02:00
Tudor Brindus 699d724000 xwm: use correct list link when iterating over `unpaired_surfaces` 2021-05-31 10:41:29 +02:00
Simon Ser abf527b075 render/gles2: fix texture cleanup on destroy
When importing a DMA-BUF wlr_buffer as a wlr_texture, the GLES2
renderer caches the result, in case the buffer is used for texturing
again in the future. When the wlr_texture is destroyed by the caller,
the wlr_buffer is unref'ed, but the wlr_gles2_texture is kept around.
This is fine because wlr_gles2_texture listens for wlr_buffer's destroy
event to avoid any use-after-free.

However, with this logic wlr_texture_destroy doesn't "really" destroy
the wlr_gles2_texture. It just decrements the wlr_buffer ref'count.
Each wlr_texture_destroy call must have a matching prior
wlr_texture_create_from_buffer call or the ref'counting will go south.

Wehn destroying the renderer, we don't want to decrement any wlr_buffer
ref'count. Instead, we want to go through any cached wlr_gles2_texture
and destroy our GL state. So instead of calling wlr_texture_destroy, we
need to call our internal gles2_texture_destroy function.

Closes: https://github.com/swaywm/wlroots/issues/2941
2021-05-30 10:11:09 -04:00
Simon Ser d0560e2597 contributing: add link to IRC web chat 2021-05-26 22:21:06 +02:00
Simon Ser b3ff6db730 readme: fix broken IRC web client link
Previous commit has tweaked the link name.
2021-05-26 22:19:41 +02:00
Simon Ser 8fa4a6b303 readme: add link to IRC web client 2021-05-26 22:15:53 +02:00
Simon Zeni e6c00f7eea Update IRC links 2021-05-24 10:04:17 +02:00
Brian McKenna 2fd20b17b6 Flush events in virtual pointer example 2021-05-22 13:36:30 +02:00
Simon Ser fd2b1f018e backend/x11: use common renderer and allocator
Instead of managing our own renderer and allocator, let the common
code do it.
2021-05-21 22:13:54 +02:00
Simon Ser c82f37542d backend/headless: use common renderer and allocator
Instead of managing our own renderer and allocator, let the common
code do it.

Because wlr_headless_backend_create_with_renderer needs to re-use
the parent renderer, we have to hand-roll some of the renderer
initialization.
2021-05-21 22:13:54 +02:00
Simon Ser 349553d011 backend/wayland: use common renderer and allocator
Instead of managing our own renderer and allocator, let the common
code do it.
2021-05-21 22:13:54 +02:00
Simon Ser 4dae12890f backend: automatically create allocator
Introduce a new backend_get_allocator function that automatically
creates an allocator for the backend if the backend has a renderer.
2021-05-21 22:13:54 +02:00
Simon Ser bcabe34a2e backend: automatically create renderer
If a backend accepts buffers (as indicated by get_buffer_caps) but
doesn't implement get_renderer, automatically create a renderer.
2021-05-21 22:13:54 +02:00
Simon Ser 7ec5bf6b10 backend: introduce wlr_backend_finish
This new functions cleans up the common backend state. While this
currently only emits the destroy signal, this will also clean up
the renderer and allocator in upcoming patches.
2021-05-21 22:13:54 +02:00
Simon Ser beae3018cb render: relax stride check in wlr_texture_from_pixels
Some formats have a byte-per-pixel lower than 1. Let's not encode
an arbitrary limitation into the wlr_renderer API.
2021-05-19 11:13:42 -04:00
Simon Ser f73c04b801 render/pixman: avoid sqrt() in render_quad without rotation
When the matrix doesn't have a rotation, we can avoid a sqrt() call.

Tested with Sway's tabbed containers.
2021-05-19 09:57:37 -04:00
Simon Ser 66e100ffbf render/gbm_allocator: add support for gbm_bo_get_fd_for_plane
See [1]. This allows us to remove the workaround for GBM API
limitations.

[1]: https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/5442
2021-05-19 10:17:28 +02:00
Simon Ser 9ca743f9fd backend/drm: use wlr_texture_from_buffer 2021-05-17 16:22:43 +02:00
Simon Ser a8d7c2d4ea screencopy-v1: stop using wlr_client_buffer
We can just use the wlr_dmabuf_v1_buffer directly, no need to wrap
it in a wlr_client_buffer.
2021-05-17 16:22:43 +02:00
Simon Ser 68758e8c21 buffer: use wlr_texture_from_buffer for DMA-BUFs 2021-05-17 16:22:43 +02:00
Simon Ser f6ba26ff58 render/gles2: implement texture_from_buffer
Make it so wlr_gles2_texture is ref'counted (via wlr_buffer). This
is similar to wlr_gles2_buffer or wlr_drm_fb work.

When creating a wlr_texture from a wlr_buffer, first check if we
already have a texture for the buffer. If so, increase the
wlr_buffer ref'count and make sure any changes made by an external
process are made visible (by invalidating the texture).

When destroying a wlr_texture created from a wlr_buffer, decrease
the ref'count, but keep the wlr_texture around in case the caller
uses it again. When the wlr_buffer is destroyed, cleanup the
wlr_texture.
2021-05-17 16:22:43 +02:00
Simon Ser 9d55f712e3 render: introduce wlr_texture_from_buffer
This adds a a function to create a wlr_texture from a wlr_buffer.

The main motivation for this is to allow the renderer to create a
single wlr_texture per wlr_buffer. This can avoid needless imports
by re-using existing textures.
2021-05-17 16:22:43 +02:00
Simon Ser 9221ed7b4c render/gles2: add gles2_texture_create
This centralizes the wlr_texture initialization.

In future commits, more fields will need to get initialized.
2021-05-17 16:22:43 +02:00
Simon Ser 6f39574ff5 linux-dmabuf-v1: implement wlr_buffer
This allows the DMA-BUF wl_buffer objects to be used directly as
wlr_buffers, without having to use wlr_client_buffer_import.
2021-05-17 16:22:43 +02:00
Simon Ser 6f69e2f12e render/gles2: remove unnecessary EGL import ext checks
We require the ext in the renderer init function.
2021-05-17 10:09:22 -04:00
Simon Ser 6369f70931 render: remove wlr_texture_get_size
Users can just access the width/height fields directly.
2021-05-17 10:03:17 +02:00
Thomas Weißschuh 9f211b5dd4 xwayland: actually use Xwayland from pkg-config
eec2e1d3b1 introduced logic to use the Xwayland
binary discovered via pkg-config.
While the newly introduced checks correctly used the binary from pkg-config,
the actual execution still used the previous PATH-search logic.
2021-05-12 10:55:15 +02:00
Simon Ser 101b9a193d render/egl: query and display EGL driver name
GL_RENDERER typically displays a human-readable string for the name
of the GPU, and EGL_VENDOR typically displays a human-readable string
for the GPU manufacturer. EGL_DRIVER_NAME_EXT should give the name of
the driver in use.

References: e8baa0bf39
2021-05-11 12:43:25 +02:00
Yuya Nishihara 8008d21f5b virtual_keyboard: Emulate key release events on destroy
According to libinput, release events are generated when device is unplugged,
and libinput copies this behavior for device removal. Let's do the same for
our virtual keyboard.

8f846a41fa

This is another attempt to fix #2034 and the following sway issue:

https://github.com/swaywm/sway/issues/6254

Note that we have other key repeating issues in sway, which aren't addressed
by this patch. Since the virtual keyboard itself isn't destroyed when the
keyboard grab is destroyed, we'll probably need some trick to reset the state
of the corresponding virtual keyboard when the grab is released.

https://github.com/swaywm/sway/issues/6095
https://github.com/swaywm/sway/issues/6193
2021-05-07 11:18:19 +02:00
Simon Zeni ed7f2651b6 render: add DRM dumb buffer allocator 2021-05-05 10:40:21 +02:00
Simon Zeni 2c90e0f521 render/gbm_allocator: duplicate drm fd during creation process 2021-05-05 10:40:21 +02:00
Tobias Stoeckmann d0c1f0c0b6 xcursor: fix CVE-2013-2003
The libXcursor fix for CVE-2013-2003 has never been imported into
wlroots, leaving it vulnerable to it.

Changing the argument type to an unsigned type is an effective merge of
Ilja Van Sprundel's commit in libXcursor.

Proof of Concept (compile with address sanitizer):

$ mkdir -p ~/.local/share/icons/poc/cursors
$ base64 -d <<< WGN1chAAAAAAAAAA/////w== > \
    ~/.local/share/icons/poc/cursors/poc
$ echo "seat seat0 xcursor_theme poc 10" > ~/poc-config
$ sway -c ~/poc-config
2021-05-02 17:04:59 +02:00
Simon Ser 66d5805594 build: move wayland-protocols dep to protocol/ 2021-05-01 12:33:50 +02:00
Kenny Levinsen 2603a5dee7 backend/drm: Do not require mode commit on enable
If a mode is not provided, use the current mode intead.

Closes: https://github.com/swaywm/wlroots/issues/2904
2021-04-30 21:33:28 +02:00
Simon Ser c85789a3a9 backend/headless: dup DRM FD in wlr_headless_backend_create_with_renderer
We were not dup'ing the DRM FD coming from wlr_renderer_get_drm_fd,
resulting in a double-close on backend destroy.
2021-04-29 19:59:42 +02:00
Simon Ser a1e8a639b3 backend/drm: introduce drm_connector_commit_state
Backend-initiated mode changes can use this function instead of
going through drm_connector_set_mode. drm_connector_set_mode becomes
a mere drm_connector_commit_state helper.
2021-04-29 19:51:57 +02:00
Simon Ser e543e26206 backend/drm: take a wlr_drm_connector instead of a wlr_output
No need to take a generic wlr_output, we already know the output
comes from the DRM backend at that point.
2021-04-29 19:51:57 +02:00
Simon Ser e06c62af77 backend/drm: take output state arg in drm_connector_commit_buffer
This will allow a whole state to be applied at once, instead of
individually applying the buffer and the mode.
2021-04-29 19:51:57 +02:00
Simon Ser 218955ce95 backend/drm: remove mode arg from drm_connector_set_mode
All of the information is in wlr_output_state.
2021-04-29 19:51:57 +02:00
Simon Ser 7aba881c47 backend/drm: remove mode arg from drm_connector_init_renderer
This is now unused.
2021-04-29 19:51:57 +02:00
Simon Ser 31082a0554 backend/drm: remove wlr_drm_crtc_state.mode
Replace it with drm_connector_state_mode, which computes the mode
from the wlr_output_state to be applied.
2021-04-29 19:51:57 +02:00
Simon Ser dfea0ff31d backend/drm: remove wlr_drm_crtc_state.active
Replace it with drm_connector_state_active, which figures out
whether the connector is active depending on the wlr_output_state
to be applied.
2021-04-29 19:51:57 +02:00
Simon Ser 485ecc11a6 backend/drm: remove wlr_drm_crtc.pending_modeset
Replace it with a new drm_connector_state_is_modeset function that
decides whether a modeset is necessary directly from the
wlr_output_state which is going to be applied.
2021-04-29 19:51:57 +02:00
Simon Ser c9c2d7539c backend/drm: fill scratch wlr_output_state for set_mode
Populate the wlr_output_state when setting a mode. This will allow
drm_connector_set_mode to stop relying on ephemeral fields in
wlr_drm_crtc. Also drm_connector_set_mode will be able to apply
both a new buffer and a new mode atomically.
2021-04-29 19:51:57 +02:00
Simon Ser 1a9701cd7c backend/drm: take wlr_output_state as arg in commit callers
Instead of relying on wlr_output.pending to be empty when performing
backend-initiated CRTC commits, use a zero wlr_output_state.
2021-04-29 19:51:57 +02:00
Simon Ser 8f90d7f8f5 backend/drm: take wlr_output_state as arg in crtc_commit
Stop assuming that the state to be applied is in output->pending in
crtc_commit. This will allow us to remove ephemeral fields in
wlr_drm_crtc, which are used scratch fields to stash temporary
per-commit data.
2021-04-29 19:51:57 +02:00
Simon Ser 69d4cf19b5 render/gles2: assert texture comes from the same renderer
Rendering a wlr_texture with a different wlr_renderer is invalid.
Add an assert to make sure this doesn't happen.
2021-04-29 15:59:13 +02:00
Simon Ser e7f68ba081 backend/drm: fix allocator DRM FD on multi-GPU setups
On multi-GPU setups, there is a primary DRM backend and secondary
DRM backends. wlr_backend_get_drm_fd will always return the parent
DRM FD even on secondary backends, so that users always use the
primary device for rendering.

However, for our internal rendering we want to use the secondary
device. Use allocator_autocreate_with_drm_fd to make sure the
allocator will create buffers on the secondary device.

We do something similar to ensure our internal rendering will
happen on the secondary device with renderer_autocreate_with_drm_fd.

Fixes: cc1b66364c ("backend: use wlr_allocator_autocreate")
2021-04-29 15:58:56 +02:00
Simon Ser 5be76bb047 render/allocator: add allocator_autocreate_with_drm_fd
Same as wlr_allocator_autocreate, but allows the caller to force a
DRM FD.

Similar to renderer_autocreate_with_drm_fd.
2021-04-29 15:58:56 +02:00
Simon Ser 619a975025 render: remove wlr_ prefix from wlr_renderer_autocreate_with_drm_fd
This function is only required because the DRM backend still needs
to perform multi-GPU magic under-the-hood. Remove the wlr_ prefix
to make it clear it's not a candidate for being made public.
2021-04-29 09:46:34 +02:00
Simon Ser 6bf2406dbf backend/drm: reword wlr_renderer failure message
Remove the assumption about EGL.
2021-04-29 09:38:10 +02:00
Simon Ser 1c1ef69326 Log when WLR_BACKENDS/WLR_RENDERER is set
Makes it easier to figure out why a backend/renderer is picked.
2021-04-28 21:06:41 +02:00
Simon Zeni cc1b66364c backend: use wlr_allocator_autocreate 2021-04-28 20:55:57 +02:00
Simon Zeni 318e3ac92c render/allocator: introduce wlr_allocator_autocreate 2021-04-28 20:55:57 +02:00
Simon Zeni 982498fab3 render: introduce renderer_get_render_buffer_caps 2021-04-28 20:55:57 +02:00
Simon Zeni 144189674e backend: introduce backend_get_buffer_caps 2021-04-28 20:55:57 +02:00
Simon Zeni 6ec6527855 types/wlr_buffer: introduce wlr_buffer_cap 2021-04-28 20:55:57 +02:00
Simon Zeni a8c91fbac9 render/shm_allocator: make wlr_shm_allocator_create return a wlr_allocator 2021-04-28 20:55:57 +02:00
Simon Zeni c75aa71816 render/gbm_allocator: make wlr_gbm_allocator_create return a wlr_allocator 2021-04-28 20:55:57 +02:00
Simon Ser 3a04fb4560 render/pixman: check format is supported in create_buffer 2021-04-27 20:36:03 +02:00
Simon Ser 24fde77c62 buffer: add format param to get_data_ptr
Allow wlr_buffer_impl.get_data_ptr to return a format.

This allows the Pixman renderer to not care about get_dmabuf/get_shm,
and only care about get_data_ptr. This will also help with [1], because
client wl_shm buffers can't implement get_shm.

[1]: https://github.com/swaywm/wlroots/pull/2892

References: https://github.com/swaywm/wlroots/issues/2864
2021-04-27 20:36:03 +02:00
Simon Zeni ccbce0f0a6 types/wlr_screencopy_v1: log error on read pixels failure 2021-04-27 18:28:41 +02:00
Simon Zeni 144b41a45c pixman: implement read pixels 2021-04-27 18:28:41 +02:00
Simon Zeni 30706b71fb render/pixman: implement preferred_read_format 2021-04-27 18:28:41 +02:00
Simon Ser 0411dc0663 Revert "backend/drm: fail instead of stripping a modifier"
This reverts commit f9f90b4173.

gbm_bo_get_modifier may return a modifier in these cases:

- The kernel doesn't support modifiers but Mesa does
- WLR_DRM_NO_MODIFIERS=1 is set

However, in both of these cases, the gbm_bo has been allocated
without modifiers.

There is already a check in drm_fb_create for modifiers:
wlr_drm_format_set_has will make sure buffers with an explicit
modifier will be rejected if the DRM backend doesn't support them.
So no need for an additional check in get_fb_for_bo.

Closes: https://github.com/swaywm/wlroots/issues/2896
2021-04-27 15:38:10 +02:00
Simon Ser 4839664a92 backend/drm: carry on when disabling a CRTC fails
On GPU unplug, disabling a CRTC can fail with EPERM.

References: https://github.com/swaywm/wlroots/pull/2575#issuecomment-761771264
2021-04-27 09:11:44 +02:00
Simon Ser 9b0e0970f9 backend/drm: destroy backend on udev remove event
Any use of the DRM FD after the remove event results in a "Permission
denied" error.
2021-04-27 09:11:44 +02:00
Simon Ser 5597776914 backend/session: add wlr_device.events.remove 2021-04-27 09:11:44 +02:00
Simon Ser c49ea9ef4f backend/drm: destroy when parent is destroyed 2021-04-27 09:11:44 +02:00
Simon Ser e804de923d backend/drm: clarify error message on drmModeAddFB fallback
The previous code would always print "falling back to legacy method",
even if the format wasn't ARGB8888.

Drop get_fb_for_bo_legacy, since the code can just be inlined without
hurting readability.

Ideally we should only fallback to drmModeAddFB if the error code
indicates the BE failure, but the original PR [1] doesn't say what
error code is returned by the kernel.

[1]: https://github.com/swaywm/wlroots/pull/2569
2021-04-26 23:12:53 +02:00
Simon Ser f9f90b4173 backend/drm: fail instead of stripping a modifier
We shouldn't strip a modifiers from buffers, because the will make
the kernel re-interpret the data as LINEAR on most drivers,
resulting in an incorrect output on screen.
2021-04-26 23:12:53 +02:00
Simon Ser 1a5530d14d xcursor: quiet debug log
Stop listing all available cursors each time a cursor theme is
loaded. They can be obtained from the CLI instead, e.g.

    ls /usr/share/icons/Adwaita/cursors
2021-04-26 20:11:05 +02:00
Simon Ser 5c699f09cb Log drmGetDevices2 error code 2021-04-26 16:27:24 +02:00
Simon Ser af78ecb86b render: unconditionally read WLR_RENDERER
Prior to this commit, WLR_RENDERER was only looked up when the
backend returned a DRM FD.

Make it so WLR_RENDERER is always looked up, so that running wlroots
on a system without a GPU and with WLR_RENDERER=gles2 fails as
expected instead of falling back to the Pixman renderer.
2021-04-26 15:05:34 +02:00
Simon Zeni 8a27050b4e render/egl: fail on EGL_MESA_device_software 2021-04-26 09:02:52 +02:00
Simon Ser ce30a22159 readme: use webchat link for IRC channel
GitHub refuses to render ircs:// URLs.
2021-04-24 12:46:29 +02:00
Simon Ser b5cfaea705 readme: mention IRC channel 2021-04-24 12:43:07 +02:00
Simon Ser 565f67f805 readme: update and cleanup xcb deps
Switch from the pkg-config dependency names to the upstream
libxcb repository names. Each repository contains multiple
libraries but is generally available in distributions as a single
package.

libxcb contains xcb, xcb-composite, xcb-xfixes, xcb-xinput and
xcb-shm. libxcb-render-util contains xcb-render. libxcb-wm
contains xcb-icccm. xcb-image was outdated and is no longer needed.
2021-04-24 12:40:10 +02:00
Aleksei Bavshin e48dcdf72c xwayland: remove _NET_WM_PID handler
We already get the PID from XRes and _NET_WM_PID code can overwrite it
with incorrect data.
2021-04-23 09:55:01 +02:00
Aleksei Bavshin e0f239fa28 xwayland: query window PIDs via XResQueryClientIds
`_NET_WM_PID` is unreliable: it is optional and even if set it may
contain PIDs from sandbox namespaces or remote systems.
Prefer XRes v1.2 QueryClientIds method which returns PIDs as seen by the
Xwayland server.
2021-04-23 09:55:01 +02:00
Simon Ser c314920a3d render: remove NULL checks in wlr_texture_impl.destroy
The NULL check already exists in wlr_texture_destroy, no need to
duplicate it in each impl
2021-04-22 15:44:49 +02:00
Simon Ser 8ca2b4cf49 render/pixman: destroy textures on renderer teardown 2021-04-22 15:44:49 +02:00
Simon Ser 661ba49564 render/gles2: destroy textures on renderer teardown 2021-04-22 15:44:49 +02:00
Simon Ser 9901d49fa5 render/pixman: cleanup when renderer is destroyed
We were leaking wlr_pixman_buffers and a wlr_drm_format_set.
2021-04-22 15:44:49 +02:00
Simon Ser 6622cd3277 output: correctly handle outputs without a test() impl
This fixes a crash when calling wlr_output_test() on a headless
output.

Closes: https://github.com/swaywm/sway/issues/6213
2021-04-21 08:52:50 +02:00
Simon Ser 8e375ae340 render/gles2: log when creating renderer
Make it clear GLES2 is being used. Before this commit, various
GL-related information was printed, but not an easy-to-find line
about which renderer is being picked up.
2021-04-21 08:30:35 +02:00
Simon Zeni cdacf4f632 render: introduce WLR_RENDERER in wlr_renderer_autocreate_with_drm_fd
This env var forces the creation of a specific renderer. If no renderer
is specified, the function will try to create all of the renderers one
by one until one is created successfuly.
2021-04-20 21:14:27 +02:00
Simon Zeni 10c5199d85 render/gles2: introduce wlr_gles2_renderer_create_with_drm_fd 2021-04-20 21:14:27 +02:00
Tadeo Kondrak 014c59aa40 backend/x11: add support for shm buffers 2021-04-20 20:27:25 +02:00
nyorain 572b5910bb render: Assert that texture dimensions are > 0 2021-04-20 18:10:48 +02:00
Simon Zeni 78b94a570c examples/rotation: remove unused includes 2021-04-20 08:46:59 +02:00
Simon Zeni 217c4f79a0 examples: introduce quads example
This examples uses `wlr_render_quad_with_matrix` to render coloured
squares on the screen, and uses the rotation to make them spin on their
middle.
2021-04-20 08:46:59 +02:00
tomKPZ 7c9b61b18c Fix stuck keys on X11 backend 2021-04-20 08:19:48 +02:00
Simon Ser e8df7c367a linux-dmabuf-v1: split params and buffer
Previously, the same struct was used for linux-dmabuf-v1 params
and buffer. This made the whole logic a little bit awkward, because
a wlr_dmabuf_v1_buffer could either be still being constructed, or
be a complete buffer.

Introduce a separate wlr_linux_buffer_params_v1 struct for buffer
params still being constructed. Once the params are complete (ie.
once the create request is sent), the params struct is destroyed
and the buffer struct is created.

This will help with [1] as well.

[1]: https://github.com/swaywm/wlroots/issues/2664
2021-04-19 18:09:13 +02:00
Simon Ser f64ed60c7b linux-dmabuf-v1: drop some from_resource helpers
Drop wlr_dmabuf_v1_buffer_from_params_resource and
wlr_linux_dmabuf_v1_from_resource. Contrary to wl_buffer, these
resources are internal linux-dmabuf-v1 implementation details and
should not be shared with other interfaces.
2021-04-19 18:09:13 +02:00
Simon Ser fbc2182b9f Stop specifying xkb_rule_names
If a NULL xkb_rule_names pointer is passed to
xkb_keymap_new_from_names, libxkbcommon will default to reading
the XKB_* env variables. So there's no need to do it ourselves.

Also s/xkb_map_new_from_names/xkb_keymap_new_from_names/ since the
latter is more consistent with the returned struct name.

[1]: https://xkbcommon.org/doc/current/structxkb__rule__names.html
2021-04-19 17:33:28 +02:00
Simon Ser 83670fce65 examples: remove dependency on GLES2 for compositor examples
Most of the examples had a GLES2 dependency, but weren't using it.
Convert multi-pointer to wlr_renderer instead of using directly
glClear.
2021-04-19 17:32:07 +02:00
Simon Ser fd7e565ce3 examples: use wlr_output_preferred_mode 2021-04-19 17:25:41 +02:00
Kenny Levinsen 3432ab2ba7 backend/session: Close remaining devices on destroy 2021-04-18 18:32:24 +02:00
Kenny Levinsen e7515529ce backend/session: Close fd in wlr_session_close_file
This was lost in the session_impl removal refactor.
2021-04-18 18:32:24 +02:00
ayaka ed1924800d render: make GLES2 renderer optional
Allow selecting whether the GLES2 renderer gets enabled.

Co-authored-by: Simon Ser <contact@emersion.fr>
2021-04-17 16:39:40 +02:00
Simon Zeni 122d6c6988 renderer: create pixman renderer if getting drm fd failed 2021-04-17 09:54:39 +02:00
Simon Zeni 0d90dddfab render: introduce pixman renderer 2021-04-17 09:54:39 +02:00
Simon Ser 9de93a866f backend/wayland: fallback to wl_shm on missing render node 2021-04-17 09:54:39 +02:00
Simon Ser 80865351bd backend/wayland: add support for wl_shm buffers 2021-04-17 09:54:39 +02:00
Simon Ser c6b009ef85 render: introduce shared memory allocator
It allocates in local main memory via shm_open, and provides a FD
to allow sharing with other processes.

This is suitable for software rendering under the Wayland and X11
backends.
2021-04-17 09:54:39 +02:00
Simon Ser 6c61de996c buffer: introduce wlr_buffer_get_shm
References: https://github.com/swaywm/wlroots/issues/2399#issuecomment-769408708
2021-04-17 09:54:39 +02:00
Simon Zeni 0b9288ec0b buffer: introduce wlr_buffer_get_data_ptr
The function has been place in an internal header for API stability
reasons.
2021-04-17 09:54:39 +02:00
Simon Ser 9f33d8ad39 readme: mark libseat as mandatory dep
[1] has made wlroots unconditionally depend on libseat. Mention this
in the README.

[1]: https://github.com/swaywm/wlroots/pull/2839
2021-04-17 09:48:26 +02:00
Ryan Farley d87ede0d69 xwayland/sockets: ensure proper permissions
Create a private UNIX socket directory (755), or use an existing one but
ensure proper permissions are set to prevent meddling from other users.
2021-04-16 11:53:05 +02:00
Simon Ser 3c6826df71 examples/rotation: error out on invalid option 2021-04-16 08:58:29 +02:00
Simon Zeni 84dea55b20 render: rename get_dmabuf_render_formats into get_render_formats 2021-04-15 17:10:06 +02:00
Simon Ser 004cf887b7 render/gles2: prevent imported DMA-BUF textures from being mutated
The compositor shouldn't write to client buffers if the client
attaches a DMA-BUF to a wl_surface, then attaches a shm buffer.
Make gles2_texture_write_pixels return an error to prevent this
from happening.
2021-04-15 10:49:42 +02:00
Simon Ser 1e5460d4c6 backend/x11: check for connection errors 2021-04-14 23:56:56 +02:00
Simon Ser 053ebe7c27 backend/drm: terminate display on drmHandleEvent failure 2021-04-14 23:56:56 +02:00
Simon Ser 846e0838d6 backend/libinput: terminate display on error 2021-04-14 23:56:56 +02:00
Kenny Levinsen 7f09085461 backend/session: Remove session_impl
libseat provides all session functionality, so there is no longer need
for a session backend abstraction. The libseat device ID, seat handle
and event loop handle are moved to the main wlr_session and wlr_device
structs.
2021-04-14 23:25:07 +02:00
Kenny Levinsen 3f87c2caea backend/session: Remove noop backend
This is instead delegated to libseat.
2021-04-14 23:25:07 +02:00
Kenny Levinsen d037c2dddc backend/session: Remove direct backend
This is instead delegated to libseat.
2021-04-14 23:25:07 +02:00
Kenny Levinsen 95b657ba80 backend/session: Make libseat mandatory 2021-04-14 23:25:07 +02:00
Kenny Levinsen 21e8a940b8 ci: Add libseat-dev to alpine 2021-04-14 23:25:07 +02:00
Yuya Nishihara 3c5cc02b18 xcursor: use memcpy() to append string of known size
Since len <= strlen(elt) is known, we don't need a str*() function. Let's
simply do memcpy() to suppress linter false positive.

Fixes #2777.
2021-04-13 16:55:46 +02:00
Yuya Nishihara a71d565138 Revert "xcursor: use strncat instead of strncpy"
This reverts commit 7dffe9339b, which introduced
another linter error with -O3:

  error: ‘strncat’ specified bound 7 equals source length [-Werror=stringop-overflow=]

This makes sense because strncat(dest, "cursors", strlen("cursors")) is moot
in security point of view.

The next commit will replace strncpy() with memcpy(), so let's restore the
original implementation.
2021-04-13 16:55:46 +02:00
Roman Gilg b36af22c94 backend: move get_drm_fd to public interface
The get_drm_fd was made available in an internal header with a53ab146f. Move it
now to the public header so consumers opting in to the unstable interfaces can
make use of it.
2021-04-12 11:43:56 +02:00
Ryan Farley b29ac8fbac util/uuid: replace with util/token, remove libuuid
Use 128-bit hexadecimal string tokens generated with /dev/urandom
instead of UUIDs for xdg-foreign handles, removing the libuuid
dependency. Update readme and CI. Closes #2830.

build: remove xdg-foreign feature

With no external dependencies required, there's no reason not to always
build it. Remove WLR_HAS_XDG_FOREIGN as well.
2021-04-11 19:09:36 +02:00
Simon Ser 5a178c4a23 build: remove wayland-scanner fallback
See the Weston discussion [1] for motivation.

[1]: https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/157
2021-04-11 17:37:54 +02:00
Benoit Gschwind ee3640363e Document the wlr_output_layout_get function 2021-04-11 10:19:00 +02:00
Kenny Levinsen d50bbf0bbc backend/session: Remove logind backend
This is instead delegated to libseat.
2021-04-11 10:03:13 +02:00
Stephan Hilb 9f012cac2f drm: check for PRIME support
PRIME support for buffer sharing has become mandatory since the renderer
rewrite. Make sure we check for the appropriate capabilities in backend,
allocator and renderer.

See also #2819.
2021-04-10 10:49:55 +02:00
Simon Ser d5105c42e3 build: disable libseat subproject server and man pages
When libseat is built as a subproject, we're not interested in
building the server or the man pages.
2021-04-09 22:28:46 +02:00
Simon Ser 1eb38e0015 Remove WLR_HAS_XCB_ERRORS
wlroots' dependency on this library doesn't change the features
exposed to compositors. It's purely a wlroots implementation detail.
Thus downstream compositors shouldn't really care about it.

Introduce an "internal_features" dictionary to store the status of
such internal dependencies.
2021-04-09 21:54:38 +02:00
Isaac Freund 78befa59f9 gtk-primary-selection: drop support
The standard primary-selection protocol is now widely supported.
2021-04-08 09:50:18 +02:00
Simon Ser a109a80dca render: drop support for ellipses
For anything more complicated than quads, compositors can easily
ship their own shaders.

Closes: https://github.com/swaywm/wlroots/issues/2759
2021-04-08 09:10:03 +02:00
Simon Ser 9ecfa4343a render: remove wlr_texture_to_dmabuf
This is unused in wlroots, and the use-cases for compositors are
pretty niche since they can access the original DMA-BUF via the
wlr_buffer.
2021-04-08 09:09:50 +02:00
Simon Ser 1cdef8da57 render: drop wlr_renderer_blit_dmabuf
It can be replaced with wlr_renderer_bind_buffer. blit_dmabuf is
broken as-is (dies on an assertion).
2021-04-08 09:09:03 +02:00
Simon Ser 1c10079a67 build: bump version to 0.14.0
We now bump the version number right after releases, so that a Git
snapshot is not mistaken for a previous version.

References: https://github.com/swaywm/wlroots/issues/2792
2021-04-08 08:53:07 +02:00
Simon Ser 69c71dbc8a build: bump to v0.13.0
References: https://github.com/swaywm/wlroots/issues/2778
2021-04-07 21:19:31 +02:00
zccrs 69e1997ebe render/egl: check "EGL_KHR_platform_gbm" for EGL gbm platform
See the
https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_platform_gbm.txt
example code. On EGL_PLATFORM_GBM_KHR platform, we should be check the
"EGL_KHR_platform_gbm" extension.

Change-Id: Icf11c07c2949841a80b10527fb09987257fbd63b
2021-04-07 15:38:35 +02:00
Simon Ser 8ccb4bbb5f backend/drm: stop testing for buffer in drm_connector_commit_buffer
We now do the test in drm_connector_test, called from
drm_connector_commit.
2021-04-06 20:35:15 +02:00
Simon Ser d0bf750916 backend/drm: use atomic test-only commits for direct scan-out
This allows callers to use wlr_output_test to figure out whether a
buffer can be scanned out prior to committing the output.
2021-04-06 20:35:15 +02:00
Simon Ser 7efc2d05b7 backend/drm: downgrade test-only commit failure log level
Let's not clutter the logs with error messages when a test-only
atomic commit fails.
2021-04-06 20:35:15 +02:00
Simon Ser 5088e25eaf backend/drm: don't set NONBLOCK with TEST_ONLY
The kernel ignores NONBLOCK when TEST_ONLY is set. Let's just not
set it, to make it clear it's unused.
2021-04-06 20:35:15 +02:00
Simon Ser 55aaeb25c5 build: use get_variable instead of get_pkgconfig_variable
This fixes the following warning:

    WARNING: Project targeting '>=0.56.0' but tried to use feature deprecated since '0.56.0': Dependency.get_pkgconfig_variable. use Dependency.get_variable(pkgconfig : ...) instead
2021-04-06 20:35:00 +02:00
Simon Ser 6721444114 build: simplify HAS_LIBUUID definition
We can just use to_int() instead of having two if branches.
2021-04-06 20:34:47 +02:00
Simon Ser e9361e0492 backend/x11: reject DMA-BUFs with flags
We cannot scan-out DMA-BUFs with any flag right now.
2021-04-06 15:03:52 +02:00
Simon Ser 6bfbf35618 backend/drm: reject DMA-BUFs with flags
We cannot scan-out DMA-BUFs with any flag right now.
2021-04-06 15:02:57 +02:00
Simon Ser 1ec97bdf4f backend/drm: improve logs in drm_fb_create
Downgrade errors to DEBUG level, because drm_fb_create is used in
test_buffer, so errors aren't always fatal. Add ERROR logs at call
sites where a failure is fatal, to make it clear something wrong
happened.
2021-04-06 14:57:59 +02:00
Simon Ser 511e42be5e backend/drm: try to import buffer when testing it
If the import to KMS succeeds, we have a better chance to be able to
scan it out.

Importing is also necessary for test-only commits, which we want to
add in the future.
2021-04-06 14:57:59 +02:00
Simon Ser b514d4afe2 render/egl: stop relying on platform for high priority
All backends use the GBM platform. We can't use it to figure out
whether the DRM backend is used anymore.

Let's just try to always request a high-priority EGL context. Failing
to do so is not fatal.
2021-04-06 14:57:54 +02:00
Simon Ser a9e5df44d8 surface: remove resource_list arg from surface_create
This is never used.
2021-04-06 14:57:44 +02:00
Simon Ser c430cd7d53 surface: make wlr_surface_create private
This is not meant to be exposed in the public API, just like
wlr_region_create [1].

[1]: https://github.com/swaywm/wlroots/pull/2662
2021-04-06 14:57:44 +02:00
zccrs e76583f1ad Use absolute paths in include in header files
To unify the code style of the project, absolute paths have been used in
some places, such as '#include "render/allocator.h"' in
"render/gbm_allocator.h". Except for include the wayland protocol
headers should be consistent.
2021-04-06 10:40:56 +02:00
Simon Ser 07a5345aa5 build: add subproject fallback for libseat
This allows libseat to be compiled as a Meson subproject when it's
not installed system-wide. This can ease development and compilation
on distributions where libseat isn't packaged.
2021-04-02 19:13:17 +02:00
Simon Ser 7709a965e5 backend/drm: use format table in test_buffer
Instead of an ad-hoc strip_alpha_channel function, use the
centralized format table to get an opaque substitute.
2021-03-31 17:29:31 +02:00
Simon Ser a2535b80ce xwayland: use ICCCM state defines from xcb-icccm 2021-03-29 12:24:26 +02:00
Simon Ser de5347d0f2 xwayland: require xcb-icccm
This dependency is already required by many other widely used X11
programs, such as i3, Qt, and other XWMs. So it should be available
on most systems.

X11 support can be pretty broken without xcb-icccm, with focus issues
for instance. Let's just remove this --please-break-my-desktop footgun
option.
2021-03-29 12:24:26 +02:00
Simon Ser 96aa18ae44 xwayland: assume no WM_HINTS means window wants input
Some X11 clients (e.g. Chromium, sxiv) don't set WM_HINTS. The spec
says:

> Window managers are free to assume convenient values for all fields of the
> WM_HINTS property if a window is mapped without one.

Our wlr_xwayland_icccm_input_model function assumes missing WM_HINTS
means the window doesn't want input, but this is incorrect. Assume the
window wants input unless it explicitly opts-out by setting WM_HINTS.

Closes: https://github.com/swaywm/sway/issues/6107
2021-03-29 12:24:26 +02:00
Simon Ser b89bcffea4 render/egl, backend/wayland: add workaround for split render/display setups
Split render/display setups have two separate devices: one display-only
with a primary node, and one render-only with a render node. However
in these cases the EGL implementation and the Wayland compositor will
advertise the display device instead of the render device [1]. The EGL
implementation will magically open the render device when the display
device is passed in.

So just pass the display device as if it were a render device. Maybe in
the future Mesa will advertise the render device instead and we'll be
able to remove this workaround.

[1]: https://gitlab.freedesktop.org/mesa/mesa/-/issues/4178
2021-03-27 11:23:32 +01:00
Kenny Levinsen 741da702bc xdg_shell: Fix invert_y of top right anchor 2021-03-26 23:20:09 +01:00
Simon Ser 80dbb9ba71 subsurface: immediately unlock cached state in set_desync
set_desync takes effect immediately without waiting for the next
wl_surface.commit request.
2021-03-25 19:34:47 +01:00
Simon Ser 8ecc557ab0 subsurface: use cached surface state 2021-03-25 19:34:47 +01:00
Simon Ser e0258f4506 surface: introduce cached states
Cached states allow a surface commit to be delayed. They are useful for:

- Subsurfaces
- The upcoming transactions protocol [1]
- Explicit synchronization

[1]: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/26
2021-03-25 19:34:47 +01:00
Simon Ser 7ac76aba8a surface: introduce commit sequence numbers
Very similar to output commit sequence numbers. Can be useful to
reference a specific commit.
2021-03-25 19:34:47 +01:00
Simon Zeni 78d21fa131 render/gles2: remove depth and bpp gles2_pixel_format, use drm pixel format 2021-03-25 10:55:54 +01:00
Simon Zeni 9b3f2e327f render/pixel_format: add XBGR8888 and ABGR8888 format 2021-03-25 10:55:54 +01:00
Simon Zeni c8b3536b01 backend/drm: use pixel format table in renderer 2021-03-25 10:55:54 +01:00
Simon Zeni 50d2985607 Move render/shm_format functions to render/pixel_format 2021-03-25 10:55:54 +01:00
Simon Zeni 5fd82c6f54 render/pixel_format: introduce pixel format info table 2021-03-25 10:55:54 +01:00
Simon Ser d9cae04ffc linux-dmabuf-v1: always advertise support for implicit modifiers
Some clients (like Xwayland) will fallback to wl_drm if the compositor
doesn't explicitly advertise support for implicit modifiers, even when
the compositor supports explicit modifiers. This behavior sounds correct
from a protocol point of view.
2021-03-23 23:32:44 +01:00
Simon Ser 6230f7be4f examples/dmabuf-capture: stop using av_init_packet
It's deprecated in ffmpeg >= 4.4.

Closes: https://github.com/swaywm/wlroots/issues/2798
2021-03-22 17:15:06 +01:00
Simon Ser e8ad05913f ci: update xwayland dep on alpine
The xwayland package has changed its name.
2021-03-22 11:54:57 +01:00
Simon Ser c740fccc9d Fix buffer blit matrices
There was a missing wlr_matrix_scale call, so we ended up with black
frames.

Closes: https://github.com/swaywm/wlroots/issues/2780
2021-03-16 17:57:51 +01:00
Simon Ser 7720dde74d screencopy: stop using wlr_renderer_blit_dmabuf
The original motivation is the following crash:

    #0  0x00007f0ddeddeef5 in raise () at /usr/lib/libc.so.6
    #1  0x00007f0ddedc8862 in abort () at /usr/lib/libc.so.6
    #2  0x00007f0ddedc8747 in _nl_load_domain.cold () at /usr/lib/libc.so.6
    #3  0x00007f0ddedd7646 in  () at /usr/lib/libc.so.6
    #4  0x00007f0de033f8de in gles2_get_renderer_in_context (wlr_renderer=0x612000003640) at ../subprojects/wlroots/render/gles2/renderer.c:38
    #5  0x00007f0de0341675 in gles2_begin (wlr_renderer=0x612000003640, width=3840, height=2160) at ../subprojects/wlroots/render/gles2/renderer.c:186
    #6  0x00007f0de033b100 in wlr_renderer_begin (r=0x612000003640, width=3840, height=2160) at ../subprojects/wlroots/render/wlr_renderer.c:56
    #7  0x00007f0de03466ed in gles2_blit_dmabuf (wlr_renderer=0x612000003640, dst_attr=0x60b000150148, src_attr=0x7fff9f9bfde0) at ../subprojects/wlroots/render/gles2/renderer.c:591
    #8  0x00007f0de033d717 in wlr_renderer_blit_dmabuf (r=0x612000003640, dst=0x60b000150148, src=0x7fff9f9bfde0) at ../subprojects/wlroots/render/wlr_renderer.c:210
    #9  0x00007f0de04cbc3b in frame_handle_output_commit (listener=0x611000220bc8, data=0x7fff9f9c0050) at ../subprojects/wlroots/types/wlr_screencopy_v1.c:303

The GLES2 renderer assumes it'll have a wlr_buffer to render to.

Instead of accomodating for the edge-case of rendering without a
wlr_buffer, drop wlr_renderer_blit_dmabuf calls and instead use
wlr_renderer_bind_buffer just like the rest of the wlr_renderer users.
wlr_renderer_blit_dmabuf is now unused in wlroots.

The upside is that the new blit_dmabuf function in screencopy is
renderer-agnostic.
2021-03-16 17:57:51 +01:00
Kenny Levinsen 00bee2a6bd docs: Minor libseat corrections 2021-03-15 23:44:09 +01:00
Kenny Levinsen 883d5b6e7c backend/session/libseat: Set loglevel to INFO 2021-03-15 21:29:22 +01:00
Simon Ser 2382684e94 render/egl: don't fail on EGL_MESA_device_software
Mesa may advertise EGL_MESA_device_software even when hardware
drivers are in use [1]. Demote the error to a warning until the Mesa
bug is fixed.

[1]: https://gitlab.freedesktop.org/mesa/mesa/-/issues/4178

References: https://github.com/swaywm/wlroots/pull/2689
2021-03-15 15:14:33 +01:00
Simon Ser 44fa2c4b49 output: fix transform matrix for 90/270 rotations
We need to adjust the second translation depending on the transform
we applied.

Fixes: 9601a2abf0 ("output: improve transform matrix calculation"
Closes: https://github.com/swaywm/wlroots/issues/2774
2021-03-11 22:52:38 +01:00
Simon Zeni 9601a2abf0 output: improve transform matrix calculation
Compute only the transform matrix in the output. The projection matrix
will be calculated inside the gles2 renderer when we start rendering.

The goal is to help the pixman rendering process.
2021-03-10 15:33:36 +01:00
Simon Ser 52e40025c4 output: document hotspot coord space 2021-03-09 22:29:32 +01:00
Ilia Mirkin 10dbb00f5f backend/x11: clamp hotspot to texture bounds
When a new texture is set, the hotspot may actually belong to the
previous texture and be out of bounds. Rather than incur X errors for
these, clamp the hotspot to be inside of the texture.

This fixes weston examples updating their cursors (e.g.
weston-eventdemo).
2021-03-09 22:27:07 +01:00
Lukas Märdian 7dffe9339b xcursor: use strncat instead of strncpy
strncat appends '\0' automatically and avoids the stringop-truncation
warning/error
2021-03-08 12:03:48 +01:00
Lukas Märdian d8a422575b Fix false positive -Wstringop-truncation
Fix false positive stringop-truncation warning/error with GCC 10 on s390x by indicating GCC to explicitly ignore this case, as it is clearly a false positive (NUL is set in the following line).

This allow the compilation to succeed with -Werror on.

Fixes: https://github.com/swaywm/wlroots/issues/2018
2021-03-08 12:03:48 +01:00
Kenny Levinsen 46d2f80c38 wlr_seat_pointer: Remove log on notify_button 2021-03-07 11:01:38 +01:00
Simon Ser a02da8e6f6 backend/drm: add new writeback and SPI connector types
We already depend on libdrm 2.4.95, which is the first to have the
writeback connector type.
2021-03-05 17:59:26 +01:00
Simon Ser e6f6e1ad0a xwayland: use -listenfd if available
Xwayland's -listen option was deprecated in [1] in favor of -listenfd.

[1]: https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/593
2021-03-03 19:04:47 +01:00
Simon Ser eec2e1d3b1 xwayland: check executable exists on init
Instead of walking PATH like a previous proposal [1], this one
checks that the Xwayland path specified in the pkg-config file
exists.

I think this is a reasonable compromise:

- Users that don't have Xwayland installed system-wide won't get
  a bogus DISPLAY env variable set up.
- Users that have WLR_XWAYLAND set won't be affected by this check.
- Users that have Xwayland installed system-wide and a different
  Xwayland in their PATH still get their custom Xwayland.
- Users that don't have Xwayland installed system-wide but have it
  somewhere else in PATH are left out. But this is pretty niche,
  and they can just set WLR_XWAYLAND.

[1]: https://github.com/swaywm/wlroots/pull/2314
2021-03-03 18:19:12 +01:00
Simon Ser 3504bb587d xwayland: add dependency on xwayland
Check that the pkg-config file is available. This will be required
in the future to check whether xwayland supports features such as
-listenfd, -initfd or -verbose.

If there's no pkg-config file, check that the Xwayland executable
is available.

This effectively makes our relationship with xwayland closer to what
a dynamic library is: checked at build-time, but can be overridden
at run-time.
2021-03-03 18:19:12 +01:00
Simon Ser 73137ace84 backend/session: fix KMS device filtering
As explained in [1], user-space should perform a drmModeGetResources
call to figure out whether a device supports KMS.

[1]: https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/127
2021-02-28 21:07:48 +01:00
Simon Ser c5dad8b626 readme: use references for links
Avoids cluttering the text with URLs.
2021-02-23 20:43:51 +01:00
Simon Ser fda46ce56f readme: use spaces for list indent 2021-02-23 20:43:17 +01:00
Simon Ser 70a7c7f389 readme: make it clear build/ is a dir
Can be confused with a sub-command for people not familiar with
Meson and ninja.
2021-02-23 20:36:26 +01:00
Simon Ser c5202b728a examples: add libdrm partial dependency for compositors
Fixes: 675bc39658 ("Fix wl_shm_format passed to wlr_texture_from_pixels")
2021-02-23 20:29:58 +01:00
Simon Ser 675bc39658 Fix wl_shm_format passed to wlr_texture_from_pixels
Fixes: 27fba3df43 ("render: use DRM formats in wlr_texture_from_pixels")
2021-02-23 17:36:32 +01:00
Simon Ser c2815fd44d buffer: add missing convert_wl_shm_format_to_drm call
Fixes: 27fba3df43 ("render: use DRM formats in wlr_texture_from_pixels")
Closes: https://github.com/swaywm/wlroots/issues/2757
2021-02-23 17:33:08 +01:00
Simon Ser 3695ae97b4 build: rollback -Wformat=2
Causes some build failures on Clang.

Fixes: 4b43aebdc7 ("build: add -Wformat=2 -Walloca")
2021-02-23 17:06:52 +01:00
Simon Ser 4b43aebdc7 build: add -Wformat=2 -Walloca 2021-02-23 16:41:26 +01:00
Simon Ser cf5b09ede2 Remove unnecessary wayland-server-protocol.h includes 2021-02-23 16:09:26 +01:00
Simon Ser 27fba3df43 render: use DRM formats in wlr_texture_from_pixels 2021-02-23 16:09:26 +01:00
Simon Ser b54ef3372d render: use DRM formats in wlr_renderer_read_pixels 2021-02-23 16:09:26 +01:00
Simon Ser 00bf6674b3 output: use DRM format in wlr_output_preferred_read_format 2021-02-23 16:09:26 +01:00
Simon Ser ddfee63055 render: use DRM formats in wlr_renderer_get_shm_texture_formats 2021-02-23 16:09:26 +01:00
Simon Ser 549435aee5 render/gles2: replace wlr_gles2_texture.wl_format with drm_format 2021-02-23 16:09:26 +01:00
Simon Ser fab396f149 render/gles2: convert format table to DRM formats 2021-02-23 16:09:26 +01:00
Simon Ser 5d6d76c61f render/shm_format: add wl_shm_format conversion helpers 2021-02-23 16:09:26 +01:00
Simon Ser bfd020047d render/gles2: remove current_buffer checks
All backends now use wlr_swapchain. This means the renderer is
guaranteed to have a current_buffer bound.

Remove the legacy code used for EGLSurface.
2021-02-19 23:36:00 +01:00
Simon Ser 6ca59519c9 render/gles2: check buffer stride when uploading texture
If the stride is too small, the driver could end up segfaulting
(e.g. radeonsi segfaults in __memmove_sse2_unaligned_erms).
2021-02-19 23:35:38 +01:00
Simon Ser f3758d1d0a backend: add error messages in attach_render impls 2021-02-18 22:14:19 +01:00
Simon Ser 641c223d3c surface: don't send protocol error on invalid buffer size
A libwayland-cursor bug [1] makes many clients crash.

[1]: https://gitlab.freedesktop.org/wayland/wayland/-/issues/194

Fixes: 91fa2ff395 ("surface: check buffer size is compatible with scale")
Closes: https://github.com/swaywm/sway/issues/6014
2021-02-17 10:47:54 +01:00
Simon Ser 2530235139 surface: move INVALID_SIZE check into surface_state_finalize
This fixes some build warnings.

Closes: https://github.com/swaywm/wlroots/issues/2740
References: https://github.com/swaywm/wlroots/pull/2736
2021-02-17 10:47:54 +01:00
Simon Ser 38ec1c0e73 build: bump meson version to 0.56.0
Fixes this warning:

    WARNING: Project targeting '>=0.54.0' but tried to use feature introduced in '0.56.0': variables as dictionary.

Fixes: 6f873078d4 ("build: use dictionnary for features instead of configuration_data")
2021-02-15 23:45:26 +01:00
Simon Ser ccb86448eb Replace leftover conf_data with features 2021-02-15 23:44:44 +01:00
Tadeo Kondrak 78685ec6aa text_input_v3: correct typo in enum field 2021-02-15 16:33:05 +01:00
Simon Ser 27f65c2c77 build: set pkg-config variables for our features
this avoids having to parse the config.h file from builds systems
of projects using wlroots.
2021-02-15 16:32:33 +01:00
Simon Ser fdd9088e05 build: set have_* dep variables for our features
This allows users to to something like this when wlroots is used as a
subproject:

    wlr_has_xwayland = wlroots.get_variable('have_xwayland')

Instead of having to parse conf_data from the subproject object.
2021-02-15 16:32:33 +01:00
Simon Ser 6f873078d4 build: use dictionnary for features instead of configuration_data
This allows us to easily iterate on all features and only deal with
bools.
2021-02-15 16:32:33 +01:00
Tudor Brindus 2118a3ce47 xwayland/selection: flush connection after changing xwm selection owner
This was the actual underlying cause of #2192; we were not getting the
XFIXES_SELECTION_NOTIFY event in time.
2021-02-15 13:50:14 +01:00
Tudor Brindus 2827a9554c xwayland/selection: log when proxy window loses ownership 2021-02-15 13:50:14 +01:00
Tudor Brindus 7d52b4d0b5 xwayland/selection: ignore requests for anything but the newest data
Our internal state machine gets screwed up if selection events are not
monotonically increasing in time, and we can enter a self-copy loop from
the proxy window that exhausts all pipes.

Snippet of logs when this occurs:

  00:00:46.238 [wlr] [xwayland/selection/incoming.c:487] XCB_XFIXES_SELECTION_NOTIFY (selection=277, owner=4194626)
  00:00:46.238 [wlr] [xwayland/selection/incoming.c:487] XCB_XFIXES_SELECTION_NOTIFY (selection=277, owner=2097153)
  00:00:46.238 [wlr] [xwayland/selection/outgoing.c:378] XCB_SELECTION_REQUEST (time=58979563 owner=2097153, requestor=2097153 selection=277, target=279, property=278)
  00:00:46.238 [wlr] [xwayland/selection/outgoing.c:397] ignoring old request from timestamp 58979563; expecting > 58979563
  00:00:46.238 [wlr] [xwayland/selection/outgoing.c:29] SendEvent destination=2097153 SelectionNotify(31) time=58979563 requestor=2097153 selection=277 target=279 property=0
  00:00:46.238 [wlr] [xwayland/selection/incoming.c:453] XCB_SELECTION_NOTIFY (selection=277, property=0, target=279)

Note that 2097153 is `selection->window`, and 4194626 is Emacs.

The race occurs if the selection owner changes back to our proxy window
between when we get `XCB_XFIXES_SELECTION_NOTIFY` for Emacs and when we
call `xcb_convert_selection` in `incoming.c:source_send` -- the
ConvertSelection request can end up hitting our proxy window, but the
timestamp will be rejected.

Fixes #2192.
2021-02-15 13:50:14 +01:00
Simon Ser 4a9e70ccde examples: remove wlroots dep from client examples
There is one exception: layer-shell still uses wlr_log. Would need to
convert to fprintf to drop the wlroots dep there.

Fixes: 34e7f69d69 ("examples: remove dependency on wlr_egl from clients")
2021-02-14 16:49:54 +01:00
Simon Ser b60c5fa450 examples: drop wlr/render/egl.h include from client examples
We use egl_common.h instead.

Fixes: 34e7f69d69 ("examples: remove dependency on wlr_egl from clients")
2021-02-14 16:49:54 +01:00
Simon Ser 12cc465144 examples: drop wlroots dep from egl_common.c
Just use fprintf instead of wlr_log.
2021-02-14 16:49:54 +01:00
Tadeo Kondrak 5e19e0053a xdg-foreign: rename finish_* functions to destroy_*
They free their argument, so the name is more clear.
2021-02-08 18:09:00 +01:00
Tadeo Kondrak 99ef23b62c xdg-foreign: Make imported object inert when exported is destroyed
Fixes a double-free experienced with Firefox and
xdg-desktop-portal-gtk.
2021-02-08 18:09:00 +01:00
Simon Ser d595a4ebe3 build: remove extra whitespace 2021-02-08 14:57:41 +01:00
Simon Ser 91fa2ff395 surface: check buffer size is compatible with scale
This relies on [1].

[1]: 8d5fadad47
2021-02-08 13:38:48 +01:00
Simon Ser 8d76d3263d seat: use WL_SEAT_ERROR_CAPABILITY
Depends on [1].

[1]: https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/79
2021-02-08 13:30:21 +01:00
Simon Ser 533a36f05a backend/x11: drop x11-xcb dependency
We don't need it anymore now that we've stopped using the EGL Xlib
platform.
2021-02-08 12:29:12 +01:00
Justus Rossmeier b9e9e0e133 tablet_v2: Fix implicit grab end detection
Store the previously focused surface in `state->focused` as well as in
`state->original` when starting an implicit grab. That way at the end
of an implicit grab, the detection whether the grab started and ended
on the same surface works as intended, even if the original surface was
never left at all.
2021-02-07 19:49:33 -05:00
Brandon Dowdy a02ac01be3 region: remove leftover description of resource_list
"resource_list" no longer exists for regions. Remove the last remaining description of what it does.
2021-02-06 00:05:36 +01:00
Simon Ser 9396d8433a
render/gles2: remove YUV blocklist
Mesa provides YUV shaders, and can import multi-planar YUV DMA-BUFs
as a single EGLImage. Remove the arbitrary limitation.

If the driver doesn't support importing YUV as a single EGLImage,
the import will fail and the result will be the same anyways.
2021-02-05 15:56:29 +01:00
Ilia Mirkin ef94e7e847 backend/x11: use native cursors
Fixes #2659
2021-02-05 11:45:54 +01:00
Ilia Mirkin 8ad078f46f xwayland: free render picture backing cursor
Otherwise it gets leaked never to be recovered.
2021-02-05 11:45:54 +01:00
Manuel Stoeckl a290d7a78d Make implementation function lists static const
This requires a change to the type of `struct wlr_tablet` and
`wlr_tablet_init` signature, both of which are part of the unstable API.
2021-02-05 10:04:20 +01:00
Manuel Stoeckl b6dea80907 xcursor: make cursor data and metadata const 2021-02-05 10:04:20 +01:00
Manuel Stoeckl 79be26ff1f xwayland/xwm: make atom_map const 2021-02-05 10:04:20 +01:00
Simon Ser b86eea0897
readme: fix typo in xcb-icccm dependency 2021-02-04 23:18:25 +01:00
Simon Ser 90cdf43b5f Fix bad indentation 2021-02-04 20:44:14 +01:00
Tudor Brindus 3d46d3f7a1 xwayland/selection: allow simultaneous Wayland-to-X11 transfers
There seems to be no reason why we can't service multiple Wayland-to-X11
transfers concurrently, so long as they are to different windows (or
possibly, same windows but different target properties?)

This commit removes the queuing logic, but retains the request
de-duplication from #2428.
2021-02-04 17:16:43 +01:00
Tudor Brindus 2fa257313a xwayland/selection: use one target window per selection
Previously, the clipboard and primary selections shared the same window.
This was racey, and could have led to pasting failures.

On xfixes selection owner change notification, the logic for requesting
the supported mimetypes of the new owner's selection looks like:

  xcb_convert_selection(
    xwm->xcb_conn,
    selection->window,
    selection->atom,
    xwm->atoms[TARGETS],
    xwm->atoms[WL_SELECTION],
    selection->timestamp
  );

This means ask the selection owner to write its TARGETS for the
`selection->atom` selection (one of PRIMARY, CLIPBOARD, DND_SELECTION)
to `selection->window`'s WL_SELECTION atom.

However, `selection->window` is shared for both PRIMARY and CLIPBOARD
selections, and WL_SELECTION is used as the target atom in both cases.
So, there's a race when both selections change at the same time.

The CLIPBOARD selection might support mimetypes {A, B, C}, and the
PRIMARY only {A, B}. If the ConvertSelection requests/responses "cross
on the wire", so to speak, wlroots can end up believing that the PRIMARY
selection also supports C.

A Wayland client may then ask for the PRIMARY selection in C format,
which will fail with "convert selection failed".

This commit fixes this by using a separate window for PRIMARY and
CLIPBOARD target requests, so that WL_SELECTION can be used as the
target atom in both cases.
2021-02-04 17:06:14 +01:00
Tudor Brindus 7964a313e8 xwayland/selection: use one X11 window per incoming transfer
This commit introduces logic for using a new X11 window for each
incoming transfer, rather than having a global window for each selection
source.

This eliminates a whole class of bugs involving multiple concurrent
incoming transfers.

For now, we retain the outgoing transfer queue, and the selection
source-specific windows to support it. Source-specific windows are no
longer used in the incoming path, and will be removed in a future PR.

Refs #1497.
2021-02-04 13:33:59 +01:00
Brandon Dowdy 0977633457 examples/egl_common: make attribs const
Make (config, context)_attribs const just to be on the safe side.
2021-02-04 09:38:27 +01:00
Simon Ser 8e27418dd3 backend/wayland: flush remote display
It turns out wl_event_source_check is not enough to guarantee that the
remote wl_display will be flushed after we queue requests. We need to
explicitly flush, just like we do in our X11 code.

References: https://gitlab.freedesktop.org/wayland/wayland/-/issues/187
2021-02-03 15:22:36 +01:00
Simon Ser a39dc1f7a8 region: remove resource_list arg from region_create
This is never used.
2021-02-02 23:42:53 +01:00
Simon Ser 45f992b27b region: make wlr_region_create private
This function should only be called from the handler for
wl_compositor.create_region requests.
2021-02-02 23:42:53 +01:00
Simon Ser 3d7aa73867 render/dmabuf: make src arg const in wlr_dmabuf_attributes_copy 2021-02-02 19:53:16 +01:00
Simon Ser 7ac2ce25e3 render/dmabuf: cleanup on wlr_dmabuf_attributes_copy error 2021-02-02 19:52:20 +01:00
Quantum 975d14b799 render/wlr_texture: clamp texture coordinates to edge by default
Clamping texture coordinates prevents OpenGL from blending the left and
right edge (or top and bottom edge) when scaling textures with GL_LINEAR
filtering. This prevents visual artifacts like swaywm/sway#5809.

Per discussion on IRC, this behaviour is made default. Compositors that want
the wrapping behaviour (e.g. for tiled patterns) can override this by doing:

    struct wlr_gles2_texture_attribs attribs;
    wlr_gles2_texture_get_attribs(texture, &attribs);

    glBindTexture(attribs.target, attribs.tex);
    glTexParameteri(attribs.target, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(attribs.target, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glBindTexture(attribs.target, 0);
2021-02-01 21:19:17 +01:00
Simon Ser 01d21cdd9f render/egl: log whether DMA-BUF modifiers are supported 2021-02-01 16:16:37 +01:00
Brandon Dowdy f1d37c54c8 render/egl: add check for EGL_KHR_surfaceless_context
As surfaces are no longer going to be used for wlr_egl, I may as well just go and add this check as it is needed for safety whenever surface-less rendering is being used.
2021-02-01 09:31:44 +01:00
Tudor Brindus dd4c8aa45e xwayland/selection: make xwm_selection_init take a wlr_xwm_selection *
This makes it consistent with xwm_selection_finish.
2021-01-31 19:17:04 +01:00
Tudor Brindus b3d782f818 xwayland/selection: introduce `xwm_selection_transfer_init`
Currently, all this does is initialize `wl_client_fd` to -1, so that
comparisons with 0 are meaningful.
2021-01-31 19:17:04 +01:00
Tudor Brindus aa86a022fa xwayland/selection: make xwm_selection_finish take a wlr_xwm_selection *
Previously it took a wlr_xwm *, which was a bit surprising in that it
freed members of wlr_xwm *, not just its respective selections.
2021-01-31 19:17:04 +01:00
Tudor Brindus b6ba595862 xwayland/selection: destroy all selections on Xwayland restart
Previously, Xwayland could restart, and we'd get events for transfers
pointing to the previous (now freed) xwm instance. This led to
use-after-free segfaults.

Closes #2565.
2021-01-31 10:24:59 +01:00
Tudor Brindus 3417fc0cca xwayland/selection: don't leak Wayland fd if ConvertSelection fails
If our ConvertSelection failed, we would previously leak the pending
Wayland client fd.

Refs swaywm/sway#5946.
2021-01-31 10:24:53 +01:00
Tudor Brindus e0dfc14983 xwayland/selection: don't request another selection while one is pending
This will hopefully be fixed in the future by having separate windows
for each X11-to-Wayland transfer, but until then, let's avoid a
compositor crash.
2021-01-31 10:24:47 +01:00
Brandon Dowdy c89dba9435 examples: remove "major" and "minor" from egl_common.c
Remove "major" and "minor" from egl_common.c as they are not used by the examples that use egl_common.c.
2021-01-30 00:31:20 +01:00
Simon Ser 7b50f5d324 backend/wayland: remove unnecessary cast 2021-01-29 12:12:47 +01:00
Tudor Brindus 211c1e23be xwayland/selection: end incr transfer on empty prop, not next selection
Previously, `transfer->incr` was being cleared on the next selection.
However, if the next selection was *also* incremental, it's possible
that `xwm_handle_selection_property_notify` would route us to
`xwm_get_incr_chunk` instead of `xwm_selection_get_data`.
2021-01-29 10:18:03 +01:00
Tudor Brindus 703c17ae41 xwayland/selection: refactor remaining incremental transfer code 2021-01-29 10:18:03 +01:00
Tudor Brindus 23148d283f xwayland/selection: extract out property requests
Apart from reducing duplication, this has the positive side-effect of
allowing all deallocs to use
`xwm_selection_transfer_destroy_property_reply`, as opposed to the
latter and a mix of ad-hoc `free`s.
2021-01-29 10:18:03 +01:00
Tudor Brindus dea94f2bad xwayland/selection: simplify incremental transfer control flow
Previously, if the Wayland client died before an incremental transfer
was complete, the logs would be spammed by "write error to target fd" as
wlroots entered some control flow wherein it'd continually try
scheduling further writes to the already-dead pipe.

This commit contains no behavioral changes, but introduces explicit
handling for draining the X11 selection in case of Wayland client death.
2021-01-29 10:18:03 +01:00
Tudor Brindus 10a2d57055 xwayland/selection: explicitly bail if first write to Wayland fd fails
If `xwm_data_source_write` failed, it's failed permanently. In fact, a
failing `xwm_data_source_write` sets `transfer->property_reply` to
null as part of its error handling.

Instead of relying on an indirect check (whether
`transfer->property_reply` is still non-null), explicitly use the return
value from `xwm_data_source_write`.
2021-01-29 10:18:03 +01:00
Tudor Brindus 40b2e7669a xwayland/selection: make `xwm_data_source_write` return 0 on failure
The `fd` is marked `O_NONBLOCK`, so `write` will never spuriously return
`EINTR`. Therefore, `write` failing is permanent, and we can return 0 to
make the return value meaningful.
2021-01-29 10:18:03 +01:00
Brandon Dowdy 8aa38fe73e render/egl: remove *config_attribs and wlr_egl->config
Breaking changes:

Both "EGLint *config_attribs" and "wlr_egl->config" no longer exist.
2021-01-29 10:03:24 +01:00
Brandon Dowdy 705b3da7cb render/egl: remove wlr_egl_{create,destroy}_surface
Breaking changes:

wlr_egl_create_surface and wlr_egl_destroy_surface have been
removed and no longer exist.
2021-01-29 10:03:24 +01:00
Brandon Dowdy 34e7f69d69 examples: remove dependency on wlr_egl from clients
The specified clients in this commit used to rely on wlr_egl and
some of its related functions in order to render surfaces.

This is no longer the case as of this commit.
2021-01-29 10:03:24 +01:00
Simon Ser 50b9921642
backend/x11: remove output_set_refresh
The X11 backend uses the Present extension to schedule frames. The
refresh rate is unused.
2021-01-28 18:47:47 +01:00
Simon Ser f8a66072e7 xwayland: fix extraneous NET_WM_STATE log messages
wlroots would log "Unhandled NET_WM_STATE property change" log
messages for atoms we know about. Simplify the code structure
and remove these extra messages.
2021-01-28 12:03:50 +01:00
Simon Ser a406f19479 render/egl: fix NULL dereference when using llvmpipe
Fixes: 6becc69ec9 ("render/egl: fail to create wlr_egl on software renderer")
2021-01-27 21:12:19 +01:00
Simon Zeni 6becc69ec9 render/egl: fail to create wlr_egl on software renderer
The creation of `wlr_egl` will fail is the device extension
EGL_MESA_device_software is defined. The creation process is allowed to
continue only if the environment variable `WLR_RENDERER_ALLOW_SOFTWARE`
is defined to the value 1.
2021-01-27 15:50:19 +01:00
Simon Ser 5a2ef794dc xwayland: drop unused enum net_wm_state_action 2021-01-27 10:47:16 +01:00
Tudor Brindus e75f483aeb xwayland/selection: rename Wayland-facing data and helpers
Previously, wlr_xwm_selection_transfer.source_fd meant:

- the source of data in a Wayland -> X11 copy (good)
- the destination of data in a X11 -> Wayland copy (confusing)

This made reading through xwayland/selection/incoming.c difficult: in
many places, "source" actually means "destination".
2021-01-25 21:02:55 +01:00
fwsmit 1b8330d1f8 examples/foreign-toplevel: Add documenation and output selection 2021-01-25 10:14:49 +01:00
Tudor Brindus 0db191d3bf xwayland/selection: prevent fd leak on unsupported MIME type
Since we never end up calling xcb_convert_selection, the file descriptor
ends up getting leaked (i.e., not cleaned up within
xwm_data_source_write).
2021-01-25 09:46:20 +01:00
Tudor Brindus abb56152ff xwayland: use wlr_log_errno instead of %m
Previously, any error would be masked by an internal isatty call:

  24:31:48.174 [DEBUG] [wlr] [xwayland/selection/incoming.c:386] XCB_SELECTION_NOTIFY (selection=277, property=278, target=256)
  24:31:48.174 [ERROR] [wlr] [xwayland/selection/incoming.c:30] write error to target fd: Inappropriate ioctl for device
2021-01-25 09:22:04 +01:00
Simon Ser 73ffab70b0 backend/drm: don't blit in drm_fb_import
Instead blit in drm_plane_lock_surface. This makes drm_fb_import simpler
and better fits its name.
2021-01-24 18:33:56 +01:00
Simon Ser 4af85f4c19 backend/drm: simplify drm_fb_lock_surface
Make it take a plane instead, and rename to drm_plane_lock_surface.
2021-01-24 18:22:03 +01:00
Simon Ser 44a3d6e74d backend/drm: document wlr_drm_plane surface fields 2021-01-24 18:21:38 +01:00
Simon Ser 4f06ce2550 render/gbm_allocator: avoid drmPrimeHandleToFD
Some drivers (e.g. nouveau) need to do some book-keeping when exporting
a BO handle to a DMA-BUF [1]. Using drmPrimeHandleToFD directly hides
the export from the user-space drivers and causes some ref'counting
issues.

So we can't properly handle BOs with a different handle for each plane
until [2] lands.

[1]: https://gitlab.freedesktop.org/drm/nouveau/-/issues/43#note_772661
[2]: https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/5442

Suggested-by: Ilia Mirkin <imirkin@alum.mit.edu>
2021-01-23 16:41:54 +01:00
Ilia Mirkin 7bc8dbb991 backend/x11: keep track of exposed rects, add them to damage regions
When we receive an Expose event, that means that we must redraw that
region of the X11 window. Keep track of these regions with pixman
regions, and merge them with the additional output damaged regions.

Fixes #2670
2021-01-21 15:14:54 +01:00
Ilia Mirkin 922b7f415d backend/x11: skip events we don't care about
These are ones I see log messages about in my setup.
2021-01-21 15:14:54 +01:00
Ilia Mirkin bb92fd4c90 backend/x11: add support for scanout mode
This makes full-screen with weston-terminal work.
2021-01-21 12:35:52 +01:00
Simon Ser 7c995b78b2 Revert "render: add wlr_egl_create_from_drm_fd function"
This reverts commit ee31be167b.
2021-01-20 21:32:50 +01:00
Simon Ser 54e5ef39c0 Revert "render/egl: remove unused gbm_device member"
This reverts commit 306cf11d87.
2021-01-20 21:32:50 +01:00
Simon Zeni 306cf11d87 render/egl: remove unused gbm_device member 2021-01-20 15:29:00 +01:00
Simon Zeni ee31be167b render: add wlr_egl_create_from_drm_fd function 2021-01-20 15:29:00 +01:00
Ilia Mirkin 62f37ee319 backend/x11: make sure output transform matrix is initialized
The transform matrix was all 0's, which meant that effectively nothing
got rendered other than the clear color.
2021-01-20 14:55:27 +01:00
BrassyPanache d6649a8a4b Expose ICCCM input status
In certain situations windows can have their input field set to false
but still expect to receive input focus by passively listening to key
presses via a parent window. The ICCCM specification outlines how focus
should be given to clients.

Further reading: https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7

Relates to #2604
2021-01-20 10:38:58 +01:00
Isaac Freund f6fe439718 xdg-output: destroy outputs before manager
Since output_destroy() calls wl_list_remove() on the output's link,
the manager must still be valid. This is the same bug fixed in bf926e3
but with a different interface.
2021-01-19 11:56:31 +01:00
Simon Ser 702eed5cbd backend/x11: fix region not being actually used
The region variable was shadowed in an if block. As a result, in the
outer block region was always XCB_NONE and was never destroyed (causing
a memory leak on the server).

Reported-by: Ilia Mirkin <imirkin@alum.mit.edu>
2021-01-18 22:31:09 +01:00
Ilia Mirkin 966e653935 backend/x11: set a blank cursor rather than hiding globally
This actually simplifies the logic since we no longer have to wait for
enter/leave events, and also improves the UX when e.g. handling a crash
with gdb attached.

See #2659
2021-01-18 21:07:11 +01:00
Simon Ser 04d89a8bc5
backend/drm: fix modifiers for cursor plane buffers
In 93cd3a79b2 ("backend/drm: stop using GBM flags"), we stopped
using the GBM_BO_USE_LINEAR flag in favor of a modifier list set
to { DRM_FORMAT_MOD_LINEAR }. However, the last argument of
drm_plane_init_surface disables modifiers -- so the buffer will just
get allocated with an implicit modifier, without necessarily being
LINEAR.

To fix this, allow modifiers when allocating the cursor buffers.
wlr_drm_plane.formats should already have the necessary LINEAR
restrictions.

Fixes: 93cd3a79b2 ("backend/drm: stop using GBM flags")
2021-01-17 22:25:18 +01:00
Simon Ser f17b0f975d backend/drm: add wlr_drm_connector_get_id
This allows a compositor to get a KMS connector object ID from a
wlr_output. The compositor can then query more information about
the connector via libdrm.

This gives more freedom to compositors and allows them to read
KMS properties that wlroots doesn't know about. For instance,
they could read the EDID or the suggested_{X,Y} properties and
change their output configuration based on that.
2021-01-17 12:42:32 +01:00
Simon Ser cb6f584496 backend/drm: add support for the subconnector property
The subconnector property indicates the connector sub-type. This is
useful because that usually indicates what kind of connector the user
has plugged in to their monitor, e.g. a DisplayPort-to-DVI cable will
indicate a DVI subconnector. Also some laptops have non-DP connectors
that are internally linked to a DP port on the GPU.

Set the output description accordingly.

See https://drmdb.emersion.fr/properties/3233857728/subconnector
2021-01-17 12:42:25 +01:00
Chris Chamberlain 6af748171a Free xwayland cursor in wlr_xwayland_destroy
One of many memory leaks detected by an asan build
2021-01-17 12:28:55 +01:00
Simon Ser 879cadd34e backend/x11: add xcb_dri3_pixmap_from_buffers check for n_planes
Just in case.
2021-01-17 12:08:40 +01:00
Simon Ser e537382991 backend/x11: add support for DRI3 1.0
Add fallbacks when DRI3 1.2 isn't supported.

Closes: https://github.com/swaywm/wlroots/issues/2586
2021-01-17 12:08:40 +01:00
Simon Ser 284233c34f backend/x11: log DRM node name 2021-01-17 10:28:12 +01:00
Simon Ser 5373187186 backend/x11: log when creating X11 backend 2021-01-17 10:28:12 +01:00
Simon Ser bf86110fc5 render/gbm_allocator: set modifier to INVALID if implicit
When gbm_bo_create is used and the GBM implementation does support
modifiers, gbm_bo_get_modifier may return something other than
DRM_FORMAT_MOD_INVALID. This can cause issues with the rest of the
stack (e.g. EGL or KMS) in case these don't support modifiers.

Instead, force the modifier to INVALID, to make sure no one uses
modifiers.
2021-01-17 10:27:46 +01:00
Simon Ser b5cefada92 render/gbm_allocator: export to DMA-BUF on init
Instead of lazily exporting BOs to DMA-BUFs, do it on init. This is
the only way to use the buffer, so there's no point in deferring
that work.

This is preparatory work for the next commit.
2021-01-17 10:27:46 +01:00
Simon Ser c6c7fccd96
backend/headless: create renderer after wlr_backend_init
We were calling wlr_renderer_autocreate before wlr_backend_init,
which caused a NULL dereference on wlr_backend.impl.
2021-01-17 01:08:40 +01:00
Simon Ser 9e98f497af backend/drm: use local DRM FD for wlr_rend
The new wlr_renderer_autocreate API is great for compositors, however
it causes some issues with DRM multi-GPU support.

A DRM child backend wants the compositor to use the parent GPU, so it
exposes the parent's DRM FD in get_drm_fd. However, in order to be able
to perform multi-GPU buffer copies, the child DRM backend still needs to
create a local renderer.

Use the new private wlr_renderer_autocreate_with_drm_fd function to
avoid creating a renderer for the parent GPU.

Fixes: e128e6c08d ("render: drop egl parameters from wlr_renderer_autocreate")
2021-01-16 22:52:26 +01:00
Simon Ser 2f11914613 render: introduce private wlr_renderer_autocreate_with_drm_fd 2021-01-16 22:52:26 +01:00
Simon Ser 32c30481d3
render/egl: fix gbm_device use-after-free
The GBM device needs to be destroyed after the EGL display.

    ==50931==ERROR: AddressSanitizer: SEGV on unknown address 0x7fe40a000049 (pc 0x7fe446121d30 bp 0x60400001bbd0 sp 0x7ffc99c774d0 T0)
    ==50931==The signal is caused by a READ memory access.
        #0 0x7fe446121d30  (/usr/lib/dri/radeonsi_dri.so+0x5f0d30)
        #1 0x7fe4474717bd  (/usr/lib/../lib/libEGL_mesa.so.0+0x177bd)
        #2 0x7fe4474677d9  (/usr/lib/../lib/libEGL_mesa.so.0+0xd7d9)
        #3 0x7fe44cca7b6f in wlr_egl_destroy ../subprojects/wlroots/render/egl.c:379
        #4 0x7fe44ccc2626 in gles2_destroy ../subprojects/wlroots/render/gles2/renderer.c:705
        #5 0x7fe44ccb5041 in wlr_renderer_destroy ../subprojects/wlroots/render/wlr_renderer.c:37
        #6 0x7fe44cd17850 in backend_destroy ../subprojects/wlroots/backend/wayland/backend.c:296
        #7 0x7fe44ccca4de in wlr_backend_destroy ../subprojects/wlroots/backend/backend.c:48
        #8 0x7fe44cd11b21 in multi_backend_destroy ../subprojects/wlroots/backend/multi/backend.c:58
        #9 0x7fe44cd125b0 in handle_display_destroy ../subprojects/wlroots/backend/multi/backend.c:125
        #10 0x7fe44c315e0e  (/usr/lib/libwayland-server.so.0+0x8e0e)
        #11 0x7fe44c3165a6 in wl_display_destroy (/usr/lib/libwayland-server.so.0+0x95a6)
        #12 0x55a2c8870683 in server_fini ../sway/server.c:203
        #13 0x55a2c886cbf2 in main ../sway/main.c:436
        #14 0x7fe44b77c151 in __libc_start_main (/usr/lib/libc.so.6+0x28151)
        #15 0x55a2c883172d in _start (/home/simon/src/sway/build/sway/sway+0x33472d)
2021-01-16 09:07:56 +01:00
Simon Ser b3e76d6678 output: send commit event after pending state is cleared
References: https://github.com/swaywm/wlroots/issues/2098
2021-01-16 09:06:27 +01:00
Simon Ser 1fb9535e99 output-damage: stop relying on output pending state on commit
References: https://github.com/swaywm/wlroots/issues/2098
2021-01-16 09:06:27 +01:00
Simon Ser afdf4dc890 render/gles2: make EGL context current in bind_buffer
Instead of requiring callers to manually make the EGL context current
before binding a buffer and unsetting it after unbinding a buffer, do
it inside wlr_renderer_bind_buffer.

This hides renderer-specific implementation details inside the
wlr_renderer interface. Non-GLES2 renderers may not use EGL.
This removes all EGL dependencies from the backends.

References: https://github.com/swaywm/wlroots/issues/2618
References: https://github.com/swaywm/wlroots/pull/2615#issuecomment-756687006
2021-01-16 09:06:17 +01:00
Simon Zeni 08a4c62aac render: remove egl include from wlr_renderer 2021-01-16 08:57:42 +01:00
Simon Zeni e128e6c08d render: drop egl parameters from wlr_renderer_autocreate 2021-01-16 08:57:42 +01:00
Simon Ser affc59454e backend/headless: implement get_drm_fd 2021-01-16 08:57:42 +01:00
Simon Ser 6dfc8ce00b backend/x11: implement get_drm_fd 2021-01-16 08:57:42 +01:00
Simon Ser b7c95d483a backend/drm: implement get_drm_fd 2021-01-16 08:57:42 +01:00
Simon Ser 400f4e7f27 backend/wayland: implement get_drm_fd 2021-01-16 08:57:42 +01:00
Simon Ser ad3a455db9 backend/multi: implement get_drm_fd
Just like get_renderer, iterate over all sub-backends until we find one
that implements get_drm_fd.
2021-01-16 08:57:42 +01:00
Simon Ser a53ab146fe backend: add get_drm_fd to interface
This function allows backends to return the DRM FD they are using. This
will allow the allocator and the renderer to use the right device.
2021-01-16 08:57:42 +01:00
Simon Ser 87293d1b15
render: extract gles2 build files to subdir
This will make it easier to toggle when we make our GLES2 renderer
optional.
2021-01-15 22:13:04 +01:00
Simon Ser c73a8cde83 render/gbm_allocator: fix gbm_device use-after-free
We need to destroy any gbm_bo we've created before gbm_device_destroy.

Closes: https://github.com/swaywm/wlroots/issues/2601
2021-01-15 19:27:49 +01:00
Simon Ser 9dd059376c
render/gbm_allocator: document that DRM primary FDs work 2021-01-15 11:25:30 +01:00
Simon Ser f0303978e3
render/gbm_allocator: log GBM backend name 2021-01-15 11:18:17 +01:00
Simon Ser 642b349e94 render/gles2: restore EGL context after texture operations
It can be surprising and unexpected that texture operations (such as
texture upload and import) change the current EGL context, especially
when it's done under-the-hood by wlroots in response to wl_surface
requests.

To prevent surprises, save and restore the previous EGL context.
2021-01-15 10:19:31 +01:00
Simon Ser cc56b4f073 output: remove scale/transform events
Instead, the commit event should be used.
2021-01-15 10:18:56 +01:00
Simon Ser aab43b3c76 output-layout: stop listening to scale/transform events
Instead, listen to the commit event only.
2021-01-15 10:18:56 +01:00
Simon Ser f6f46b4ee2 output-damage: stop listening to scale/transform events
Only listen to the commit event.
2021-01-15 10:18:56 +01:00
Simon Ser a6a0568316
backend: remove unnecessary GLES2 includes 2021-01-14 12:36:49 +01:00
Simon Ser 5642b880c3
render: document wlr_renderer_bind_buffer 2021-01-14 12:16:51 +01:00
Simon Ser 02a086599c
render/gles2: save/restore EGL context in destroy_buffer
It can be surprising that destroying a buffer changes the EGL context,
especially since this can be triggered from anywhere wlr_buffer_unlock
is called.

Prevent this from happening by saving and restoring the EGL context.
2021-01-14 12:00:06 +01:00
Simon Ser dc61f471da
backend/drm: remove unnecessary wlr_drm_fb.wlr_buf check
We don't need this check, wlr_buf is guaranteed not to be NULL.
2021-01-13 19:01:02 +01:00
Daniel Kondor b7dc4f2990 layer-shell: allow new values for keyboard-interactivity
Value is now an enum with a new value ("on-demand") that compositors can use to allow "normal" keyboard focus semantics regardless of the layer the client surface is on. An error is sent for invalid keyboard interactivity values. The old behavior is retained for clients using the previous version of the protocol.

Also adjusted the layer-shell example program to use the new keyboard interactivity options.
2021-01-12 20:13:52 +01:00
Simon Ser 5d054258af
backend/drm: fix segfault in page_flip_handler
Since 5b1b43c68c ("backend/drm: make wlr_drm_plane.{pending,queued,current}_fb
pointers"), current_fb can be NULL if there's no buffer. If current_fb
is not NULL, current_fb->wlr_buf is guaranteed to not be NULL.

Closes: https://github.com/swaywm/wlroots/issues/2634
2021-01-12 11:41:45 +01:00
Simon Ser 3f7e0cf5f0 render/egl: remove surface and buffer age args from make_current
These aren't used anymore.
2021-01-12 11:31:04 +01:00
Simon Ser 1d461687d2 render/egl: replace init/finish with create/destroy
This ensures wlr_gles2_renderer can properly take ownership of the
wlr_egl.

Closes: https://github.com/swaywm/wlroots/issues/2612
2021-01-12 11:31:04 +01:00
Simon Ser 50b120927d render/egl: remove wlr_egl_swap_buffers 2021-01-12 11:31:04 +01:00
Simon Ser 76ed2255ef render/egl: remove support for EGL_NATIVE_VISUAL_ID
Nobody uses it anymore.
2021-01-12 11:31:04 +01:00
Isaac Freund 07111828c5 layer shell: rename popup iterator for consistency
This iterates over the subsurfaces of popups as well, so rename it to
match wlr_xdg_surface_for_each_popup_surface().
2021-01-12 11:26:08 +01:00
Isaac Freund f574ca934c xdg shell: remove wlr_xdg_surface_for_each_popup()
This function is inferior to wlr_xdg_surface_for_each_popup_surface()
for rendering as it does not iterate over subsurfaces. Furthermore,
no compositor is known to use this to iterate popups for any purpose
other than rendering. Therefore remove the function, which may of course
be reintroduced at a later date if a use-case is found.
2021-01-12 11:25:55 +01:00
Simon Ser 672e8e99b7
build: use more consistent feature names in summary
Use the same name as the Meson option.
2021-01-12 10:49:33 +01:00
Simon Ser 2585f322cb
render/gles2: fix EGL use-after-free
The wlr_egl was cleaned up too early.

While at it, also fix a memory leak.

Fixes: b899a412e3 ("backend: remove wlr_egl from all backends")
2021-01-12 10:45:14 +01:00
Simon Ser 248b8e647a
build: remove Clang workarounds
We have these disabled for all compilers anyways.
2021-01-11 16:13:53 +01:00
Simon Ser 4f80fab337 gamma-control-v1: schedule frame when resetting gamma LUT
Closes: https://github.com/swaywm/wlroots/issues/2632
2021-01-10 18:40:08 +01:00
Simon Ser edf5082a4c
output-damage: use output commit event
Instead of relying on output.pending.committed, use
wlr_output_event_commit to find out whether a buffer was committed.
Eventually output.pending will be cleared before the commit event is
emitted.

References: https://github.com/swaywm/wlroots/issues/2098
2021-01-10 17:02:25 +01:00
Isaac Freund d6890cb847 output-management: use same types as wlr_output
This is more correct and also makes things much nicer for languages
that don't allow implicit conversions between these types.
2021-01-10 16:28:19 +01:00
Simon Ser 098094c5cb backend/wayland: use the EGL GBM platform
Just like other backends, use the EGL GBM platform. This will make it
easier to move the allocator and renderer initialization out of the
backends.
2021-01-10 11:32:00 +01:00
Simon Ser 8f065810f6 protocol: add drm.xml
This is Mesa's legacy wl_drm protocol. It will eventually get replaced
with linux-dmabuf, however right now it's the only way to get the DRM
device used by the parent compositor.
2021-01-10 11:32:00 +01:00
Simon Ser 7036dceb0e xwayland: remove protocol debug messages
Developers can use x11trace or similar to analyze the protocol messages.
2021-01-10 11:29:36 +01:00
Simon Ser c94728b53a
backend/drm: stop using surface size for BO
Stop using wlr_drm_surface.{width,height} to figure out the size of a
gbm_bo. In the future we'll stop using wlr_drm_plane.surf, so these will
be zero. Instead, rely on gbm_bo_get_{width,height}.
2021-01-10 11:24:44 +01:00
Simon Ser d9bbc416a6 backend/drm: re-use FBs
Instead of importing buffers to GBM and KMS at each frame, cache them
and re-use them while the wlr_buffer is alive.

This is the same as [1] and [2] but for the DRM backend.

[1]: https://github.com/swaywm/wlroots/pull/2538
[2]: https://github.com/swaywm/wlroots/pull/2539
2021-01-10 11:24:35 +01:00
Simon Ser 91cb0fc443 backend/drm: only keep track of local buffer
Stop keeping track of buffers on the parent GPU when multi-GPU is used.

This removes support for export_dmabuf on secondary GPUs, but renderer
v6 will bring this back by managing the swapchains in wlr_output instead
of the backends.
2021-01-10 11:24:35 +01:00
Simon Ser 5bd86b94f9 backend/drm: add wlr_drm_buf.local_wlr_buf 2021-01-10 11:24:35 +01:00
Simon Ser cd64610c66 backend/drm: introduce drm_fb_create 2021-01-10 11:24:35 +01:00
Simon Ser 5b1b43c68c backend/drm: make wlr_drm_plane.{pending,queued,current}_fb pointers
This will be useful once we start re-using wlr_drm_fb.
2021-01-10 11:24:35 +01:00
Simon Ser 64da8f0c8d
render/egl: document NULL config_attribs passed to wlr_egl_init 2021-01-09 12:02:39 +01:00
Simon Ser 41aa80d4a1
render/egl: use KHR function pointer type
KHR is more "core" than EXT.
2021-01-09 11:20:20 +01:00
Simon Ser 34b14d2fee
render/egl: document that config may be zero 2021-01-09 11:19:25 +01:00
Rouven Czerwinski dd920f602e util: fix uuid support for freebsd
Fixes:
  FAILED: subprojects/wlroots/libwlroots.so.7.p/util_uuid.c.o
  cc -Isubprojects/wlroots/libwlroots.so.7.p -Isubprojects/wlroots -I../subprojects/wlroots -Isubprojects/wlroots/include -I../subprojects/wlroots/include -Isubprojects/wlroots/protocol -I../subprojects/wlroots/protocol -I/usr/local/include -I/usr/local/include/libepoll-shim -I/usr/local/include/libdrm -I/usr/local/include/pixman-1 -Xclang -fcolor-diagnostics -pipe -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wextra -Werror -std=c11 -g -DWLR_USE_UNSTABLE -Wundef -Wmissing-include-dirs -Wold-style-definition -Wpointer-arith -Winit-self -Wstrict-prototypes -Wendif-labels -Wstrict-aliasing=2 -Woverflow -Wmissing-prototypes -Wno-missing-braces -Wno-missing-field-initializers -Wno-unused-parameter '-DWLR_REL_SRC_DIR="../subprojects/wlroots/"' -Wno-missing-field-initializers -Wno-missing-braces -DHAS_LIBUUID=0 '-DICONDIR="/usr/local/share/icons"' -fPIC -pthread -D_THREAD_SAFE -MD -MQ subprojects/wlroots/libwlroots.so.7.p/util_uuid.c.o -MF subprojects/wlroots/libwlroots.so.7.p/util_uuid.c.o.d -o subprojects/wlroots/libwlroots.so.7.p/util_uuid.c.o -c ../subprojects/wlroots/util/uuid.c
  ../subprojects/wlroots/util/uuid.c:28:2: error: implicit declaration of function 'assert' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
          assert(strlen(str) + 1 == 37);
          ^
  ../subprojects/wlroots/util/uuid.c:28:2: error: this function declaration is not a prototype [-Werror,-Wstrict-prototypes]
  ../subprojects/wlroots/util/uuid.c:29:25: error: sizeof on array function parameter will return size of 'char *' instead of 'char [static 37]' [-Werror,-Wsizeof-array-argument]
          memcpy(out, str, sizeof(out));
                                 ^
  ../subprojects/wlroots/util/uuid.c:15:25: note: declared here
  bool generate_uuid(char out[static 37]) {
                          ^
  ../subprojects/wlroots/util/uuid.c:29:26: error: 'memcpy' call operates on objects of type 'char' while the size is based on a different type 'char *' [-Werror,-Wsizeof-pointer-memaccess]
          memcpy(out, str, sizeof(out));
                 ~~~              ^~~
  ../subprojects/wlroots/util/uuid.c:29:26: note: did you mean to provide an explicit length?
          memcpy(out, str, sizeof(out));

Fixes #2616
2021-01-08 20:51:51 +01:00
Isaac Freund b482c90e1a xdg/layer shell: reduce code duplication in iterators 2021-01-08 14:53:45 +01:00
Isaac Freund 4ee4a36c0c layer shell: add wlr_layer_surface_v1_popup_surface_at()
This function will allow compositors to implement input handling in a
way consistent with rendering more easily.

Calling wlr_layer_surface_v1_surface_at() and checking if the result is
a wlr_xdg_popup is flawed as there may be subsurfaces in the popup tree.
2021-01-08 12:05:13 +01:00
Isaac Freund 8f63557ed7 xdg shell: add wlr_xdg_surface_popup_surface_at()
This function will allow compositors to implement input handling in a
way consistent with rendering more easily.

Calling wlr_xdg_surface_surface_at() and checking if the result is a
wlr_xdg_popup is flawed as there may be subsurfaces in the popup tree.
2021-01-08 12:05:13 +01:00
Isaac Freund c5c5ab9724 xdg shell: add wlr_xdg_surface_for_each_popup_surface()
When rendering, it is necessary to iterate the subsurfaces as well,
so add a function that makes this easy.
2021-01-08 10:33:16 +01:00
Isaac Freund 129e02b57d xdg shell: make unconstrain_from_box arg const 2021-01-07 21:28:55 +01:00
Simon Zeni 9192c0480a remove unnecessary egl includes 2021-01-07 17:11:22 +01:00
Simon Zeni b899a412e3 backend: remove wlr_egl from all backends 2021-01-07 17:11:22 +01:00
Simon Ser 5773794baf
backend/drm: don't log errno on plane_get_next_fb failure
errno isn't guaranteed to be set after a plane_get_next_fb failure, so
we were printing garbage.
2021-01-07 16:53:20 +01:00
Simon Zeni 1458f7d974 tinywl: fix wlr_backend_autocreate call 2021-01-07 14:39:04 +01:00
Simon Zeni 826108373c ci: build tinywl 2021-01-07 14:39:04 +01:00
Andri Yngvason e136a4168b types/seat: Clear focus in wlr_seat_destroy()
This fixes use-after-free in surface destroy signal listeners.
2021-01-07 14:35:08 +01:00
Isaac Freund 87e216b740 layer shell: advertise version 3
This should have been done in 45c0877 but was overlooked unfortunately.
2021-01-07 12:12:08 +01:00
Simon Ser 07d75c99db render: remove EGL config and visual from wlr_renderer_autocreate
This isn't used anymore by any backend.

Some examples still provide an EGL config to wlr_egl_init, so we can't
drop it yet there.
2021-01-06 12:05:51 +01:00
Ilia Bozhinov 01dcfb360e types: add wlr_xdg_foreign_v2
Co-authored-by: Jason Francis <cycl0ps@tuta.io>
2021-01-05 20:32:56 +01:00
Ilia Bozhinov 162f160def types: add wlr_xdg_foreign_v1
Co-authored-by: Jason Francis <cycl0ps@tuta.io>
2021-01-05 20:32:56 +01:00
Ilia Bozhinov 37602e153b types: add wlr_xdg_foreign_registry 2021-01-05 20:32:56 +01:00
Ilia Bozhinov 42d033e738 xdg-shell: add wlr_xdg_toplevel_set_parent
Co-authored-by: Jason Francis <cycl0ps@tuta.io>
2021-01-05 20:32:56 +01:00
Ilia Bozhinov bf4e2e0eac util: add support for generating UUIDs
Co-authored-by: Jason Francis <cycl0ps@tuta.io>
2021-01-05 20:32:56 +01:00
Simon Ser 3721dbfddb
backend/wayland: remove unnecessary flags from event source
wl_event_loop_add_fd only accepts READABLE and WRITABLE. ERROR and
HANGUP are always implicitly enabled.
2021-01-05 19:54:20 +01:00
Isaac Freund 0cba1ce747 gtk primary selection: destroy devices before manager
Since device_destroy() calls wl_list_remove() on the device's link,
the manager must still be valid. However if the manager is destroyed
before the seat as the wl_display destroy handlers are called, devices
listening for the seat to be destroyed will access free'd memory in
wl_list_remove().
2021-01-05 13:53:58 +01:00
Isaac Freund bf926e31a0 primary selection: destroy devices before manager
Since device_destroy() calls wl_list_remove() on the device's link,
the manager must still be valid. However if the manager is destroyed
before the seat as the wl_display destroy handlers are called, devices
listening for the seat to be destroyed will access free'd memory in
wl_list_remove().
2021-01-05 13:53:58 +01:00
Kenny Levinsen d3047011d0 backend/wayland: Avoid uninitialized read
keyboard_handle_leave would always process 1 keycode more than was
pending, which meant reading uninitialized memory from the "pressed"
array.

Found by valgrind.
2021-01-05 12:26:00 +01:00
Isaac Freund 83fdfa511d docs: wlr_surface_from_resource cannot return NULL
Surfaces never become inert.
2021-01-05 12:22:47 +01:00
Isaac Freund 5d24f6e098 toplevel-management: avoid redundant state events
Check if there would be a state change on setting maximized, minimized,
activated, or fullscreen before sending a state event.
2021-01-05 12:22:20 +01:00
Isaac Freund 8b90d5e17f toplevel-management: handle strdup failure
Sending a NULL string to a client would be a violation of the protocol.
2021-01-05 12:22:20 +01:00
Isaac Freund abcab0331f toplevel-management: ignore inert seats 2021-01-05 12:22:20 +01:00
Simon Ser e8d56ca415 backend/session: allow wlr_session_find_gpus to return an error
Sometimes wlr_session_find_gpus will encounter an error. This is
different from finding zero GPUs.

On error, wlr_session_find_gpus already returns -1. However, this is
casted to size_t, so callers uncorrectly assume this is a success.

Instead, make wlr_session_find_gpus return a ssize_t and allow callers
to handle the error accordingly.
2021-01-04 19:46:44 +01:00
Simon Ser 7febdc7334 backend/wayland: re-use cursor wl_buffers
We were importing cursor buffers as wl_buffers over and over again.
Instead, only import these once.
2021-01-04 13:48:28 +01:00
Simon Ser 198560fc1f
examples: request an EGL config
Client examples using wlr_egl would fail with EGL_BAD_CONFIG because they
need an EGL config. Set the config attribs to a non-NULL value to make
sure wlr_egl creates an EGL config.

Fixes: 037710b1d4 ("render/egl: support config-less wlr_egl")
2021-01-04 11:30:30 +01:00
Simon Ser 9714638f3b
examples: remove visual for EGL_PLATFORM_WAYLAND_EXT
The Wayland EGL platform doesn't have visuals.
2021-01-04 11:26:08 +01:00
Simon Ser adfb7cd35a backend/drm: stash pending page-flip CRTC
wlr_drm_connector.crtc may be updated by the DRM backend while a
page-flip is pending. In this case, the page-flip handler won't be able
to find the right wlr_drm_connector from the CRTC ID.

Save the CRTC when performing a page-flip to ensure we always find the
right connector when we get the event.
2021-01-03 19:35:02 +01:00
Simon Ser 576ff57db0 backend/drm: ignore hotplug events while inactive
When the session is inactive, we can't change the KMS state. Ignore
hotplug events so that compositors don't try to perform a modeset when
a connector is plugged in. We already re-scan connectors when the
session becomes active.

To test, run a wlroots compositor on VT 1, switch to VT 2, unplug a
connector, re-plug it, switch back to VT 1. Without this patch the
screen is black on VT 1.

References: https://github.com/swaywm/wlroots/issues/2370
2021-01-03 19:34:54 +01:00
Ilia Bozhinov eb30cde777 noop: listen to display destroy and destroy the backend 2020-12-31 20:34:36 +01:00
Simon Ser 4ffd537d2d
backend/x11: print version on extension error 2020-12-31 20:17:31 +01:00
Simon Ser 1491ec42da backend/x11: always open render node
If we get an authenticated primary node from the X11 server, don't use
it because we can't authenticate our Wayland clients with it. Instead,
open a render node.

Closes: https://github.com/swaywm/wlroots/issues/2576
2020-12-30 19:37:19 +01:00
Simon Ser 4b03bdc3ab Remove wlr_create_renderer_func_t
This callback allowed compositors to customize the EGL config used by
the renderer. However with renderer v6 EGL configs aren't used anymore.
Instead, buffers are allocated via GBM and GL FBOs are rendered to. So
customizing the EGL config is a no-op.
2020-12-30 17:09:40 +01:00
Simon Ser bec1e6b149 backend/drm: remove special linear case for cursor plane
We now properly mark the cursor plane's formats as linear-only, and we
now have a version of wlr_drm_format_intersect that handles the case of
linear-only formats and implicit modifiers.

We can remove the special drm_plane_init_surface flag we had for cursor
planes. This also allows us to use a non-linear layout for cursor planes
on drivers that support it.

Tested on amdgpu GFX9.
2020-12-30 11:17:43 +01:00
Simon Ser 92a0fc0435 backend/drm: extract linear format creation into function
Simplifies error handling.
2020-12-30 11:17:43 +01:00
Simon Ser c4635c68d2 render/drm_format_set: add special case for LINEAR-only formats
Our wlr_format_set structs don't hold GBM usage flags. Instead, users
who want to get a LINEAR buffer can use the DRM_FORMAT_MOD_LINEAR
modifier even if the kernel driver doesn't support modifiers.

Add a special case to wlr_drm_format_intersect to properly handle this
situation.
2020-12-30 11:17:43 +01:00
Simon Ser 7ea0e9f277 backend/drm: force LINEAR for cursor plane formats
If the kernel driver doesn't support modifiers, it still expects cursor
FBs to have a LINEAR layout. See [1] for expectations for framebuffers
attached to the cursor plane.

[1]: https://patchwork.freedesktop.org/patch/408512/
2020-12-30 11:17:43 +01:00
Simon Ser caeed70f28 backend/x11: create per-window present event contexts
The Present protocol states:

> An event context is associated with a specific window; using an existing
> event context with a different window generates a Match error.

Instead of a global event context, use a per-window event context to fix
this error:

    [backend/x11/backend.c:608] X11 error: op Present (SelectInput), code Match (no extension), sequence 63, value 4194307

Closes: https://github.com/swaywm/wlroots/issues/2577
2020-12-28 16:57:56 +01:00
Ariadne Conill 23b6f3e3f5 drm: add fallback drmModeAddFB for drivers which do not support drmModeAddFB2
This makes wlroots able to run on some big-endian machines like G4 and
G5 systems with ATI Radeon 7500 AGP graphics.
2020-12-28 14:15:04 +01:00
Simon Ser c012d770f7 backend/x11: implement a real rendering loop
Instead of using a timer, rely on X11 Present events and send a new
frame event when the parent compositor displays a new frame on screen.

The previous attempt at doing this [1] hit issues with EGLSurface, but
we don't use that anymore.

[1]: https://github.com/swaywm/wlroots/pull/1894
2020-12-27 10:10:07 +01:00
Simon Ser c5f239f411
backend/drm: make listener names more idiomatic
Use the "<object>_<event>" notation for listeners, use
"handle_<listener>" for handlers.
2020-12-25 14:45:00 +01:00
Simon Ser b9460ab724
Stop using wlr_texture_get_size
Just use wlr_texture.{width,height} directly.
2020-12-25 12:21:29 +01:00
Simon Ser ae5275c09f
client-buffer: remove unnecessary wlr_resource_get_buffer_size
We can just get the size from the imported texture.
2020-12-25 12:14:31 +01:00
Simon Ser 17dd4c9e9a
backend/drm: use connector log helpers in atomic backend 2020-12-24 17:55:45 +01:00
Simon Ser a7a230ebef
backend/drm: make drmModePlane arg in add_plane const
Make it clearer it'll be free'd after add_plane returns.
2020-12-24 12:34:13 +01:00
Simon Ser c011a0e2ed
backend/drm: add special case for cursor in add_plane
The code calling add_plane now makes sure not to add a cursor plane to a
CRTC which already has one.
2020-12-24 12:31:20 +01:00
Simon Ser 5548406667
backend/drm: log whether ADDFB2 modifiers are supported 2020-12-24 12:29:30 +01:00
Simon Ser 71eaab9d8c
docs/env_vars: document seatd session 2020-12-24 12:19:34 +01:00
Simon Ser 430d37846f
docs/env_vars: _WAYLAND_DISPLAY isn't used anymore 2020-12-24 12:17:31 +01:00
Simon Ser 21ed6582ce
Remove contrib/_incr_version
Not used anymore.
2020-12-24 12:14:19 +01:00
Simon Ser 64a2ca4dba
backend/drm: don't retry page-flip when modifiers are unsupported
Parse WLR_DRM_NO_MODIFIERS at startup. Don't parse IN_FORMATS when
WLR_DRM_NO_MODIFIERS is set, so that the legacy behaviour is better
reproduced.

When modifiers aren't supported, try the initial page-flip once only.
2020-12-23 19:49:27 +01:00
Simon Ser 54ec17ff64
backend/drm: use plural form for possible_crtcs
Make it clearer that this is a bitmask of CRTC indices.
2020-12-23 12:14:36 +01:00
Simon Ser 2de400a541
backend/drm: assert connector state after wlr_output_destroy 2020-12-22 19:53:33 +01:00
Simon Ser 85cf4b235d
backend/drm: destroy DRM connectors that disappear
This was lost during the refactoring. We were previously calling
wlr_output_destroy, which destroyed the connector as well.

Fixes: 248c7787c7 ("backend/drm: refactor wlr_output destruction")
2020-12-22 19:38:29 +01:00
Isaac Freund f6fc4c2883 subsurface: handle NULL parent in get_root_surface 2020-12-22 15:55:05 +01:00
Simon Ser ad4dae0844 backend/drm: error on size mismatch in drm_surface_blit 2020-12-22 15:53:10 +01:00
Simon Ser d3bcd63a40 backend/drm: remove drm_fb_acquire
Instead, import the buffer into GBM and KMS in drm_fb_import. Also move
the multi-GPU copy there if necessary.
2020-12-22 15:53:10 +01:00
Simon Ser 5ee8b12dc3 backend/drm: make drm_fb_acquire return a FB ID 2020-12-22 15:53:10 +01:00
Simon Ser d09abe86c1 backend/drm: drop export_drm_bo
Not used anymore.
2020-12-22 15:53:10 +01:00
Simon Ser dabd2e7207 backend/drm: grab DMA-BUF from wlr_buffer instead of gbm_bo
Get the DMA-BUF directly out of the wlr_buffer instead of relying on the
gbm_bo. This eliminates a roundtrip through GBM.
2020-12-22 15:53:10 +01:00
Simon Ser 83925f04c3 backend/drm: don't save texture in gbm_bo user data
The GBM BO is destroyed when released anyways.
2020-12-22 15:53:10 +01:00
Simon Ser 55b02f753f backend/x11: destroy buffers when destroying output 2020-12-21 16:31:07 +01:00
Simon Ser d6dbdd97e9 backend/x11: re-use pixmaps
Instead of re-importing a buffer each time we submit a frame, re-use the
pixmaps if possible.
2020-12-21 16:31:07 +01:00
Simon Ser defcd9b025 backend/wayland: re-use wl_buffers
Instead of re-importing a buffer each time we submit a new frame, re-use
the wl_buffer objects if possible.
2020-12-21 16:30:47 +01:00
Simon Ser de896caceb backend/wayland: remove EGL config
We don't use EGLSurface anymore, so we don't need to choose an EGL
config anymore.
2020-12-19 21:11:01 +01:00
Isaac Freund 6c08fe9796 xwayland: avoid crash on repeated server_finish_display() call
This function may end up being called more than once if the Xwayland
binary does not exist on the system.
2020-12-19 10:39:31 +01:00
Ronan Pigott 917ecca58e backend/drm: avoid gcc stringop-truncation warning 2020-12-18 22:19:16 +01:00
Simon Ser 352fdd1bb0
backend/drm: remove unused wlr_drm_plane.drm_format 2020-12-18 11:44:50 +01:00
Simon Ser 248c7787c7 backend/drm: refactor wlr_output destruction
Instead of hand-rolling our own manual wlr_output cleanup function, rely
on wlr_output_destroy to remove an output from the compositor's state.
2020-12-18 10:48:44 +01:00
Simon Ser 019ffe8a5b backend/drm: introduce wlr_drm_connector.name
The DRM backend is a little special when it comes to wlr_outputs: the
wlr_drm_connectors are long-lived and are created even when no screen is
connected.

A wlr_drm_connector only advertises a wlr_output to the compositor when
a screen is connected. As such, most of wlr_output's state is invalid
when the connector is disconnected.

We want to stop using wlr_output state on disconnected connectors.
Introduce wlr_drm_connector.name which is always valid regardless of the
connector status to avoid reading wlr_output.name when disconnected.
2020-12-18 10:48:44 +01:00
Simon Ser c89b131f29 backend/drm: introduce wlr_drm_conn_log
Simplify and unify connector-specific logging with a new
wlr_drm_conn_log macro. This makes it easier to understand which
connector a failure is about, without having to explicitly integrate the
connector name in each log message.
2020-12-18 10:48:44 +01:00
Simon Ser 0aefa18690 backend/x11: send more precise output present events
Instead of sending dummy output present events, use the X11 Present
extension to send more precise events.
2020-12-18 10:37:08 +01:00
Simon Ser f0c1b32120 util/time: add timespec_from_nsec 2020-12-18 10:37:08 +01:00
Simon Ser 94fda895ac backend/x11: use DRI3Open to get DRM FD
Instead of relying on EGL to retrieve the DRM FD, query it from the
DRI3 extension.

Use the EGL GBM platform, and drop the EGL config.
2020-12-18 10:12:55 +01:00
Simon Ser 1e2c7fce86 backend/drm: use wlr_drm_format_{create,add}
Instead of manually allocating and initializing the structs, use the new
wlr_drm_format helpers.
2020-12-18 09:41:12 +01:00
Simon Ser d37214cb16 render/drm_format_set: add wlr_drm_format_{create,add} 2020-12-18 09:41:12 +01:00
Simon Ser 253f447329
backend/drm: print DRM device name when scanning connectors 2020-12-17 20:50:19 +01:00
Simon Ser 9cd3f03f65
backend/drm: add wlr_drm_backend.name
Save the DRM device name in a wlr_drm_backend field, so that we can
easily use it for logging purposes.
2020-12-17 20:48:47 +01:00
Simon Ser 60001a75a2
backend/drm: remove nouveau workaround
The workaround is broken because drm_fb_acquire doesn't leave the EGL
context current anymore. We'll need to re-introduce it.

References: https://github.com/swaywm/wlroots/issues/2525
2020-12-17 20:38:02 +01:00
Simon Ser bdf26f87d5
render/allocator: ignore NULL in wlr_allocator_destroy 2020-12-17 20:34:19 +01:00
Simon Ser 0dcdb5e7a1 backend/x11: fix DRI3 formats not used
We queried DRI3 formats, but we weren't using them. Because of a typo,
only render formats were used.

Fixes: c59aacf944 ("backend/x11: query modifiers supported by X11 server")
Closes: https://github.com/swaywm/wlroots/issues/2552
2020-12-17 17:02:09 +01:00
Simon Ser 0aa2ba0c03 backend/headless: select the rendering device ourselves
Backends will eventually stop creating their renderer. To prepare for
this, stop using EGL_PLATFORM_SURFACELESS_MESA in the headless renderer.
Pick a render node using libdrm.

The new allocator/renderer creation logic looks very much like what will
end up in common code.
2020-12-16 12:12:36 +01:00
Simon Ser da2a216934
backend/drm: add wlr_drm_connector.backend
This allows the DRM code to have direct access to the wlr_drm_backend
without having to go through an upcast via get_drm_backend_from_backend.
2020-12-15 22:56:14 +01:00
Simon Ser 87bd718de5
backend: use fcntl(F_DUPFD_CLOEXEC) instead of dup
This makes sure the CLOEXEC flag is set on the dup'ed FD.
2020-12-15 20:52:53 +01:00
Simon Ser 1ca4d6b029
backend/drm: dup FD before wlr_gbm_allocator_create
The GBM allocator takes ownership of the DRM FD.
2020-12-15 20:50:04 +01:00
Simon Ser 3fd8098881
render/gles2: require GL_EXT_unpack_subimage
We implicitly depended on this extension.
2020-12-15 14:55:18 +01:00
Simon Ser e57a52e7f7
Remove inline keyword
The compiler is smarter at figuring out whether a function should be
inlined or not.
2020-12-15 13:49:42 +01:00
Simon Ser 93cd3a79b2 backend/drm: stop using GBM flags
gbm_bo_create_with_modifiers doesn't take GBM flags, so our
wlr_gbm_allocator interface doesn't either. We were still internally
using GBM flags in the DRM backend, leading to awkward back-and-forth
conversions.

The only flag passed to drm_plane_init_surface was GBM_BO_USE_LINEAR, so
turn that into a bool to make sure other flags can't be passed in.

Move the "force linear" logic out of init_drm_surface, because the
supplied wlr_drm_format should already contain that information.
2020-12-13 13:20:39 +01:00
Simon Ser 525fa6ada0
backend/x11: fix xinput mask mixed up with present
Don't mix xinput and present flags.

Fixes: d79a00bf02 ("backend/x11: switch to wlr_swapchain")
2020-12-13 12:21:21 +01:00
Simon Ser c59aacf944 backend/x11: query modifiers supported by X11 server 2020-12-13 12:16:52 +01:00
Simon Ser d79a00bf02 backend/x11: switch to wlr_swapchain 2020-12-13 12:16:52 +01:00
Simon Ser 16a51bbab2 backend/wayland: query render formats 2020-12-13 12:16:28 +01:00
Simon Ser 858a1940b5 build: move wayland-egl dependency to examples/
Now that the Wayland backend has moved to wlr_swapchain, only
client examples use the dependency. Stop linking against wayland-egl
in the wlroots library.
2020-12-13 12:16:28 +01:00
Simon Ser 441bac139f backend/wayland: use wlr_swapchain for cursor surface 2020-12-13 12:16:28 +01:00
Simon Ser 3923ff005d backend/wayland: use wlr_swapchain for main surface
The cursor surface still uses a wl_egl_window.

References: https://github.com/swaywm/wlroots/issues/1352
2020-12-13 12:16:28 +01:00
Simon Ser 038285d496 backend/wayland: stop rendering black frame on init
Instead of rendering a black frame, schedule a frame event to ask the
compositor to render a proper frame.
2020-12-13 12:16:28 +01:00
Simon Ser 768131e488 output: stop assuming a frame is pending in init
- The DRM backend initially doesn't have a frame scheduled initially.
  However the compositor is expected to set a mode to start the
  rendering loop (frame_pending is set to true in drm_crtc_pageflip).
- The headless and X11 backends have a timer to schedule frames, so they
  ignore this hint completely.
- The Wayland backend renders a fake frame to start the rendering loop.
  It's the only case where a frame is pending on init, move the
  assumption there.
2020-12-13 12:16:28 +01:00
Simon Ser 4c363a564f backend/drm: remove workaround for amdgpu DP MST
Closes: https://github.com/swaywm/wlroots/issues/2533
2020-12-11 23:27:13 +01:00
Ilia Bozhinov 12ede67c62 egl: fix memory leak
I have noticed this with LeakSanitizer, I hope these are all occurrences.
2020-12-10 23:29:59 +01:00
Simon Ser e9c1f0f7d3
output: improve basic test logging 2020-12-09 22:20:24 +01:00
Simon Ser f91e89fd9f render/gles2: query alpha size from render buffer
If we're using a render buffer, query the alpha size from it.

Closes: https://github.com/swaywm/wlroots/issues/2527
2020-12-09 21:45:28 +01:00
Simon Ser be8403e73d render/gles2: don't eglGetConfigAttrib on EGL_NO_CONFIG_KHR
If we don't have an EGL config, don't try to query anything from it.
2020-12-09 21:45:28 +01:00
Simon Ser 6ff478632a backend/drm: remove EGL config
Since we're using wlr_swapchain, we don't need to provide an EGL config.
2020-12-09 14:25:41 +01:00
Simon Ser 8a6930c138
render/drm_format_set: assert len <= cap when duplicating 2020-12-08 23:35:05 +01:00
Simon Ser 06ab41a160
backend/drm: fix missing wlr_drm_format.cap
This caused issues with wlr_drm_format_dup.
2020-12-08 23:32:04 +01:00
Simon Ser eb5886ddbe backend/headless: add support for direct scan-out
I was about to add a check to fail instead of crash when the compositor
uses direct scan-out, but with renderer v6 it's so simple to just add
support for direct scan-out, why bother?

Closes: https://github.com/swaywm/wlroots/issues/2523
2020-12-08 18:57:11 +01:00
Stephane Chauveau b790e5ea34 backend/drm: don't assume possible_crtcs has only one bit set
This isn't necessarily the case [1].

This should fix an assertion failure on Raspberry Pi 4 dual screen.

[1]: https://lists.freedesktop.org/archives/dri-devel/2020-August/275142.html

Closes: https://github.com/swaywm/wlroots/issues/1943
Co-authored-by: Simon Ser <contact@emersion.fr>
2020-12-08 18:38:42 +01:00
Simon Ser 863acb26c0 backend/drm: stop tracking overlay planes
We don't do anything with them. Once we do, we can easily add this back.
2020-12-08 18:38:42 +01:00
Simon Ser 29da97c185
render/drm_format_set: allocate using cap when duplicating
In wlr_drm_format_dup, allocate the new wlr_drm_format using cap instead
of len. This makes it so the cap field is up-to-date and the chunk of
memory isn't too small if we append new modifiers (we don't allow this
yet but might in the future).
2020-12-08 16:02:44 +01:00
Simon Ser e69bbfd0d6 backend/drm: unset current surface before importing
drm_fb_import_wlr may need to change the current EGL context. For
instance by calling drm_fb_clear, which calls wlr_buffer_unlock, which
may destroy a buffer if the cursor swapchain size has changed, which
calls gles2's destroy_buffer, which calls glDeleteFramebuffers.

Closes: https://github.com/swaywm/wlroots/issues/2479
2020-12-08 00:10:57 +01:00
Isaac Freund c9760569ae docs: fix error in wlr_output_set_damage() comment
output-buffer-local coordinates are neither scaled nor transformed
2020-12-07 20:45:36 +01:00
Dominik Honnef 431ec52b9c xwayland: use pipe instead of SIGUSR1 to signal readiness
Closes: https://github.com/swaywm/wlroots/issues/2154
2020-12-07 12:24:56 +01:00
Simon Ser 325cba6414
backend/drm: use EGL_PLATFORM_GBM_KHR
This is just the vendor-agnostic define for the GBM platform. It has the
same value as EGL_PLATFORM_GBM_MESA, so should work with old drivers
that only offer the Mesa-vendored extension too.
2020-12-07 12:06:38 +01:00
Simon Ser bfb59fd4d7 backend/headless: create a config-less EGL context 2020-12-07 11:40:45 +01:00
Simon Ser 037710b1d4 render/egl: support config-less wlr_egl
When using wlr_swapchain, there's no need to select an EGLConfig. Add
support for creating config-less EGL contexts.
2020-12-07 11:40:45 +01:00
Marten Ringwelski 44b1ff16e9 wlr-output-management: Handle modes added after initializing
The DRM backend adds custom modes to wlr_output.modes
Currently modes that are added after the first occurence of
wlr_output_configuration_head_v1 are not added to wlr_output_head.mode_resources.
2020-12-07 11:18:58 +01:00
Isaac Freund baf2319fd3 screencopy: don't use output transform for damage copy
Only wlr_output_damage works in transformed coordinates,
wlr_output->damage is in output-buffer-local coordinates.

This essentially reverts 1ecc1b5 and fixes 1477401.
2020-12-07 11:16:45 +01:00
Ilia Bozhinov 54b7ca56c0 drm: do not unset make/model before emitting destroy event 2020-12-07 10:39:29 +01:00
Isaac Freund 37cb3eb8dd screencopy: check if damage committed before copy
This check avoids copying stale state from output->pending.damage.
2020-12-06 17:11:46 +01:00
Isaac Freund 1ecc1b5987 screencopy: use output transform for damage copy 2020-12-06 17:11:46 +01:00
Isaac Freund 1477401acd screencopy: handle compositor not setting damage
Damage the full output if the compositor didn't submit damage but did
submit a buffer.
2020-12-06 17:11:46 +01:00
Simon Ser 90c8452959 backend/session/libseat: set XDG_SESSION_TYPE
libseat will call logind's SetType method if necessary.
2020-12-05 00:14:04 +01:00
Simon Ser 1336ad2a23
backend/drm: remove unused if in drm_connector_move_cursor
We return early if we don't have a plane.
2020-12-04 19:34:35 +01:00
Simon Ser 0e927533b0 backend/drm: query render formats
On some platforms it's possible that the display engine supports
modifiers not supported by the render engine.

Query render formats and intersect them with plane formats to accommodate
for this.
2020-12-03 10:52:25 +01:00
Simon Ser 82443ea46b render/drm_format_set: introduce wlr_drm_format_intersect
Intersects modifiers from two wlr_drm_format structs. If either format
doesn't support modifiers, the resulting format won't support modifiers.
2020-12-03 10:52:25 +01:00
Simon Ser 237c2cf2fb backend/drm: take a wlr_drm_format in init_drm_surface
Instead of taking a format code and wlr_drm_format_set, simplify the
function signature and take a single wlr_drm_format.
2020-12-03 10:52:25 +01:00
Simon Ser 513eca8dab
build: add leftover WLR_HAS_LIBCAP
We don't support libcap anymore. This was left as a comment by Meson:

    /* #undef WLR_HAS_LIBCAP */
2020-12-03 00:05:38 +01:00
Simon Ser 50b5f8558e
xwayland: add -core to flags
Xwayland has its own special handling for signals like SIGSEGV/SIGABRT.
Instead of leaving the job to the OS, it tries to walk up the call stack
(badly, because a lot of information is missing), print the stack trace
to stdout, then exit(1). This is very annoying because it prevents
Xwayland crashes from being easily debugged.

Xwayland has a flag "-core" that aborts instead of exiting. This allows
the OS to generate a coredump. It's far from perfect but better than
nothing, I guess.
2020-12-02 11:49:57 +01:00
Simon Ser 8bc5a92a98 Revert "backend/drm: stop force-probing connectors"
This reverts commit 713c1661b7.

It turns out we do need to force-probe on startup and on hotplug [1].
This is unfortunate, but that's just how the uAPI works. Fixing this
would require patching the kernel.

[1]: https://lists.freedesktop.org/archives/dri-devel/2020-November/289506.html

Closes: https://github.com/swaywm/wlroots/issues/2499
2020-12-01 11:31:03 +01:00
Simon Ser de9ff46629
backend/drm: "scaling mode" is a connector property
It's not a CRTC property. Remove it altogether since it's unused.
2020-11-30 11:57:08 +01:00
Simon Ser 2649600fa1
backend/drm: rotation is a plane property
"rotation" is a plane property, it's not a CRTC property. It was also
missing from plane_info.
2020-11-30 11:57:08 +01:00
Ilia Bozhinov d2329ac07a xwm: add wlr_xwayland_surface_restack() 2020-11-30 11:29:28 +01:00
Simon Ser 83a5d03bf3 render/egl: remove wlr_egl.external_only_dmabuf_formats
Replace it with wlr_egl.dmabuf_render_formats.
2020-11-30 11:08:44 +01:00
Simon Ser 1f15dd093d render: assert {X,A}RGB8888 are supported
The Wayland protocol requires those to be supported.
2020-11-30 11:08:44 +01:00
Simon Ser c94ab99ae2 render: rename wlr_renderer_get_formats
Rename wlr_renderer_get_formats to wlr_renderer_get_shm_texture_formats.
This makes it clear those formats are only suitable for shm import.
2020-11-30 11:08:44 +01:00
Simon Ser 49115e9d5d render: rename wlr_renderer_get_dmabuf_formats
Rename wlr_renderer_get_dmabuf_formats to
wlr_renderer_get_dmabuf_texture_formats. This makes it clear the formats
are only suitable for creating wlr_textures.
2020-11-30 11:08:44 +01:00
Simon Ser c045253927 backend/headless: use a format suitable for rendering
When allocating buffers, use a format suitable for rendering. This
avoids picking a format that won't work.
2020-11-30 11:08:44 +01:00
Simon Ser 5d008d9030 render: introduce wlr_renderer_get_dmabuf_render_formats
It describes which DMA-BUF formats can be used to render.
2020-11-30 11:08:44 +01:00
Simon Ser 61612ecb36 render: remove wlr_renderer_format_supported
Instead, callers can just use wlr_renderer_get_formats and iterate over
the list.

This function was unused in wlroots.
2020-11-30 11:08:44 +01:00
Simon Ser c15ca3793e render: expand wlr_renderer_get{,_dmabuf}_formats docs
Make it clear formats returned are only suitable for import/sampling.
These formats can't be used to be rendered to.
2020-11-30 11:08:44 +01:00
Simon Ser 44cea53e72 render/egl: don't rely on being able to open primary node
On some setups (e.g. remote access via SSH) the current user won't have
the permission to open the primary node at all. It's still possible to
use drmGetDevices to match the primary node name returned by EGL.

Closes: https://github.com/swaywm/wlroots/issues/2488
2020-11-28 19:21:36 +01:00
Isaac Freund 78e9e692e8 wlr-output-management: add missing NULL check
Handle allocation failure in wlr_output_configuration_head_v1_create
2020-11-28 18:24:14 +01:00
Simon Ser 6485fadc16
backend/wayland: don't set EGL visual
The Wayland platform doesn't have visuals. By chance,
WL_SHM_FORMAT_ARGB8888 is zero, which means egl_get_config was ignoring
it and everything worked fine.
2020-11-25 13:52:05 +01:00
Simon Ser 713c1661b7 backend/drm: stop force-probing connectors
After discussing with Pekka and Daniel on #dri-devel, we concluded [1]
that user-space shouldn't need to force-probe connectors. Force-probing
can take some time, so using drmModeGetConnectorCurrent can result in
faster start-up.

Users can manually trigger a force-probe if necessary:

    echo detect | sudo tee /sys/class/drm/card0-DP-1/status

Or just by running a tool like drm_info.

A similar change has been submitted to Weston [2].

[1]: https://lists.freedesktop.org/archives/dri-devel/2020-November/287728.html
[2]: https://gitlab.freedesktop.org/wayland/weston/-/issues/437
2020-11-24 15:00:46 +01:00
Simon Ser 154fe8696f backend/x11: log unhandled client messages 2020-11-24 14:57:25 +01:00
Simon Ser f6c36f8881 backend/x11: log unhandled X11 events 2020-11-24 14:57:25 +01:00
Simon Ser 52805feae9 backend/x11: log errors
Register an X11 error handler, and optionally use xcb-errors to print a
detailed message.
2020-11-24 14:57:25 +01:00
Isaac Freund 262740bc9a backend/libseat: fix change_vt return value
This should return true on success and false on failure not vice-versa.
2020-11-24 13:09:13 +01:00
Kenny Levinsen ebecc5404b surface: Make send_enter store entered outputs
wlr_surface_send_enter now stores outputs that have been entered.
Combined with a new 'bind' event on wlr_output, this allows us to delay
enter events as necessary until the respective wl_output global has been
bound.

Closes: https://github.com/swaywm/wlroots/issues/2466
2020-11-23 10:58:50 +01:00
Simon Ser 754179dacd backend/session: add a timeout waiting for GPUs
If a GPU doesn't show up in 10s, bail out and return zero GPUs.
2020-11-19 22:47:49 +01:00
Simon Ser c491a21d25 backend/session/logind: log when using XDG_SESSION_ID
This makes it easier to figure out how wlroots selected the session.
2020-11-19 22:47:49 +01:00
Simon Ser 0e76f92de7 backend/session: replace session_signal with events.active
This is more idiomatic wlroots API. The new name makes it clear that the
signal is emitted when wlr_session.active changes.
2020-11-19 22:47:49 +01:00
Simon Ser cd95d70df0 Revert "session/logind: support CanGraphical property"
We now use udev to wait for DRM card devices.

This reverts commit 3ebf079a9a.
2020-11-19 22:47:49 +01:00
Simon Ser fbf11a41e1 backend/session: wait for DRM device if none found
Wait for a DRM device if none is found in wlr_session_find_gpus. This
can happen if the compositor is loaded before the display kernel driver.

This supersedes the logind CanGraphical property.

To test, e.g. with i915 and sway:

    rmmod -f i915
    sway &
    modprobe i915

Closes: https://github.com/swaywm/wlroots/issues/2093
2020-11-19 22:47:49 +01:00
Simon Ser 76bcddf071 backend/session: introduce wlr_session.events.add_drm_card
This is triggered when a new DRM card is added.

An easy way to test this patch is `modprobe vkms`.
2020-11-19 22:47:49 +01:00
Simon Ser 768fbaad54 backend/session: filter udev events by sysname
We're only interested in card devices. The loop over wlr_session.devices
would take care of ignoring non-card events, but a future patch will
listen to udev "add" events as well.
2020-11-19 22:47:49 +01:00
Simon Ser 44a4792fd8 backend/session: operate on wlr_device
Instead of operating on FDs in {open,close}_device, operate on
wlr_devices. This avoids the device lookup in wlr_session and allows
callers to have access to wlr_device fields.

For now, we use it to remove wlr_session_signal_add and replace it with
a more idiomatic wlr_session.events.change field. In the future, other
events will be added.
2020-11-19 22:47:49 +01:00
Simon Ser 63df2bcbe6 backend/session: don't return FD on failure in open_file
When wlr_session_open_file fails, don't return the FD, otherwise the
caller will think the call succeeded.
2020-11-19 22:47:49 +01:00
Kenny Levinsen fb3bea8014 backend/drm: Use legacy gamma size for legacy backend
We would always return the GAMMA_LUT_SIZE property if available, and
only fall back to legacy gamma size otherwise. This leads to issues if
both are available in differs in size while we use the legacy backend.

Ensure that we only return the legacy size if we're using the legacy
backend.

Closes: https://github.com/swaywm/wlroots/issues/2429
2020-11-19 22:46:25 +01:00
Ilia Bozhinov 6284af121f texture: document that functions should not be called while rendering 2020-11-19 11:35:04 +01:00
Ronan Pigott dc7c6c4860 render/egl: recognize EGL_BAD_DEVICE_EXT error 2020-11-19 00:42:37 +01:00
Simon Ser e18599b05e render/egl: stop including eglmesaext.h
This is a Mesa-specific header that was needed because some Wayland EGL
extensions were missing from the Khronos registry. Now that this has
been fixed [1] and Mesa [2] & glvnd [3] have sync'ed their headers, we
can drop this workaround.

[1]: https://github.com/KhronosGroup/EGL-Registry/pull/95
[2]: https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/4953
[3]: https://gitlab.freedesktop.org/glvnd/libglvnd/-/merge_requests/225
2020-11-19 00:42:09 +01:00
Simon Ser 526ae5944c
build: improve summary via bool_yn
Shows YES/NO instead of 1/0, improves readability.
2020-11-15 23:15:49 +01:00
Simon Ser 02df7b7ac8 backend/headless: implement export_dmabuf 2020-11-15 22:54:07 +01:00
Simon Ser 61f8cdfb9e backend/headless: switch to wlr_swapchain 2020-11-15 22:54:07 +01:00
Simon Ser eb8360bda3 render: introduce wlr_renderer_get_drm_fd 2020-11-15 22:54:07 +01:00
Simon Ser c8d95acc37 render/egl: introduce wlr_egl_dup_drm_fd 2020-11-15 22:54:07 +01:00
Simon Ser eef8b3dde8 backend/drm: check drm_surface_render_black_frame return value
This avoids hitting an assertion in drm_fb_lock_surface when
we failed to render a black frame.
2020-11-15 22:48:42 +01:00
Simon Ser c881008e1c backend/drm: add support for wlr_swapchain multi-GPU 2020-11-15 22:48:42 +01:00
Simon Ser 8058e338ea backend/drm: get rid of wlr_drm_fb_type
Since all DRM FBs are backed by a wlr_buffer, there's no need for this
anymore.
2020-11-15 22:48:42 +01:00
Simon Ser 68a8d99055 backend/drm: add support for wlr_swapchain buffer age 2020-11-15 22:48:42 +01:00
Simon Ser c11c6c4568 render/swapchain: add support for buffer age 2020-11-15 22:48:42 +01:00
Simon Ser ef846a8839 backend/drm: use wlr_swapchain 2020-11-15 22:48:42 +01:00
Simon Ser 1245730ea2 render/gles2: fix y-inverted output when rendering to buffer 2020-11-15 22:48:42 +01:00
Simon Ser 6136fe87d1 render/gles2: implement wlr_renderer_bind_buffer 2020-11-15 22:48:42 +01:00
Simon Ser c88c54fb38 render: introduce wlr_renderer_bind_buffer 2020-11-15 22:48:42 +01:00
Simon Ser 0b40d09a21 buffer: add wlr_client_buffer_get 2020-11-15 22:48:42 +01:00
Simon Ser b0a663d39d render: introduce wlr_swapchain
The swapchain maximum capacity is set to 4, so that we have enough room
for:

- A buffer currently displayed on screen
- A buffer queued for display (e.g. to KMS)
- A pending buffer that'll be queued next commit
- An additional pending buffer in case we want to invalidate the
  currently pending one
2020-11-15 22:48:42 +01:00
Simon Ser 7c6212a0f7 render/drm_format_set: introduce wlr_drm_format_dup 2020-11-15 22:48:42 +01:00
Simon Ser 5913040110 render: introduce wlr_gbm_allocator 2020-11-15 22:48:42 +01:00
Simon Ser f47445f142 render: introduce wlr_allocator 2020-11-15 22:48:42 +01:00
Simon Ser aaa3fcf66f
backend/libinput: require libinput 1.14
We have the policy of requiring up-to-date dependencies instead of
adding conditionals for older versions. libinput 1.14 was published more
than 1 year ago.
2020-11-15 18:57:16 +01:00
Mykola Orliuk 2eae9ec7c8 backend/wayland: Set cursor indivdualy per output 2020-11-12 12:31:32 +01:00
Mykola Orliuk 44531e16e0 backend/wayland: Add active pointer per host seat
Every host seat with pointer capability propagates events to one of
sub-pointer depending which output window we entered.
active_pointer tracks reference to sub-pointer on enter/leave events to
avoid lookup for it on every move events.

Fixes swaywm/wlroots#1499
2020-11-12 12:31:32 +01:00
Mykola Orliuk ce8855ca2a backend/wayland: Bind pointer listener to seat 2020-11-12 12:31:32 +01:00
Mykola Orliuk 07e2e0f60c backend/wayland: Listen to pointers from all seats
This effectively gets swaywm/wlroots#1499 to the point where
functionality somewhat preserved and no crash happens.
We still can have only one cursor, but we can control it from multiple
seats in time-sharing manner by entering/leaving output.
2020-11-12 12:31:32 +01:00
Mykola Orliuk 44c4773d58 backend/wayland: Use seat name in input names 2020-11-12 12:31:32 +01:00
Mykola Orliuk 70ffda3ea3 backend/wayland: Add registering multiple seats 2020-11-12 12:31:32 +01:00
Mykola Orliuk 85b0872650 backend/wayland: Link input devices with seats 2020-11-12 12:31:32 +01:00
Mykola Orliuk 40bfd9f8f7 backend/wayland: Bind seat listener to wlr_wl_seat 2020-11-12 12:31:32 +01:00
Isaac Freund e06c9e43af Remove unneeded includes from wlr_input_device.h
This uncovered many places where we were using things without directly
including them.
2020-11-11 15:40:47 +01:00
Isaac Freund 0724b3c453 Use uint32_t in wlr_renderer_begin signature
This matches the signature of wlr_renderer_impl.begin
2020-11-11 11:01:46 +01:00
Isaac Freund 7693f61d81 Replace wlr_key_state with wl_keyboard_key_state
There's no reason to have duplicate enums
2020-11-11 10:58:38 +01:00
Simon Ser 238d1c078f
Update version to 0.12.0 2020-11-08 15:01:44 +01:00
Ilia Bozhinov 9595f95452 xdg_shell: handle inert popups
xdg_popups can be destroyed by the compositor when closed. When this happens,
wlroots makes the xdg_popup surface inert and resets the xdg_surface role to
NONE.

Currently, wlroots sends a protocol error and asserts that an xdg_surface has
a role when committed. This is racy if at the same time the client commits an
xdg_popup and the compositor closes it. This patch removes the assertion and
ignores commits on xdg_surfaces without a role set.
2020-11-08 14:26:03 +01:00
Simon Ser 372a52ecc0
input-method: send modifiers in set_keyboard
Otherwise the client might have an outdated modifiers state. The same is
done in wlr_seat_keyboard [1].

[1]: 8348fc3ef8/types/seat/wlr_seat_keyboard.c (L163)
2020-11-06 19:14:55 +01:00
Isaac Freund 8348fc3ef8 xwayland: remove unused listener 2020-11-05 13:18:04 +01:00
Simon Ser 4471a83ed0 screencopy: send failed when copying a DMA-BUF with a region
We don't support that yet.
2020-11-05 09:43:42 +01:00
Simon Ser 51fc7ddb29 screencopy: perform DMA-BUF copy on output commit 2020-11-05 09:43:42 +01:00
Simon Ser f0945e112f export-dmabuf: export DMA-BUF on output commit
We were previously exporting DMA-BUFs when receiving the capture_output
request, and sending a done event on wlr_output.events.precommit. Instead,
export and send done on wlr_output.events.commit.
2020-11-05 09:43:42 +01:00
Simon Ser 8c8164c4a6 output: add when field to wlr_output_event_commit
Similar to the one already present in wlr_output_event_precommit.
2020-11-05 09:43:42 +01:00
Simon Ser ccd313e01a output: update docs to reflect reality
The docs were outdated and weren't matching what the DRM backend does
(the only implementor of wlr_output_export_dmabuf).
2020-11-05 09:43:42 +01:00
Simon Ser 1328477a82 backend/drm: export pending FB in export_dmabuf, if any
This allows callers to grab the current frame right after committing it,
without having to incur a full vblank worth of latency.
2020-11-05 09:43:42 +01:00
Simon Ser 2934a72920
screencopy: stop setting needs_frame flag
This is already done by wlr_output_schedule_frame (it calls
wlr_output_update_needs_frame).
2020-11-04 12:02:56 +01:00
Simon Ser c2db691cad gamma-control-v1: apply gamma LUT when output gets enabled
Closes: https://github.com/swaywm/wlroots/issues/2372
2020-11-03 17:47:04 +01:00
Simon Ser be1e7647c3 xwayland: log unhandled NET_WM_STATE property changes 2020-11-03 18:36:30 +02:00
Simon Ser 1fdaaf697a
xwayland: minor code style fixes 2020-11-03 15:31:23 +01:00
Simon Ser ee43ef3c9d backend/drm: fix "a page-flip is already pending" errors on modeset
When performing a modeset, the DRM backend will request a page-flip
event. However frame_pending wasn't set to true, so any subsequent
wlr_output_schedule_frame calls would imemdiately trigger a synthetic
frame event, asking the compositor to submit a new frame. Committing the
new frame fails with "a page-flip is already pending" error in the DRM
backend.
2020-11-03 08:09:54 +01:00
Simon Ser f0ddcd361e
render: define EGL_NO_PLATFORM_SPECIFIC_TYPES (#2452)
This avoids Xlib.h inclusion via EGL headers. See [1] for discussion.

This change is based on a Weston commit [2].

[1]: https://github.com/KhronosGroup/EGL-Registry/pull/111
[2]: https://gitlab.freedesktop.org/wayland/weston/commit/526765ddfdfd
2020-11-02 10:51:52 +01:00
Tudor Brindus 0f9b2bfa64 render/dmabuf: use bitmask for wlr_dmabuf_attributes_flags 2020-10-31 23:15:21 +01:00
Tudor Brindus 368d0146fb util/edges: use bitmask for wlr_edges 2020-10-31 23:15:21 +01:00
Tudor Brindus 0fdb41fe7c types/wlr_output_layout: use bitmask for wlr_direction 2020-10-31 23:15:21 +01:00
Tudor Brindus 7c6e06fd13 types/wlr_keyboard: use bitmasks for wlr_keyboard_led and wlr_keyboard_modifier enums 2020-10-31 23:15:21 +01:00
Marten Ringwelski 85757665e6 backend/drm: Check if output is enabled before sending frame event
When an output is disabled one last pageflip will happen to disable it.
Currently this pageflip causes a frame event.
Since the output is disabled we don't want to send this frame event.
2020-10-30 10:04:54 +01:00
Ilia Bozhinov 346188c015 xdg_shell: fix a typo 2020-10-27 18:49:19 +01:00
Scott Moreau 79c7591dc1 foreign toplevel: Fix whitespace error 2020-10-20 19:49:44 +02:00
Daniel Kondor b4ed8b3d74 foreign toplevel: send parent event only to clients that support it 2020-10-20 09:12:07 +02:00
Tudor Brindus 5217456b50 xwayland: fix minor typo in debug log
This accidentally slipped through 1b0e4c7.
2020-10-20 09:09:49 +02:00
Mykola Orliuk 31aa7f4c95 backend/wayland: fix some keyboard/touch leaks 2020-10-18 21:25:25 +02:00
Mykola Orliuk 009cd634a2 backend/wayland: fix input creation error handling 2020-10-18 21:25:25 +02:00
Mykola Orliuk 41bf1c6871 backend/wayland: add error flow in create_wl_seat 2020-10-18 21:25:25 +02:00
Daniel Kondor 36395e5b1c
foreign-toplevel-management: report parent toplevel
Based on the wlr-protocols PR:
https://github.com/swaywm/wlr-protocols/pull/52
2020-10-18 17:14:35 +02:00
Mykola Orliuk 1ac5257357 backend/wayland: factor out wlr_wl_seat 2020-10-18 16:28:12 +02:00
Mykola Orliuk df417b7e95 backend/wayland: manage cursor for current pointer 2020-10-18 16:28:12 +02:00
Ilia Bozhinov b98522b38f
backend/wayland: add touch support to the wayland backend
Goal currently is to get support working for a single output, thus there is only one touch device created.

Multi-output support is left for later.
2020-10-18 15:33:03 +02:00
Isaac Freund e410ff8dd4 wlr_drag: remove unused point_destroy field 2020-10-18 15:17:28 +02:00
Isaac Freund 616f06c25c xdg_positioner: remove unused field
The resource field of wlr_xdg_positioner is never initialized or
accessed within wlroots. The wl_resource for this interface is stored
in the wlr_xdg_positioner_resource struct.
2020-10-16 12:49:37 +02:00
Ilia Bozhinov 99f3c643bf xwayland: add set_geometry event
This is necessary to react to changes in position of override-redirect
views.
2020-10-14 21:49:51 +02:00
Tudor Brindus afeb941ca0 xwayland: notify requestor when we fail to respond to their request
We already mostly did this, but there were a couple of branches
(`calloc` failures) where we'd bail without letting the other side know.

Refs swaywm/sway#4007. Likely not going to be a real improvement there
(if `calloc` fails you're already pretty screwed), but it does address a
theoretical possibility.
2020-10-13 09:02:20 +02:00
Tudor Brindus 7bb9d48dd1 xwayland: remove stale transfers from the same requestor
It seems that if we ever try to reply to a selection request after
another has been sent by the same requestor (we reply in FIFO order),
the requestor never reads from it, and we end up stalling forever on a
transfer that will never complete.

It appears that `XCB_SELECTION_REQUEST` has some sort of singleton
semantics, and new requests for the same selection are meant to replace
outstanding older ones. I couldn't find a reference for this, but
empirically this does seem to be the case.

Real (contrived) case where we don't currently do this, and things break:

* run fcitx
* run Slack
* wl-copy < <(base64 /opt/firefox/libxul.so)  # or some other large file
* focus Slack (no need to paste)

fcitx will send in an `XCB_SELECTION_REQUEST`, and we'll start
processing it. Immediately after, Slack sends its own. fcitx hangs for a
long, long time. In the meantime, Slack retries and sends another
selection request. We now have two pending requests from Slack.

Eventually fcitx gives up (or it can be `pkill`'d), and we start
processing the first request Slack gave us (FIFO). Slack (Electron?)
isn't listening on the other end anymore, and this transfer never
completes. The X11 clipboard becomes unusable until Slack is killed.

After this patch, the clipboard is immediately usable again after fcitx
bails. Also added a bunch of debug-level logging that makes diagnosing
this sort of issue easier.

Refs swaywm/sway#4007.
2020-10-12 10:53:42 +02:00
Tudor Brindus 1b0e4c7e6e xwayland: introduce WLR_XWAYLAND for specifying which Xwayland to use
When debugging Xwayland-related issues, a common first step in debugging
has been to ask the reporter to move their real Xwayland to
/usr/bin/Xwayland.bin, and create a shell script starting Xwayland with
extra arguments under the original /usr/bin/Xwayland location.

Introducing a `WLR_XWAYLAND` environment variable makes this less
invasive, by allowing the user to swap out Xwayland without resorting to
global system changes (or source patches).
2020-10-11 09:00:52 +02:00
Tudor Brindus feb0e1c74d xwayland: fix use-after-free in selection handling
Fixes #2425.

wlroots can only handle one outgoing transfer at a time, so it keeps a
list of pending selections. The head of the list is the currently-active
selection, and when that transfer completes and is destroyed, the next
one is started.

The trouble is when you have a transfer to some app that is misbehaving.
fcitx is one such application. With really large transfers, fcitx will
hang and never wake up again. So, you can end up with a transfer list
that looks like this:

| T1: started | T2: pending | T3: pending | T4: pending |

The file descriptor for transfer T1 is registered in libwayland's epoll
loop. The rest are waiting in wlroots' list.

As a user, you want your clipboard back, so you `pkill fcitx`. Now
Xwayland sends `XCB_DESTROY_NOTIFY` to let us know to give up. We clean
up T4 first.

Due to a bug in wlroots code, we register the (fd, transfer data
pointer) pair for T1 with libwayland *again*, despite it already being
registered. We do this 2 more times as we remove T3 and T2.

Finally, we remove T1 and `free` all the memory associated with it,
before `close`-ing its transfer file descriptor.

However, we still have 3 copies of T1's file descriptor left in the
epoll loop, since we erroneously added them as part of removing T2/3/4.
When we `close` the file descriptor as part of T1's teardown, we
actually cause the epoll loop to wake up the next time around, saying
"this file descriptor has activity!" (it was closed, so `read`-ing would
normally return 0 to let us know of EOF).

But instead of returning 0, it returns -1 with `EBADF`, because the file
descriptor has already been closed. And finally, as part of error-handling
this, we access the transfer pointer, which was `free`'d. And we crash.
2020-10-11 08:59:08 +02:00
Tudor Brindus ab80ad902e xwayland: using %m in `wlr_log` is broken, use `wlr_log_errno` instead
This one was awful to track down, but calls to `wlr_log` with %m have
the errno masked by the `isatty` call in `log_stderr`. Switch them to
`wlr_log_errno` instead.

Cue quality "how can read(2) POSSIBLY be returning ENOTTY?" moments.
2020-10-11 06:36:23 +02:00
Tudor Brindus ec3f432bbb examples: use `perror` instead of `fprintf` GNU %m `printf` extension 2020-10-11 06:36:23 +02:00
Simon Ser 45c1a3621c backend/libinput: improve logger callback
- Add a prefix to make it clear log messages come from libinput
- Properly convert libinput log priority to wlroots'
2020-10-10 10:51:32 -04:00
Roman Gilg 86c0b9986b output-management-v1: send complete head state on enable change
The data of a head is only sent when it is enabled. While the head was disabled
data might have been changed. In this case clients were not informed about this
change. A later enable change that does not also update the other data must
still lead to the propagation of this data.

Since we do not know what other data was changed while the head was disabled
just send together with an enable change all current data.
2020-10-09 16:28:09 +02:00
Isaac Freund 8dec751a6d layer-shell: error on 0 dimension without anchors
The protocol requires clients to set opposing anchors when requesting
a width or height of 0.

The goal of this patch is not to break clients that rely on this
behavior but to improve the consistency of the layer shell ecosystem
through adherence to the protocol.
2020-10-09 15:42:46 +02:00
Rouven Czerwinski 5012121d33 xwm: add loop detection for read_surface_parent
Implement a simple loop detection while trying to retrieve the parent
for a TRANSIENT_FOR window.

Fixes swaywm/sway#4624
2020-10-08 19:32:58 +02:00
Simon Ser 87836dcb55 backend: remove check for _WAYLAND_DISPLAY
I'm not sure what this was used for, but it's not used by libwayland.
Setting _WAYLAND_DISPLAY would result in the Wayland backend being
picked but would ignore the actual value of the env variable.
2020-09-16 15:53:14 -04:00
Isaac Freund 8ad2cc39eb layer-shell: add for_each_popup
This brings the layer-shell api in line with that of xdg-shell and
avoids reimplementing this function in every compositor in order to
render layer shell popups correctly.
2020-09-10 17:44:55 +02:00
Roman Gilg bae8d7593c output-management-v1: send head identifying information
With version 2 we send make, model and serial number to allow clients the
identification of heads.
2020-09-04 21:21:18 +02:00
Roman Gilg e4a7075a9e output-management-v1: add head identifying events
The following information through separate events are added:
- make
- model
- serial_number

This should allow clients to identify a display over different sessions and
load configuration data back.

A note is added that the description should be preferred when representing a
display in UI to users but as a short form for example the model could be used
in this case of course too.
2020-09-04 21:21:18 +02:00
Ilia Bozhinov 3e03f786ee xwayland: disconnect display destroy listener even if xwayland didn't initialize 2020-09-04 17:55:23 +02:00
nerdopolis e44bed0c2b Accommodate for CONFIG_VT=0, all TTYs are in seat0, but not all
seat0s have TTYs
2020-09-04 11:46:54 +02:00
Patrick Steinhardt fa05d3cde6 session: Don't refuse unprivileged creation of "direct" backend
When starting a compositor that's using the "direct" session backend,
wlroots needs to handle calls to `drmSetMaster()` and `drmDropMaster()`.
As both calls used to require `CAP_SYS_ADMIN`, wlroots thus simply
refused starting in case the process doesn't enjoy evelated privileges.

Permission rules have changed since linux.git commit 45bc3d26c95a (drm:
rework SET_MASTER and DROP_MASTER perm handling, 2020-03-19). As a
result, starting with Linux v5.8, both ioctls will now also succeed if
the process is currently or has been the DRM master. And as the first
process to open render nodes will become the DRM master automatically,
this effectively means that process elevation is not strictly required
in all setups anymore.

So let's drop the `geteuid() != 0` permission check to allow those new
rules to do their magic.
2020-09-04 11:39:25 +02:00
Ilia Bozhinov efe6414640 wayland: emit relative pointer events only for current pointer 2020-09-04 10:13:35 +02:00
Drew DeVault a9cbfd950e Remove xdg-shell v6
Ding dong the witch is dead

Fixes #2381
2020-09-03 00:01:10 +02:00
Ryan Walklin 28cedb5623 Quieten failure to set login session type
(almost certainly due to systemd version <246)
2020-09-02 11:35:32 +02:00
Simon Ser 971de474f0 backend/session/libseat: register log handler
Route libseat errors through wlroots logging infrastructure.

This requires libseat 0.2.0.
2020-09-01 12:09:25 +02:00
Valentin 65abd4e92a Fix undefined behavior
Without the casts the bytes accesses get converted to int. but int is
not guaranteed to be 4 bytes large. Even when it is 4 bytes large
`bytes[3] << 24` does not fit because int is signed.
2020-09-01 11:58:56 +02:00
Valentin 8b744412aa Use fixed size integer type
This type is meant to be 4 bytes large as seen in _XcursorReadUInt which
always reads 4 bytes. An unsigned int is often 4 bytes large but this
isnt' guaranteed so it is cleaner to use the exact type we want.
2020-09-01 11:58:56 +02:00
Simon Ser 2b418b4d88 examples/dmabuf-capture: add extra roundtrip for wl_output listener
This example was relying on wl_display_dispatch being enough to fetch
output information. This worked by chance.

Add an explicit wl_display_roundtrip.

Other examples don't setup wl_output listeners, so they should be fine.

Fixes: 297354f847 ("Remove unnecessary wl_display_dispatch calls")
Closes: https://github.com/swaywm/wlroots/issues/2386
2020-08-31 08:43:54 -06:00
Simon Ser b0144c7ded output-power-management-v1: listen to output commit 2020-08-27 13:54:19 -06:00
Simon Ser c674241ec0 output: introduce wlr_output_event_commit
This event contains a `committed` bitfield, which allows callers to know
which output fields changed during the commit.

This allows users to setup a single atomic commit listener, instead of
setting up one listener for each event (mode, scale, transform, and so
on).

References: https://github.com/swaywm/wlroots/issues/2098
2020-08-27 13:54:19 -06:00
Guido Cella 6949d0fd38 render: Don't crash on 0 dimensions
Don't force compositors to check when an empty shape is being renderered.
References #2282. This was motivated by dwl crashing when setting window
borders to 0 (djpohly/dwl#51).
2020-08-27 17:39:31 +02:00
Isaac Freund 2072d59da5 xdg-shell: split last-acked and current state
These states are distinct in the time period between the ack_configure
and the next commit on the surface. Splitting these states avoids the
following race for example:

- client starts at 1000x1000
- wlr_xdg_toplevel_set_size 500x500
- size is different -> configure sent
- client acks the configure
- wlr_xdg_toplevel_set_size 1000x1000
- compare_xdg_toplevel_state returns true since there is no pending
  configure and the currently committed size is still 1000x1000
- no new configure is sent
- client commits at the size it last acked, 500x500
2020-08-27 12:36:29 +02:00
Kenny Levinsen 330c50b48d session: Add missing init to direct-freebsd
bad1e9afa8 ("session: Add libseat backend") introduced a change to to
how session backends initialize, but failed to update the FreeBSD
specific version of the direct backend accordingly.

Closes: https://github.com/swaywm/wlroots/issues/2376
2020-08-26 22:56:54 +02:00
Simon Ser 297354f847 Remove unnecessary wl_display_dispatch calls
wl_display_roundtrip already takes care of dispatching the display.
2020-08-24 07:55:35 -06:00
Kenny Levinsen bad1e9afa8 session: Add libseat backend 2020-08-24 11:13:55 +02:00
Ilia Bozhinov 9feeb2738d
backend/wayland: destroy relative pointer when output is disconnected
Fixes #2243
Fixes #2106
2020-08-17 11:33:57 +02:00
Simon Ser 379835c42f examples/simple: use wlr_renderer instead of GL 2020-08-14 09:57:31 -06:00
Simon Ser 801c7670b7 examples/simple: use wlr_output_preferred_mode 2020-08-14 09:57:31 -06:00
Daniel De Graaf c236f60bb6 wlr_virtual_keyboard: fix fd leak 2020-08-09 21:13:06 +02:00
Ryan Walklin 7e990a2991 Don't set XDG_SESSION_TYPE unless logind SetType succeeds 2020-08-07 19:15:25 +02:00
Ryan Walklin e81d2086c0 Also set XDG_SESSION_TYPE 2020-08-07 19:15:25 +02:00
Ryan Walklin f0d03fb892 Implement logind session SetType method to change session type to wayland 2020-08-07 19:15:25 +02:00
Simon Ser 30226eb1fb gamma-control-v1: fix use-after-free in gamma_control_handle_set_gamma
gamma_control_send_failed destroys gamma_control.
2020-08-05 18:18:11 +02:00
Ilia Bozhinov 0032954c75 make sure to fail setting gamma on disabled outputs 2020-08-03 12:05:35 +02:00
Devin J. Pohly aaf490d794 drm: fix uninitialized read
get_drm_prop_blob does not set path_len if it returns NULL.  Check the
return value before path_len to avoid reading uninitialized memory.

(Granted, this doesn't change the logic at all, but it does make
Valgrind a bit happier.)
2020-07-31 09:32:14 +02:00
Ilia Bozhinov 74f7be7287 xwayland: do not allow apps to change focus after wlroots request 2020-07-30 13:40:36 +02:00
Simon Ser 1dbcfdaf81 render/gles2: remove gles2_procs
Move the global into wlr_gles2_renderer. This removes global state and
allows us to have multiple renderers with different GL loaders.
2020-07-28 06:59:07 -06:00
Simon Ser 62da61716f render/gles2: make push/pop debug functions take a wlr_renderer 2020-07-28 06:59:07 -06:00
Simon Ser e8872d9ed7 render/gles2: keep ref to wlr_gles2_renderer in wlr_gles2_texture 2020-07-28 06:59:07 -06:00
Simon Ser 26af316b3b render/gles2: make wlr_gles2_texture_from_* private
These functions are unused by compositors (see e.g. [1]) and prevent
wlr_gles2_texture from accessing wlr_gles2_renderer state. This is an
issue for proper teardown [2] and for accessing GLES2 extensions.

[1]: https://github.com/swaywm/wlroots/pull/1962#issuecomment-569511830
[2]: https://github.com/swaywm/wlroots/pull/1962
2020-07-28 06:59:07 -06:00
Rouven Czerwinski c32d89ee3e
examples: remove unnecessary gles2.h imports 2020-07-28 13:36:09 +02:00
Scott Moreau 6d0ee53e1a xwm: Set _NET_WM_STATE_FOCUSED property for the focused surface
Certain clients require this property to be set for expected behavior.
Most notably, steam client CSD maximize button no longer worked
after unmaximizing once, unless the state was changed by another
method. The state is unset whenever another surface gains focus.
2020-07-27 14:26:30 +02:00
Antonin Décimo 1ae2d976c0 xwayland: free server in error path 2020-07-27 10:49:19 +02:00
Antonin Décimo d9bb792794 Fix incorrect format parameters 2020-07-27 10:49:19 +02:00
Ronan Pigott 39fd2335bf virtual_pointer: remember current axis for axis events 2020-07-27 10:39:41 +02:00
Simon Ser c72efcd1ce xwayland/xwm: use initializer for props in xsurface_set_wm_state
This avoids uninitialized items and makes it clear where the magic
number 2 is coming from.
2020-07-22 13:49:24 -06:00
Simon Ser 13f35139d3 xwayland/xwm: add prop count assert in xsurface_set_net_wm_state
This helps mitigate buffer overflows.
2020-07-22 13:49:24 -06:00
Simon Ser cd4827b3b6 xwayland/xwm: don't insert surface in list on error
In case wl_event_loop_add_timer errors out, don't insert the free'd
wlr_xwayland_surface in the list.

Closes: https://github.com/swaywm/wlroots/issues/1721
2020-07-22 13:48:59 -06:00
Antonin Décimo 9686895b4e Fix typos 2020-07-21 23:00:13 +02:00
Tobias Langendorf bd387da62d xwm: add support for xwayland minimize 2020-07-21 13:20:17 +02:00
Simon Ser 751a21d94f
Update version to 0.11.0 2020-07-16 00:27:23 +02:00
Tudor Brindus a145430afa
input/pointer: add wlr_seat_pointer_wrap
It allows a compositor to do things like skip motion events on pointer
constraint unlock.

References: https://github.com/swaywm/sway/pull/5431
2020-07-15 19:31:13 +02:00
Andri Yngvason 6ef5d18757 render: egl: Use current display to restore NULL context
eglGetCurrentDisplay() returns EGL_NO_DISPLAY when there is no context current
and eglMakeCurrent() needs a display argument.

Fixes #2327
2020-07-14 19:55:20 +02:00
xdavidwu 842df2bd6c examples/input-method-keyboard-grab: new example 2020-07-08 11:21:57 +02:00
xdavidwu 595f324f8b input-method: implement keyboard grabs 2020-07-08 11:21:57 +02:00
xdavidwu 61e2ebac90 virtual-keyboard: add wlr_input_device_get_virtual_keyboard 2020-07-08 11:21:57 +02:00
Simon Ser b6377b59ff backend/drm: check drm_surface_make_current return value
drm_connector_set_cursor wasn't checking the return value of the
drm_surface_make_current call. On failure, this results in a failed
assertion in wlr_renderer_begin (because no rendering context is
current).
2020-07-07 11:18:07 -06:00
Simon Ser cccca368c5
backend/drm: fix typo in drm_surface_make_current arg 2020-07-07 17:36:51 +02:00
Isaac Freund 92c85858a9 layer-shell: remove unused surface list
This was rendered useless in 5cde359.
2020-07-07 12:11:31 +02:00
j-n-f b61a98c417 examples: fix improper use of `free`
Closes #2303
2020-07-06 00:40:14 +02:00
Greg V b2bd536308 xdg-shell: check for existing role before setting xdg_popup_surface_role
Hopefully fixes #2056
2020-07-04 12:11:19 +02:00
John Chadwick 58bcec9d94 xwm: end transfers when the requestor is destroyed
This improves the failure cases when incremental transfers fail to
complete successfully for one reason or another.
2020-07-03 09:42:36 +02:00
Simon Ser f82a27f55a backend/drm: fix DPMS on legacy interface
This mirrors what the atomic code does in create_mode_blob.

Closes: https://github.com/swaywm/wlroots/issues/2312
2020-07-02 09:39:41 -06:00
Andri Yngvason a54ed85881 examples: screencopy-dmabuf: Fix y-inversion 2020-07-01 11:43:02 +02:00
Andri Yngvason 1d835f2035 screencopy: Use correct dmabuf to get y-inversion flag
Because wlr_renderer_blit_dmabuf() undoes y-inversion on the source
buffer, it is incorrect to pass the y-inversion flag of the source
buffer to the user.
2020-07-01 11:43:02 +02:00
Andri Yngvason e05a85327f render: gles2: Fix y-inversion in gles2_blit_dmabuf() 2020-07-01 11:43:02 +02:00
Scott Moreau b1a47245a1 xwm: Destroy xwm on hangup or error
If Xwayland is restarted, the ready handler assumes there is no xwm instance.
This means all of xwm was leaked on Xwayland restart. This caused compositors
to consume all cpu resources, where time is spent dispatching. Now we destroy
xwm if we get an event mask containing WL_EVENT_HANGUP or WL_EVENT_ERROR.
2020-06-30 21:21:25 +02:00
Scott Moreau 84d2f30faa xwayland: Don't discard ready signals
The xwayland ready signals are used to do initial setup like starting xwm.
Discarding the signals means that the handler functions will not be called
in the case that Xwayland is restarted and thus, xwm managed clients fail.

Fixes #2174."
2020-06-30 21:21:25 +02:00
Simon Ser c611a8f7e7 output: add backend docs 2020-06-30 08:03:58 -06:00
Isaac Freund 45c0877e34 layer-shell: upgrade to v3, implement destructor 2020-06-30 13:33:15 +02:00
Kenny Levinsen d2ca220fda wlr_drag: Destroy drag after releasing grabs
wlr_drag sets up keyboard, pointer and touch grabs, which block 'enter'
events (and thus focus changes). For the compositor to be able to update
focus (e.g. to focus the drop target) from the destroy handler, the
grabs must be released before the destroy event is signalled.
2020-06-30 11:11:52 +02:00
Kirill Chibisov 6c8f66ff59 xcursor: add xorg-x11 and cursors path to XCURSORPATH
This should match default XCURSORPATH, which is used by libwayland-cursor
and other xcursor loading libraries more closely.
2020-06-26 11:20:52 +02:00
Isaac Freund a7f48aab69 xdg-decoration: free old configure structs 2020-06-25 10:33:58 +02:00
Isaac Freund 86e20f48c6 xdg-shell: handle serial wrapping overflow 2020-06-25 10:33:58 +02:00
Isaac Freund b937c7b05e layer-shell: handle serial wrapping overflow 2020-06-25 10:33:58 +02:00
Simon Zeni 4a4da256dd render/gles2: use glGetAttribLocation instead of hardcoded indices 2020-06-24 20:01:19 +02:00
Simon Ser d5530b26d7 examples/pointer: fix wlr_renderer_end call order
Calling wlr_renderer_end after wlr_output_commit would make an
assertion fail.
2020-06-20 11:05:54 -06:00
Simon Ser c930160286 backend/noop: add missing rollback_render output impl
3c5dbfd97c ("output: make rollback_render mandatory") makes a no-op
output init fail without this function.
2020-06-19 13:07:24 -06:00
Simon Ser 155d57b01d output: fix dangling renderer context after wlr_output_preferred_read_format
attach_render was called without un-setting the current rendering
context afterwards.

Closes: https://github.com/swaywm/wlroots/issues/2164
2020-06-19 11:50:42 -06:00
Simon Ser 3c5dbfd97c output: make rollback_render mandatory
If the output backend provides attach_render, assert it also provides a
way to revert it via rollback_render.
2020-06-19 11:50:42 -06:00
Simon Ser bf93d2e67c output: rename impl->rollback to rollback_render
The output backend API is now mostly state-less thanks to the atomic
hooks (commit and test). There is one exception though: attach_render.
This function makes the rendering context current. However sometimes the
compositor might decide not to render after attach_render (e.g. when
there's nothing new to render to the back buffer). Thus
wlr_output_rollback has been introduced to revert the pending state.

Because the output backend API is mostly state-less, the only thing
wlr_output_impl.rollback needs to do is revert the current rendering
context. Rename the function to rollback_render to make this clear. Add
a check in the common wlr_output code to only call rollback_render when
attach_buffer has been previously called.

On the long term, we'll be able to remove attach_render and
rollback_render together.
2020-06-19 11:50:42 -06:00
Simon Ser 58df3eda9f render/egl: print error name
Allows for easier debugging.
2020-06-18 07:56:05 -06:00
Simon Ser d177abecae surface: ignore viewport src rect on NULL buffer
According to the viewporter protocol:

> If the wl_buffer is NULL, the surface has no content and therefore no size.
2020-06-18 07:55:33 -06:00
Rouven Czerwinski c18c419b56 surface: don't unset width and height in finalize
During surface finalization we may not have received a new buffer,
resetting width and height in this case is wrong since we display the
old buffer in this case.
2020-06-18 09:54:15 +02:00
Tudor Brindus c768309ab4 input/keyboard: send modifiers on first keyboard enter
Will fix Firefox bug
https://bugzilla.mozilla.org/show_bug.cgi?id=1643991.

Fixes swaywm/sway#5462.
2020-06-17 18:54:38 +02:00
Simon Ser 9e68ed2159 viewporter: new protocol implementation
Closes: https://github.com/swaywm/wlroots/issues/633
2020-06-17 09:10:54 -06:00
Simon Ser c618a76540 surface: introduce wlr_surface_get_buffer_source_box
This helper allows compositors to retrieve the buffer source box in
buffer-local coordinates.
2020-06-17 09:10:54 -06:00
Simon Ser eb22ae97b0 surface: add wlr_surface_state.viewport
This field contains the viewport source and destination parameters. It's
intended to be updated by a third-party protocol, for instance
viewporter.
2020-06-17 09:10:54 -06:00
Simon Ser 315bf08733 render: add wlr_render_subtexture_with_matrix
This renders only a subset of the texture, instead of the full texture.
2020-06-17 09:10:54 -06:00
Simon Ser 00ccb89288 util/region: add wlr_region_scale_xy 2020-06-17 09:10:54 -06:00
Simon Ser 9814213a91 box: add wlr_fbox
Same as wlr_box, but for floating-point numbers.
2020-06-17 09:10:54 -06:00
Simon Ser 5118189a2b render/gles2: use .x/.y instead of .s/.t
texcoord is a vector of coordinates, with the first member being the X
axis value and the second member being the Y axis value. It makes more
sense to use the accessors with the same names.
2020-06-17 09:10:54 -06:00
Rouven Czerwinski d3d1bac1c2 render: assert sane values for box functions
Width and height should always be > 0 for render functions which take a
wlr_box.

References https://github.com/swaywm/wlroots/issues/2281
2020-06-16 15:16:02 +02:00
Andri Yngvason cae533cad2 screencopy: Use correct constant for y-inversion 2020-06-13 15:39:49 +02:00
Andri Yngvason a6a5a19356 backend: drm: Fix dmabuf resource leak 2020-06-13 15:39:49 +02:00
Simon Ser 3c13527ead examples/fullscreen-shell: stop advertising linux-dmabuf unconditonally
Remove the wlr_linux_dmabuf_v1_create call. wlr_renderer_init_wl_display
will take care of creating the linux-dmabuf global if the OpenGL
implementation supports it.
2020-06-11 11:50:17 -06:00
Simon Ser 4615ce9099 examples/screencopy-dmabuf: call strncpy with maxlen - 1
The original code wasn't wrong since we were manually writing a null
byte anyway, but this makes GCC happy.

Closes: https://github.com/swaywm/wlroots/issues/2273
2020-06-11 07:34:55 -06:00
Simon Ser 7f9bbaaa17 render/egl: explicit client extension handling
Prior to this commit, wlr_egl_init seemed to assume the extension string
queried via EGL_NO_DISPLAY was a subset of the extension string queried
via an initialized display. This isn't correct.

EGL_EXT_client_extensions [1] defines two types of extensions: client
extensions and display extensions. The set of supported client and
display extensions are disjoint (ie. an extension is either a client or
a display extension, not both). Client extensions are queried via
EGL_NO_DISPLAY, display extensions are queried via an initialized
display.

Rename the variables to make this clear. Remove the misleading comment.
Log both client and display extensions.

[1]: https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_client_extensions.txt
2020-06-10 14:55:29 +02:00
Simon Ser 79e03c7d45 output: document wlr_output_export_dmabuf 2020-06-09 07:12:43 -06:00
Simon Ser a8a4a12c4b buffer: document wlr_buffer_get_dmabuf DMA-BUF lifetime 2020-06-09 07:12:43 -06:00
Andri Yngvason 78d6eed6b3 render: Fix blit_dmabuf() breakage due to API change 2020-06-08 21:37:32 +02:00
Andri Yngvason e0d4f75172 examples: Add screencopy-dmabuf example 2020-06-08 20:49:41 +02:00
Andri Yngvason ea83957ecc screencopy: Implement copying into linux-dmabuf 2020-06-08 20:49:41 +02:00
Andri Yngvason 909b0213b3 protocol: screencopy: Extend to report linux-dmabuf capability
A new version of the protocol is required so that the client can
discover which buffer types are supported by the compositor.
2020-06-08 20:49:41 +02:00
Andri Yngvason b64a8a7f98 render: Add wlr_renderer_blit_dmabuf() 2020-06-08 20:49:41 +02:00
Andri Yngvason 11b598fe33 render: egl: Add utility functions for saving/restoring context 2020-06-08 20:49:41 +02:00
Simon Ser e77c046cf9 backend/drm: fix stack overflow in dealloc_crtc
Call drm_crtc_commit directly instead of calling drm_connector_set_mode.
This restores the previous behaviour where conn_enable was called [1].

[1]: 0c7c562482/backend/drm/drm.c (L1093)

Closes: https://github.com/swaywm/wlroots/issues/2253
2020-06-08 10:13:00 -06:00
Simon Ser 9b85a8b43d contributing: add note about events that destroy objects 2020-06-08 10:12:38 -06:00
Simon Ser fd0a845cb4 contributing: resource destroy handlers need to cleanup pointers 2020-06-08 10:12:38 -06:00
Simon Ser a3ba82885c render: choose DMA-BUF texture target via eglQueryDmaBufModifiersEXT
EGL_EXT_image_dma_buf_import_modifiers tells us whether we should use
GL_TEXTURE_2D or GL_TEXTURE_EXTERNAL_OES. Using the right texture target
can fix some failures and/or improve performance on some drivers.

This does the same as a Weston commit [1].

[1]: https://gitlab.freedesktop.org/wayland/weston/commit/40c519a3e613

Closes: https://github.com/swaywm/wlroots/issues/2173
2020-06-06 08:59:38 +02:00
Tudor Brindus 363bf44a35 util/time: move `timespec_sub` to time utilities 2020-06-06 00:09:19 +02:00
Tudor Brindus c9c31f803e util/time: de-duplicate `timespec_to_msec` 2020-06-06 00:09:19 +02:00
Tudor Brindus dc13bb827d util: fix and move `get_current_time_msec` into a util file
This commit makes `get_current_time_msec` correctly return milliseconds
as opposed to microseconds. It also considers the value of `tv_sec`, so
we don't lose occasionally go back in time by one second. Finally, the
function is moved into `util/time.cc` so that it can be reused elsewhere
without having to consider these pitfalls.
2020-06-06 00:09:19 +02:00
Thomas Hebb dcae6f1431 Allow keyboard and pointer grabs to hook clear_focus()
This is necessary for some grabs, which currently have no way of knowing
when the pointer/keyboard focus has left a surface. For example, without
this, a drag-and-drop grab can erroneously drop into a window that the
cursor is no longer over.

This is the plumbing needed to properly fix swaywm/sway#5220. The
existing fix, swaywm/sway#5222, relies on every grab's `enter()` hook
allowing a `NULL` surface. This is not guaranteed by the API and, in
fact, is not the case for the xdg-shell popup grab and results in a
crash when the cursor leaves a surface and does not immediately enter
another one while a popup is open (#2161).

This fix also adds an assertion to wlr_seat_pointer_notify_enter() that
ensures it's never called with a `NULL` surface. This will make Sway
crash much more until it fixes its usage of the API, so we should land
this at the same time as a fix in Sway (which I haven't posted yet).
2020-06-05 17:20:26 +02:00
Thomas Hebb 8bf9f5bb8b wlr_seat: Clarify wording of non-grab-respecting function comments
I found the previous wording a bit confusing when I first read it.
Reword these comments to explicitly say that the grab-respecting
variants should be used in most cases.

This change has no functional effect.
2020-06-05 17:20:26 +02:00
Thomas Hebb e8c00e918a wlr_seat: Declare functions in the same order for pointer, keyboard, and touch
These three APIs are very similar to one another, but they all had
slightly different function orderings. For consistency, always declare
the non_`notify` functions first, then the `notify` functions, then
`{start,end,has}_grab`.

This change has no functional effect.
2020-06-05 17:20:26 +02:00
Simon Ser 8f28f5b2f8 backend/drm: fix black screens when enabling output
This patch fixes this failure:

    01:57:16.642 [ERROR] [backend/drm/drm.c:360] Failed to page-flip output 'eDP-1': a page-flip is already pending
    01:57:16.684 [ERROR] [backend/drm/drm.c:360] Failed to page-flip output 'eDP-1': a page-flip is already pending
    01:57:16.684 [ERROR] [backend/drm/drm.c:732] Failed to initialize renderer on connector 'eDP-1': initial page-flip failed
    01:57:16.684 [ERROR] [backend/drm/drm.c:805] Failed to initialize renderer for plane
    01:57:16.684 [sway/config/output.c:423] Failed to commit output eDP-1

References: https://github.com/swaywm/sway/issues/5101
2020-06-03 10:27:21 -06:00
Simon Ser e91417ea8d render: stop making EGL context current in wlr_egl_init
This leaves an EGL context current behind. wlr_gles2_renderer_create was
assuming the EGL context was already current because of this (because it
called a GL function right off the bat).
2020-06-02 14:23:24 -06:00
Simon Ser 019fe8bb7e backend/wayland: fix spurious eglSwapBuffers failures
This was introduced in [1]. However after reverting that PR I still
can't reproduce the bug the PR aimed to fix [2].

Since there's no good explanation why we would need to swap buffers
before resizing, let's just revert the PR.

[1]: https://github.com/swaywm/wlroots/pull/1486
[2]: https://github.com/swaywm/wlroots/issues/1371

Closes: https://github.com/swaywm/wlroots/issues/1768
2020-06-02 14:14:08 -06:00
Tudor Brindus 47a30957d1 backend/wayland: scale tablet tool position into [0, 1] range
Fixes #2233.
2020-06-01 22:36:46 +02:00
Simon Ser b03eebf7d4 backend/drm: always perform a CRTC commit in drm_connector_commit
When the mode, status or buffer hasn't changed, drm_connector_commit was
a no-op. Because of this individual changes to the gamma LUT or adaptive
sync status were ignored (if committed without a buffer).

Instead, perform a commit to apply the changes.
2020-06-01 11:45:11 -06:00
Simon Ser 1a2e82e327 backend/drm: drop extra wlr_output_update_enabled call
drm_connector_set_mode already takes care of keeping this up-to-date.
2020-06-01 11:45:11 -06:00
Simon Ser 80467f6ae8 backend/drm: commit/rollback FBs in drm_crtc_commit
We need to perform the FB bookkeeping on all commits, not just on
page-flips.
2020-06-01 11:45:11 -06:00
Simon Ser 70f3a0bb27 backend/drm: make adaptive_sync atomic
Stop using drmModeObjectSetProperty, set the property in the crtc_commit
function instead.
2020-06-01 11:45:11 -06:00
Simon Ser 42126c2022 backend/drm: rollback pending CRTC state on test commit
A test commit doesn't apply the pending state.

The CRTC state will be populated again if the compositor decides to
perform a regular commit afterwards.
2020-06-01 11:45:11 -06:00
Andri Yngvason 034384f5aa export-dmabuf: Keep frame->output = NULL until frame is ready
This fixes a crash that happens when a client requests a frame on a
backend that does not implement export_dmabuf.

An assertion fails with the message:
sway: types/wlr_output.c:777: wlr_output_lock_attach_render: Assertion
`output->attach_render_locks > 0' failed.
2020-06-01 12:50:26 +02:00
Brian Ashworth 32148808ad wlr_keyboard_group: introduce enter and leave
This introduces the enter and leave events for wlr_keyboard_group.

The enter event is emitted when a keyboard is added to the group while a
key is pressed that is not pressed by any other keyboard in the group.
The data is a wl_array of the pressed key codes unique to the keyboard
that should now be considered pressed.

Similarly the leave event is emitted when a keyboard is removed from the
group while at least one key is pressed that is not pressed by any other
keyboard in the group. The data is a wl_array of the pressed key codes
unique to the keyboard that should now be considered released.

The purpose of these events are to allow the compositor to update its
state to avoid corruption. Additionally, for the leave event, the
focused surface may have been notified of a key press for some or all of
the key codes and needs to be notified of a key release to avoid state
corruption.

These were previously emitted as normal key events, but they are not
normal key events. There is no actual key press or release associated
with the events. It's purely for state keeping purposes. Emitting them
as separate events allows the compositor to handle them differently.
Since these are purely for state keeping purposes and are not associated
with an actual key being pressed or released, bindings should not be
triggered as a result of these events.
2020-05-31 17:28:18 -04:00
Kalyan Sriram 8ab4d91380 Don't send redundant capability updates 2020-05-29 08:43:32 +02:00
Simon Ser 1a23c1425f Add comments for missing tablet tool entries
And stop using default cases, so that we know which parts of the code
need an update when adding a new enum entry.

Closes: https://github.com/swaywm/wlroots/issues/2208
2020-05-28 15:13:05 +02:00
Kenny Levinsen 5e0ef70cc0 seat: Create inert objects for missing capabilities
We should throw a protocol error if the relevant capability has never
existed when get_(pointer|keyboard|touch) is called. Otherwise, it
should succeed, even if the capability is not currently present.

This follows the spec, and avoids possible races with the client when
capabilities are lost.

Closes: https://github.com/swaywm/wlroots/issues/2227
2020-05-28 09:53:50 +02:00
Simon Ser d66b9966e9 backend/drm: fix missing crtc->cursor NULL check
Fixes: cdb6fdbc6c ("backend/drm: remove missing cursor plane workaround")
2020-05-27 21:01:20 +02:00
Simon Ser e7a8ea84c3 backend/drm: don't set cursor if off-screen
Closes: https://github.com/swaywm/wlroots/issues/2216
2020-05-27 21:01:20 +02:00
Tudor Brindus 5947160630 xwayland: add error-checking to `server_start_lazy`
This prevents a very unlikely crash in `xwayland_socket_connected`.

Refs #2163.
2020-05-27 18:39:26 +02:00
Simon Ser 15d8f1806e backend/drm: introduce pending and current CRTC state
Previously, we only had the pending state (crtc->pending, crtc->mode and
crtc->active). This causes issues when a commit fails: the pending state
is left as-is, and the next commit may read stale data from it.

This will also cause issues when implementing test-only commits: we need
to rollback the pending CRTC state after a test-only commit.

Introduce separate pending and current CRTC states. Properly update the
current state after a commit.
2020-05-26 23:34:37 +02:00
Simon Ser d6cc718472 backend/drm: nuke retry_pageflip
retry_pageflip is now dead code, since drm_connector_start_renderer
isn't called anymore. It was previously called when enabling an output.
The name "retry_pageflip" was a little confusing because the function
retried a modeset and the timer wasn't set up while performing a simple
page-flip.

Let's just remove this altogether for now. We can discuss whether it's
worth it to bring it back. Should we only do it on failed page-flips?
Should we only do it on EBUSY?
2020-05-26 23:34:37 +02:00
Simon Ser 61095f4a12 backend/drm: make drm_connector_set_mode take a wlr_drm_connector
Since this is an internal DRM backend function, there's no reason we
need to take a generic wlr_output.
2020-05-26 23:34:37 +02:00
Simon Ser 445750aa9a backend/drm: remove enable_drm_connector
Merge enable_drm_connector into drm_connector_set_mode. This allows us
to de-duplicate logic since enabling an output performs a modeset.
2020-05-26 23:34:37 +02:00
Simon Ser c02e9c2bb1 backend/drm: remove drm_connector_set_custom_mode
Replace it with a function that returns the pending mode.
2020-05-26 23:34:37 +02:00
Simon Ser 21c6cc5e4c backend/drm: refuse to enable an output without a mode 2020-05-26 20:10:55 +02:00
Simon Ser 47e5212823 backend/drm: rollback atomic blobs
If the atomic commit fails or is test-only, rollback
wlr_drm_crtc.{mode_id,gamma_lut} blob IDs to their previous value. This
prevents the next commits from failing or applying test-only state.
2020-05-26 20:01:38 +02:00
Tudor Brindus 0758a4fc9d xwayland: send focus change event unconditionally
This fixes issues with (at least) dialogs in Jetbrains IDEs becoming
unclickable if they ever lost focus (ref. swaywm/sway#5373). Prior to
this change, since `xwm->focus_surface` would be set prior to
`xwm_surface_activate` being called, the latter would short-circuit
immediately and not notify the application of the focus change.
2020-05-25 21:39:01 +02:00
Tudor Brindus 5c942bd597 util/log: use bright black rather than black for WLR_DEBUG
On some terminals under default settings, black is truly rendered as
`#000`, making it unreadable when the background is also black.

Refs swaywm/sway#5141.
2020-05-24 14:53:40 +02:00
Simon Ser af2f69e6c1 render/egl: unset current context after swapping buffers
After swapping buffers, it doesn't make sense to perform more rendering
operations. Unset the context to reflect this.

This commit makes it so the context is always only current between
wlr_egl_make_current and wlr_egl_swap_buffers.

This is an alternative to [1].

[1]: https://github.com/swaywm/wlroots/pull/2212
2020-05-20 17:39:34 +02:00
Ilia Bozhinov 72f28ed0b3 examples: make output-power-management oneshot by default 2020-05-20 17:21:59 +02:00
Simon Ser 2176c63856 xwayland: add option to disable WM 2020-05-19 22:07:47 +02:00
Simon Ser 27609ba0d9 xwayland: split server
Split the server part of wlr_xwayland into wlr_xwayland_server. This
allows compositors to implement their own XWM when wlroots' isn't a good
fit.
2020-05-19 22:07:47 +02:00
Simon Ser d28a7da95d backend/drm: add missing wlr_egl_unset_current 2020-05-19 21:32:38 +02:00
Simon Ser cfed5766b7 backend/drm: fix current EGL context on multi-GPU
get_tex_for_bo changes the current EGL context. Rendering operations
must immediately follow drm_surface_make_current.

Closes: https://github.com/swaywm/wlroots/issues/2209
2020-05-19 21:32:38 +02:00
Simon Ser 1edc42157b render/egl: introduce wlr_egl_unset_current
This function can be called after wlr_egl_make_current to cleanup the
EGL context. This avoids having lingering EGL contexts that make things
work by chance.

Closes: https://github.com/swaywm/wlroots/issues/2197
2020-05-19 14:56:20 +02:00
Simon Ser 781ed1ff02 Fix -Wreturn-type warnings
When calling assert(0) instead of returning a value, -Wreturn-type
warnings are triggered because assertions can be disabled. Replace these
assertions with abort().
2020-05-19 14:54:02 +02:00
Isaac Freund 666498db01 Make wlr_xcursor_manager_load() return a bool
This is currently inconsistent with the rest of the library and a bit of
a footgun for new compositors. However, this breaks the API in a very
unfortunate way for existing compositors.
2020-05-18 16:48:44 +02:00
Simon Ser 2988ebb6f3 backend/drm: fix atomic commits when the GAMMA_LUT prop is missing
We already have the logic to fallback to the legacy interface above. We
just need to avoid calling atomic_add with a zero prop ID.

Closes: https://github.com/swaywm/wlroots/issues/2187
2020-05-14 23:55:43 +00:00
Simon Ser 347bdb6d9a output: make wlr_output_set_gamma atomic
wlr_output_set_gamma is now double-buffered and applies the gamma LUT on
the next output commit.
2020-05-14 20:09:28 +02:00
Tudor Brindus 7693fdb8a7 tablet: expose wlr_tablet_tool_v2_has_implicit_grab function
This is necessary so that sway can determine when to start emulating
pointer events -- it shouldn't start doing so during an implicit grab,
even if the pen is over a surface that doesn't bind tablet input.

Refs swaywm/sway#5302.
2020-05-14 18:45:19 +02:00
Tudor Brindus 74c0d03f00 tablet: pass motion events to implicitly grabbed surface
Refs swaywm/sway#5302.
2020-05-14 18:45:19 +02:00
Simon Ser 42e485dcc3 backend/drm: fix segfault in drm_crtc_page_flip
When no cursor plane is available, drm_crtc_page_flip would segfault.
2020-05-14 10:20:37 +02:00
Tudor Brindus 8b18d389b3 input/pointer: notify compositor when constraint region changes
This allows a compositor to know when warping back into the region is
appropriate.

Refs swaywm/sway#5268.
2020-05-13 20:38:12 +02:00
Simon Ser 51bbf31742 backend/drm: print error in set_plane_props
This makes it easier to understand which plane failed.
2020-05-12 15:12:22 +02:00
Simon Ser 9412d34e2d backend/drm: disable cursor in dealloc_crtc
dealloc_crtc was destroying GBM surfaces, but the cursor_enabled flag
was left as-is. When re-enabling the output, atomic_crtc_pageflip would
try enabling the cursor plane, but would fail because no framebuffer is
available.

Closes: https://github.com/swaywm/wlroots/issues/2150
2020-05-12 15:12:22 +02:00
Simon Ser da4df82532 backend/drm: fix combined modeset+enable commits
When an output is enabled and modeset at the same time,
drm_connector_commit would first try to modeset then try to commit. This
won't work because both will trigger a page-flip. KMS will reject that.

Change the logic to only enable an output if no modeset has been
requested. The logic in wlr_output already checks that the user isn't
doing a modeset and disabling the output at the same time.
2020-05-12 15:12:06 +02:00
Simon Ser cdb6fdbc6c backend/drm: remove missing cursor plane workaround
We have a workaround for legacy drivers that support drmModeSetCursor
without exposing a cursor plane. It doesn't work anymore now that we've
moved to a more atomic interface.

Let's just remove this workaround and fallback to software cursors.

Closes: https://github.com/swaywm/wlroots/issues/2166
2020-05-12 08:57:13 +02:00
Julien Olivain c2288a7b88
render/egl: include EGL/eglmesaext.h only if present
This patch will make the EGL renderer work on any EGL/GLESv2 driver
providing the EGL_WL_bind_wayland_display extensions.

Mesa used to declare provisional EGL_WL_bind_wayland_display directly
in <EGL/eglext.h>. Then, all unofficial extensions were moved to
<EGL/eglmesaext.h>, to have a cleaner implementation. See:
ab7bb10a2a

The extension was then approved at Khronos Group, and reached the
official <EGL/eglext.h>. See:
https://www.khronos.org/registry/EGL/extensions/WL/EGL_WL_bind_wayland_display.txt
aa9b63f3ab

In order to make sure the renderer will work on any version of any
implementation providing the extension, only include the mesa-specific
header if it's present.

Signed-off-by: Julien Olivain <juju@cotds.org>
2020-05-11 08:58:30 +02:00
JonnyMako 1139234117
backend/drm: fix missing cursor on external monitors with multi-GPU setup and nouveau
We need to make the multi-GPU surface current before the glFinish call.

Closes: https://github.com/swaywm/sway/issues/5319
2020-05-11 08:51:41 +02:00
Simon Ser f72686c0b6 backend/drm: fix crash in session_signal
conn->crtc is NULL in case the output is disabled.

However, the DRM backend will set the GAMMA_LUT property anyway. On each
commit the whole state is sent to KMS. Adding WLR_DRM_CRTC_GAMMA_LUT to
the pending state would just make the backend re-create the blob
containing the gamma LUT.
2020-05-10 12:07:05 +02:00
Simon Ser 2ca3bdc35e backend/drm: simplify atomic commit logic
We don't need a per-CRTC atomic request anymore. Let's make the request
per-commit so that it's easier to debug.

This is also groundwork for supporting wlr_output_test properly.
2020-05-10 09:21:04 +02:00
Simon Ser 06d5aa5780 backend/drm: GAMMA_LUT_SIZE isn't atomic
GAMMA_LUT_SIZE isn't an atomic property. It can be used with the legacy
interface too. So we can unify both codepaths and remove
wlr_drm_interface.crtc_get_gamma_size.

It's no guaranteed to exist though, so we still need to keep the
fallback.
2020-05-10 09:20:46 +02:00
Simon Ser da63d11d34 backend/drm: remove crtc_set_cursor from interface 2020-05-09 16:42:25 +02:00
Simon Ser f8e02db4bc backend/drm: remove conn_enable from interface
Use crtc_commit instead.
2020-05-09 16:42:25 +02:00
Simon Ser c608fc89d8 backend/drm: rename crtc_pageflip to crtc_commit
Also add a flags argument.

The commit function will also be used for disabling the CRTC.
2020-05-09 16:42:25 +02:00
Simon Ser 70883fd10b backend/drm: apply gamma LUT on page-flip 2020-05-09 16:42:25 +02:00
Simon Ser 69b2279092 backend/drm: remove mode argument to crtc_pageflip
Add a new wlr_drm_crtc.pending bitfield which keeps track of pending
output changes. More fields will be added in the future (e.g. active,
gamma).
2020-05-09 16:42:25 +02:00
Simon Ser 7a149fe5ba backend/drm: remove unused fields 2020-05-09 16:42:25 +02:00
Simon Ser c9d6b18eef backend/drm: remove wlr_drm_interface.crtc_move_cursor
Instead, make the legacy backend call drmModeMoveCursor on page-flip.
2020-05-09 16:42:25 +02:00
Tudor Brindus 064f64dbf7 input/keyboard: expose keymap matching helper
sway needs this logic too, and currently ships a version that has fallen
behind in terms of bugfixes (b1a63bc).
2020-05-07 23:10:03 -04:00
Tudor Brindus 6357e166f9 backend/wayland: emit tablet tool axis events to the axis handler 2020-05-04 21:53:52 +02:00
Tudor Brindus 16af1972d6 wlr/types: use bitshifts for tablet axes enum 2020-05-04 21:52:33 +02:00
Simon Ser 46c83cbf3d backend/headless: handle renderer destroy
When the headless backend uses an already-existing renderer, it doesn't
have ownership over the renderer. When the renderer is destroyed, the
headless backend needs to destroy itself.
2020-05-02 19:36:03 +02:00
Simon Ser 7720ce7827 backend/multi: handle backends depending on each other properly
wl_list_for_each_safe only allows the current list item to be removed.
If a backend destroys itself when another backend is destroyed, this
blows up.
2020-05-02 19:36:03 +02:00
David96 e3343cf7d1 Add wlr_surface_accepts_touch 2020-05-02 18:25:47 +02:00
Isaac Freund 11e94c406b layer shell: only send unmap on close if mapped 2020-05-02 18:25:09 +02:00
Simon Ser 61d6408fdb examples/dmabuf-capture: use getopt
This makes it a little bit less annoying to provide the right arguments.
All options have reasonable defaults.
2020-05-01 16:57:08 +02:00
Simon Ser 90ede7f838 backend/drm: make page-flip error scarier
The logs don't currently display the importance of a line. It's easy to
read "skipping page-flip" as a debug message instead of an error
message.

Change the error message to make it more explicit.

References: https://github.com/swaywm/wlroots/pull/2147
2020-05-01 16:48:42 +02:00
Simon Ser 904312a0b2 util/log: write log importance
When colors aren't used, write the log importance to stderr. This makes
it easier to grep for errors and avoids mistaking error messages for
debug messages.
2020-05-01 16:45:57 +02:00
Will Daly 044a9f28d6 Fix error when reconnecting external display
Reconnecting an external display fails on initial
page-flip.  This occurs when the pageflip_pending flag
has been set on the connector at the time the monitor
is removed.  Once the connector's CRTC has been deallocated,
page_flip_handler cannot find the connector by its crtc_id,
so pageflip_pending will remain set.  This causes
initialization to fail when the monitor is reconnected
with the error messages:

    Skipping pageflip on output 'X'
    Failed to initialize renderer on connector 'X': initial page-flip failed

To fix this problem, clear the pageflip_pending flag
when cleaning up the connector.
2020-05-01 16:01:30 +02:00
Simon Ser 22d345d982
Remove .swp file added by mistake
Fixes: d698334620 ("input/tablet: clear focused surface on surface destroy")
2020-04-30 13:19:46 +02:00
Tudor Brindus d698334620 input/tablet: clear focused surface on surface destroy
Otherwise, we can end up left with a dangling pointer to a
previously-focused, now-destroyed surface.

Fixes swaywm/sway#5264.
2020-04-30 12:08:42 +02:00
Scott Anderson 906c0766df Remove libcap support
This is simply a false sense of security, and is worse than just using
setuid. CAP_SYS_ADMIN is an extremely serious capability that is
effectively as powerful as root.

It also required users to be in the input group, which allows any
process to keylog the entire system.
2020-04-29 10:39:09 +02:00
Simon Ser 98d949718c backend/drm: strip alpha channel if necessary
Some primary planes don't support ARGB8888, they only support XRGB8888
(for instance on older Intel hardware). The DRM backend would fail to
initialize.

When that's the case, try to allocate buffers without an alpha channel.
2020-04-29 08:32:06 +02:00
Simon Ser 05803511db render/texture: make write_pixels optional 2020-04-28 21:45:14 +02:00
Simon Ser 06f4c3945d render/texture: add width and height fields
Instead of requiring compositors to call wlr_texture_get_size each time
they want to access the texture's size, expose this information as
wlr_texture fields.
2020-04-28 21:45:14 +02:00
Greg Depoire--Ferrer 21397e2b4a tinywl: Fix wrong anchor point while resizing a window
Previously, when dragging the left border of a window with the mouse in tinywl,
there was a bug where it snap the top level surface's geometry X coordinate
directly to the position of the mouse, as if you started the resize right on the
border. This also affected the other (right, top and bottom) borders.

I think that the previous resize code was hard to understand. Honestly I
have not spent a lot of time trying to understand why it didn't work and
I wrote another resize algorithm instead: now, instead of working directly
with widths and heights which are complicated, we work with the borders (left,
right, top, bottom). This is easier to understand IMO.

Note: I originally fixed this [in the waybox compositor](https://github.com/wizbright/waybox/pull/23) but
then I realized that the code was taken from tinywl and that it had the same
issues so I copied my fix for tinywl.
2020-04-28 21:44:27 +02:00
Scott Anderson 321537ee92 backend/drm: don't allow legacy to use direct scanout 2020-04-28 09:54:52 +02:00
Scott Anderson 52281cb8ba backend/drm: move atomic cursor code into pageflip code
It makes sense to construct as much atomic state as possible in the same
place, so it doesn't get lost if we "reset" it.
2020-04-28 09:54:52 +02:00
Scott Anderson be90062c51 backend/drm: don't have fallback atomic commits
This is just doing atomic incorrectly.
2020-04-28 09:54:52 +02:00
Scott Anderson 8da9d9679e backend/drm: introduce wlr_drm_fb
This is a type which manages gbm_surfaces and imported dmabufs in the
same place, and makes the lifetime management between the two shared. It
should lead to easier to understand code, and fewer special cases.

This also contains a fair bit of refactoring to start using this new
type.

Co-authored-by: Simon Ser <contact@emersion.fr>
2020-04-28 09:54:52 +02:00
Isaac Freund fa5d709fc3 tinywl: handle request set selection 2020-04-28 09:19:22 +02:00
Isaac Freund 9d650a7c1a tinywl: remove unused variables 2020-04-28 09:19:22 +02:00
Tudor Brindus ad28f57533 input/tablet: fix up updated axes after rotation
In the case that only one axis received an event, rotating the input can
cause the change to actually happen on the other axis, as far as clients
are concerned.

This commit updates the axes flags to be consistent post-rotation.

Fixes swaywm/sway#4776.
2020-04-27 18:34:09 +02:00
Andri Yngvason e51d507799 export-dmabuf: Schedule output frame on request
A client requesting frames in the ready callback may miss frames that
happen while it is waiting to receive the event or sending the request.
If that happens, the client will have an outdated frame for an
indefinite period. A new frame might not be scheduled for a very long
time.

With this change, clients will receive new frames immediately upon
request.
2020-04-27 15:40:03 +02:00
Tudor Brindus 39af3535c3 input/tablet: populate tool tip event position
This commit doesn't fix any issue that I'm aware of, since sway
incidentally does not use these fields. Still, they exist, so they
should probably be filled in to prevent fun surprises in the future.
2020-04-27 10:20:44 +02:00
Kalyan Sriram 326c8bc818
tinywl: fix geo_box bug in cursor resizing
While trying out the tinywl code, I found that the resize mode was behaving
weirdly ... so I looked into code. Turns out the `begin_interactive` method
stores the cursor position plus the geo_box position; however,
`process_cursor_resize` wasn't taking this into account, causing windows to
jump down in size unexpectedly when resized and lose alignment with the cursor.
To fix this, I simply added a member to the `tinywl_server` struct that stores
the geo_box when the mouse enters grab mode, and I referenced that data in the
resize method. I considered polling for this data every time instead of storing
it in the server struct, but 1) since changes in this value are not relevant
and 2) it could potentially decrease performance (I don't know enough about
wlroots to know how much) I decided to just store it. I can change this if
desired.
2020-04-23 10:00:06 +02:00
Simon Ser 6129a6f93e backend/headless: add wlr_headless_backend_create_with_renderer
This allows one to create a headless backend with an existing renderer.
2020-04-22 22:40:54 +02:00
Simon Ser 40513f1a0e backend/headless: use FBOs instead of pbuffers 2020-04-22 22:40:54 +02:00
Simon Ser 5dc3a9c754 render/gles2: add wlr_gles2_renderer_check_ext 2020-04-22 22:40:54 +02:00
Simon Ser 01d4506253 render/drm_format_set: disallow DRM_FORMAT_INVALID
It doesn't make sense to add DRM_FORMAT_INVALID to a format set. Adding
an assertion allows us to safely query the format set with
DRM_FORMAT_INVALID. See [1].

[1]: https://github.com/swaywm/wlroots/pull/2021#discussion_r385839668
2020-04-22 22:14:44 +02:00
Simon Ser 455a9bd0ef output_layout: improve docs 2020-04-22 22:14:29 +02:00
Simon Ser f81aa6a172 build: use summary instead of message 2020-04-21 15:33:53 +02:00
Simon Ser ab4f642153 build: use dicts instead of get_variable
Closes: https://github.com/swaywm/wlroots/issues/1963
2020-04-21 15:26:45 +02:00
Simon Ser 0b882475ad build: use meson.override_dependency
When built as a subproject, this removes the need for the parent project
to know about the dependency variable name.

This requires Meson 0.54.0.
2020-04-21 14:56:05 +02:00
Tudor Brindus f0d818f36e backend/libinput: correctly populate x/y fields on tablet proximity in
Otherwise, the cursor will briefly jump to (0, 0). This is particularly
noticeable in the referenced issue (the game osu!).

Refs swaywm/sway#3633
2020-04-21 09:22:26 +02:00
Isaac Freund 85e299e6c5 Document the events of wlr_layer_surface 2020-04-17 15:44:07 +02:00
Kenny Levinsen 904c37845d logind: Close fd before releasing device
This speeds up shutdown significantly, and is in line with how Weston
does it.
2020-04-15 21:36:19 +02:00
Tudor Roman ffcf25cc25
seat: add selection event docs 2020-04-15 17:04:40 +02:00
Isaac Freund 25d0d1be3a tinywl: remove redundant create output global call 2020-04-15 12:40:20 +02:00
Simon Ser ab4dc1636c backend/multi: add backends at end of list
This allows wlr_multi_for_each_backend to iterate on the backends in the
order where they have been added.
2020-04-14 16:05:58 +02:00
r-c-f f679895c77
seat: add check for NULL keyboard
Check for a NULL keyboard_state.keyboard value in
seat_client_create_keyboard() before trying to use it, as is done in
other functions like seat_client_send_repeat_info(). Prevents a segfault
in certain situations on keyboard removal, as seen in the sway issue.

Closes: https://github.com/swaywm/sway/issues/5205
Closes: https://github.com/swaywm/wlroots/issues/2073
2020-04-14 12:12:48 +02:00
Simon Ser 0281b58d2f output: fix maybe-uninitialized warning
GCC is complaining about a maybe-uninitialized variable when doing a
release build. Even if that can't actually happen because all enum
values are handled, add an abort call to silence the warning.
2020-04-10 17:05:00 +02:00
Simon Ser 83c1ba7783 backend/wayland: check scan-out buffer is compatible in output_test
If the buffer doesn't have a supported format/modifier, make the test
fail.
2020-04-10 15:52:20 +02:00
Simon Ser 50ade3671f output: check for buffer size compatibility in common code
Instead of checking for buffer size compatibility in each backend,
centralize the check in wlr_output itself.
2020-04-10 15:52:20 +02:00
Simon Ser 5f092c55d1 output: fix blurred hw cursors with fractional scaling
The scaling factor was being implicitly cast to an int.

Closes: https://github.com/swaywm/sway/issues/4927
2020-04-10 15:10:12 +02:00
Simon Ser d10f8a98ec render: only expose linux-dmabuf if EGL extension is supported
Only expose linux-dmabuf extension if EGL_EXT_image_dmabuf_import_ext is
supported.

Closes: https://github.com/swaywm/wlroots/issues/2076
2020-04-09 00:16:03 +00:00
Simon Ser 4a1015faff render/gles2: only call wlr_egl_bind_display if supported
This allows us to hard-fail if the extension is advertised but we fail
to enable it.
2020-04-09 00:16:03 +00:00
Simon Ser 9acca4fc73 backend: set EGL_RENDERABLE_TYPE and EGL_SURFACE_TYPE
Ensure these are set to the correct value.
2020-04-09 00:15:25 +00:00
Simon Ser 6787ff521b render/egl: make config attribs const
wlr_egl_init is not allowed to mutate these attribs.
2020-04-09 00:15:25 +00:00
Simon Ser 507d9bc19e Add wlr_output_impl.rollback
Most of the pending output state is not forwarded to the backend prior
to an output commit. For instance, wlr_output_set_mode just stashes the
mode without calling any wlr_output_impl function.
wlr_output_impl.commit is responsible for applying the pending mode.

However, there are exceptions to this rule. The first one is
wlr_output_attach_render. It won't go away before renderer v6 is
complete, because it needs to set the current EGL surface.

The second one is wlr_output_attach_buffer.
wlr_output_impl.attach_buffer is removed in [1].

When wlr_output_rollback is called, all pending state is supposed to be
cleared. This works for all the state except the two exceptions
mentionned above. To fix this, introduce wlr_output_impl.rollback.

Right now, the backend resets the current EGL surface. This prevents GL
commands from affecting the output after wlr_output_rollback.

This patch is required for FBO-based outputs to work properly. The
compositor might be using FBOs for its own purposes [2], having leftover
FBO state can have bad consequences.

[1]: https://github.com/swaywm/wlroots/pull/2097
[2]: https://github.com/swaywm/wlroots/pull/2063#issuecomment-597614312
2020-04-08 17:33:00 +02:00
Simon Ser d3bd5f2a7b backend: reset EGL surface after buffer swap
This prevents GL commands to affect a previously current EGL surface
after a buffer swap.
2020-04-08 17:33:00 +02:00
Simon Ser 6977f3a843 output: check buffer in wlr_output_test
Check that buffer can be scanned out in wlr_output_test instead of
wlr_output_attach_buffer. This allows the backend to have access to the
whole pending state when performing the check.

This brings the wlr_output API more in line with the KMS API.

This removes the need for wlr_output_attach_buffer to return a value,
and for wlr_output_impl.attach_buffer.
2020-04-08 16:31:21 +02:00
Simon Ser e041158988 output: introduce wlr_output_test 2020-04-08 16:31:21 +02:00
Simon Ser 1fa9e0203b buffer: add width and height 2020-04-02 15:03:43 +02:00
Simon Ser 6595db6409 buffer: add a release event
Consumers call wlr_buffer_lock. Once all consumers are done with the
buffer, only the producer should have a reference to the buffer. In this
case, we can release the buffer (and let the producer re-use it).
2020-04-02 15:03:43 +02:00
Simon Ser 1674ca725c buffer: add destroy event 2020-04-02 15:03:43 +02:00
Simon Ser 7516a98167 Gracefully handle inert wl_output resources
Closes: https://github.com/swaywm/wlroots/issues/2088
2020-03-29 20:57:28 +02:00
Rabit 13db99b0f8 Prevent memory leak in copypaste of the screencopy example
If someone want to use screencopy as is processing multiple screenshots - it could be hard to find this issue with shm.
2020-03-25 20:36:12 +01:00
Scott Moreau 30308e35fa build: Add 'auto' to logind-provider combo option
The logind provider defaulted to systemd and in order to use elogind,
-Dlogin-provider=elogind was required. This adds 'auto' as a choice
for the login-provider option and sets it as default. Using 'auto',
the build will check for systemd first and if it's not found, try
to find and use elogind automatically.
2020-03-24 14:11:39 +01:00
Isaac Freund 8707a9b7ec Return false on wlr_keyboard_set_keymap() failure
This allows users of the library to handle or ignore the error as they
see fit.
2020-03-24 00:22:50 +01:00
Isaac Freund c682d97841 Return failure of wlr_renderer_init_wl_display()
This makes it easier for the user of this library to properly handle
failure of this function.

The signature of wlr_renderer_impl.init_wl_display was also modified to
allow for proper error propagation.
2020-03-23 15:19:16 +01:00
Scott Anderson 34303e1b47 wlr_surface: Post error if multiple role objects created
This fixes an assertion failure if a client tries to do this, e.g. by
creating multiple toplevel objects for the same surface. If the same
role data is set multiple times, this does not cause an error, which is
how cursors use this interface.
2020-03-21 13:58:27 +01:00
Simon Ser b614ded3fc backend/wayland: close keymap FD
We don't actually need the keymap. We need to close the FD or we will
run out of FDs.
2020-03-17 13:37:53 +01:00
Filip Sandborg 5ee52a3ab9
Fix uint32 overflow in fill_empty_gamma_table on Icelake platform
Closes: https://github.com/swaywm/sway/issues/4826
2020-03-15 18:41:12 +01:00
Isaac Freund 1282c3b12f Send pointer enter/leave on capability change
This is more correct according to the protocol and fixes issues with
clients that wait for an enter event before processing pointer events.
2020-03-14 00:09:32 +01:00
Isaac Freund 7c309ba4d3 Properly popluate keys array for enter on creation
This corrects an oversight made in 3f617631cb
2020-03-13 23:24:56 +01:00
Isaac Freund 3f617631cb Send keyboard enter/leave on capability change
This is more correct according to the protocol and fixes issues with
clients that wait for an enter event before processing key events
2020-03-13 22:19:51 +01:00
Simon Ser e0bbafc253 output: replace wlr_output.damage with a damage event
This patch disambiguates the needs_frame event by uncoupling it from
damage. A new separate damage event is emitted when the backend damages
the output (this happens e.g. VT is changed or software cursors are
used). The event specifies the damaged region.

The wlr_output.damage field is removed. wlr_output is no longer
responsible for tracking its own damage, this is wlr_output_damage's
job.

This is a breaking change, but wlr_output_damage users shouldn't need an
update.

Bugs fixed:

- Screen flashes on VT switch
- Cursor damage issues on the X11 and headless backends

Closes: https://github.com/swaywm/sway/issues/5086
2020-03-12 22:47:59 +01:00
Simon Ser 07737e85cc output: add comment about needs_frame in wlr_output_schedule_frame
Add a comment to not forget why this call is necessary.

References: https://github.com/swaywm/wlroots/pull/2053
2020-03-12 15:01:09 +01:00
Simon Ser 41f9916ae5 buffer: remove unused wlr_client_buffer fields
Forgot to remove these, they are superseded by fields in wlr_buffer. Some
functions were still using them.

Fixes: 8afc1ed68c ("Introduce wlr_client_buffer")
Closes: https://github.com/swaywm/sway/issues/5087
2020-03-09 19:26:12 +01:00
Simon Ser a71649dde9 render/gles2: remove duplicated format list 2020-03-09 15:26:28 +01:00
Simon Ser e7f8cc6801 util/log: improve time prefix
Log milliseconds. This is useful when debugging a rendering loop.

Print the time relative to the compositor start-up.
2020-03-07 00:25:01 +01:00
Simon Ser 7bce056f1d output: don't send a needs_frame event if already sent 2020-03-06 21:35:00 +01:00
Simon Ser a4c7c6db09 output: make wlr_output_schedule_frame set output->needs_frame
This way, wlr_output_schedule_frame will always be followed by a
wlr_output_commit. This forces the compositor to render an extra
frame before stopping the rendering loop.

To test, run wleird's frame-callback [1], make sure it's the only
visible client on the output and check the interval between frame
events is the output's refresh period instead of zero.

[1]: https://github.com/emersion/wleird/blob/master/frame-callback.c

Closes: https://github.com/swaywm/wlroots/issues/2051
2020-03-06 21:35:00 +01:00
Simon Ser 4bb391c896 xwayland: remove underscore prefix from atom names
Previously, some atoms had a leading underscore, others didn't. Be more
consistent and never use a leading underscore (symbols with a leading
underscore followed by an upper-case letter are reserved).
2020-03-06 21:34:44 +01:00
Simon Ser 175af4f74f xwayland: remove duplicate _NET_WM_NAME entry 2020-03-06 21:34:44 +01:00
Simon Ser 68a69ee079 xwayland: use explicit indexes when initializing atom_map
It's very easy to break the mapping between the atom_name enum and the
atom_map array. Use explicit indexes to prevent issues.
2020-03-06 21:34:44 +01:00
Simon Ser 52c67284e2 backend/drm: add support for adaptive_sync_enabled
The vrr_capable and VRR_ENABLED properties are used.
2020-03-06 21:32:58 +01:00
Simon Ser 9be1af3afb backend/x11: add support for adaptive_sync_enabled
This sets the _VARIABLE_REFRESH window property [1].

[1]: 0616b7ac90/src/vulkan/wsi/wsi_common_x11.c (L1374)
2020-03-06 21:32:58 +01:00
Simon Ser 7017fa95b8 output: add adaptive_sync_enabled property 2020-03-06 21:32:58 +01:00
Simon Ser 8afc1ed68c Introduce wlr_client_buffer
Split out the client/resource handling out of wlr_buffer by introducing
wlr_client_buffer. Make wlr_buffer an interface so that compositors can
create their own wlr_buffers (e.g. backed by GBM, like glider [1]).

[1]: c66847dd1c/include/gbm_allocator.h (L7)
2020-03-06 21:32:06 +01:00
Jan Beich 8d2e8d8a06 xcursor: also look for cursor themes under ${datadir}/icons by default
Same as https://gitlab.freedesktop.org/wayland/wayland/commit/dd8891be36ec
2020-03-04 10:02:31 +01:00
Simon Ser a3c699eee5 backend/wayland: fix seat caps handling
Previously, each time a wl_seat.capabilities event was received the
Wayland backend created new input devices. It now only does so the first
time.

Input devices are now destroyed when the cap is removed.

Closes: https://github.com/swaywm/sway/issues/5055
2020-03-04 09:57:10 +01:00
Simon Ser 348f52b5fc output: remove wlr_output_impl.schedule_frame
This function allowed backends to provide a custom function for frame
scheduling. Before resuming the rendering loop, the DRM and Wayland
backends would wait for vsync.

There isn't a clear benefit of doing this. The only upside is that we
get more stable timings: the delay between two repaints doesn't change too
much and is close to a mutliple of the refresh rate.

However this introduces latency, especially when a client misses a
frame. For instance a fullscreen game missing vblank will need to wait
more than a whole frame before being able to display new content. This
worst case scenario happens as follows:

- Client is still rendering its frame and cannot submit it in time
- Deadline is reached
- Compositor decides to stop the rendering loop since nothing changed on
  screen
- Client finally manages to render its frame, submits it
- Compositor calls wlr_output_schedule_frame
- DRM backend waits for next vblank
- The wlr_output frame event is fired, compositor draws new content on screen
- On the second next vblank, the new content reaches the screen

With this patch, the wlr_output frame event is fired immediately when
the client submits its late frame.

This change also makes it easier to support variable refresh rate, since
VRR is all about being able to present too-late frames earlier.

References: https://github.com/swaywm/wlroots/issues/1925
2020-03-04 03:22:19 +01:00
Simon Ser 613f9c6f8d backend/wayland: rename wl_seat.c to seat.c
I never got why we have a wl_ prefix here.
2020-03-04 03:21:40 +01:00
Guido Günther c9859f187f wlr_output_power_management_v1: Init output_power->link
This makes sure the `wl_list_remove(&output_power->link)` in
`output_power_destroy()` does not crash even when the output_power never
got added to a list. This can e.g. happen in the `mgmt->output ==
output` error path of `output_power_manager_get_output_power`.
2020-02-27 14:05:01 +01:00
Aleksei Bavshin c99d156f0d xdg-shell: fix popups rendered outside of the screen
Leave positioner inverted on the individual axis if it's no longer
constrained. Otherwise constraint adjustment like `slide_x & flip_y`
could render popup outside of the screen when both axes are constrained.

Fixes Alexays/Waybar#532
2020-02-26 13:43:53 +01:00
Michael Weiser 3ff6a5def3 idle-inhibit: Style and naming improvements
Port back style and naming improvements suggested in
https://github.com/swaywm/wlroots/pull/2026 for
keyboard-shortcuts-inhibit. These are all internal to the implementation
and therefore unproblematic.

Also, retrieve the inhibitor resource version from the manager resource
instead of setting it statically.

Signed-off-by: Michael Weiser <michael.weiser@gmx.de>
2020-02-20 21:40:34 +01:00
Michael Weiser 208e6da3c3 keyboard-shortcuts-inhibit: Add client example
Again, copy'n'search'n'replace the idle inhibit example to become a
simple keyboard shortcuts inhibit example, adding the active and
inactive events.

Getting the initial inhibitor needs to be done later than for idle
inhibit to avoid an error "xdg_surface has never been configured".

Signed-off-by: Michael Weiser <michael.weiser@gmx.de>
2020-02-20 21:40:34 +01:00
Michael Weiser 471b873de3 keyboard-shortcuts-inhibit: Implement the protocol
The keyboard shortcuts inhibitor protocol is useful for remote desktop
and virtualization software in order to request all keyboard events to
be passed to it and (almost) none being resonded to by the compositor.
This allows the session at the other end of the remote desktop
connection or inside the virtual machine to be interacted with as usual
(e.g. Alt+Tab to switch windows on the remote system instead of
locally).

Add the wayland protocol to the meson build files.

Copy'n'search'n'replace the very similar idle inhibit protocol
implementation. This already provides all the basic functionality:
- creating and destroying inhibitors upon request by a client,
- destruction in reaction to destruction of surfaces or displays,
- a list of inhibitors to search through for existing ones as well as
- a signal to be sent to the compositor upon registration of a new
  inhibitor.

Beyond that we add the active and inactive events to be sent to the
client and wire those to activate and deactivate functions for the
compositor to call in confirmation of activation of a new inhibitor or
(un-)suspending of an existing inhibitor e.g. in response to a special
key combination entered by the user as suggested by the protocol.

As mandated by the protocol, we check the existance of an inhibitor for
a given surface and seat upon creation and return the error provided by
the protocol for that purpose.

Signed-off-by: Michael Weiser <michael.weiser@gmx.de>

Closes: https://github.com/swaywm/wlroots/issues/1817
2020-02-20 21:40:34 +01:00
Guido Günther 0df5019609 examples: add output-power-management example client 2020-02-20 16:23:45 +01:00
Guido Günther ba14e196f6 Implement wlr-output-power-management-v1
Co-authored-by: Simon Ser <contact@emersion.fr>
2020-02-20 16:23:45 +01:00
Guido Günther dfc5a40288 Add wlr-output-power-management 2020-02-20 16:23:45 +01:00
Dorota Czaplejewicz 37adcac5d1 text_input_v3: Note features supported by the text field
With this information, consumers can realize they will never retrieve some state, and adjust their strategy.
2020-02-20 16:21:44 +01:00
Simon Ser 68820d6c3d xwayland: ignore pointer focus changes
This reflects what i3 does [1].

[1]: b3faf9fca9/src/handlers.c (L1076)

Fixes: c067fbc010 ("xwm: allow applications to change focus between their own surfaces")
Closes: https://github.com/swaywm/sway/issues/4926
2020-02-19 12:56:05 -05:00
Ilia Bozhinov f416efa918 output-management: add current_configuration_dirty
Previously, if the current configuration contains an output X which is
destroyed, its head is automatically removed. If the compositor submits
the new configuration after X was removed, the current output
configuration is incorrectly detected to be the same as the previous
one, and no done event is sent. To prevent this, we can just keep track
of whether the current configuration is dirty, i.e whether we have sent
a done event for it.
2020-02-19 11:40:23 +01:00
Brian Ashworth 7e990a6bdf meson.build: update wayland requirement to 1.18
This updates the version requirement for wayland-server to 1.18, which
is needed for wl_global_remove and wl_global_set_user_data
2020-02-18 09:09:03 +01:00
Manuel Stoeckl 2fddec56e8 output: fix output transform compositions
This change ensures that wlr_output_transform_compose correctly composes
transforms when the first transform includes a rotation and the second
transform includes a flip.
2020-02-17 21:43:05 +01:00
Manuel Stoeckl f22a5d1704 Fix output rotation direction
The Wayland protocol specifies output transform rotations to be
counterclockwise and applied to the surface. Previously, wlroots
copied Weston and incorrectly made rotations act clockwise on
surfaces. This commit fixes that.

This change will break compositors which expect transform rotations
to be clockwise, and the rare applications that make use of surface
transforms.
2020-02-17 21:43:05 +01:00
Simon Ser c5376c2d2c seat: don't destroy global immediately 2020-02-17 20:52:37 +01:00
Simon Ser e63b70263a output: don't destroy global immediately
Closes: https://github.com/swaywm/sway/issues/3625
2020-02-17 20:52:37 +01:00
Simon Ser 29726e6816 util: add wlr_output_destroy_safe
This adds a generic helper to destory transient globals.

See [1]. This patch depends on [2] and [3].

[1]: https://gitlab.freedesktop.org/wayland/wayland/issues/10
[2]: https://gitlab.freedesktop.org/wayland/wayland/merge_requests/28
[3]: https://gitlab.freedesktop.org/wayland/wayland/merge_requests/30
2020-02-17 20:52:37 +01:00
Jan Beich 2bad34e024 backend/session: allow GPU enumeration on FreeBSD
f11ee5b418
2020-02-17 10:43:58 +01:00
Andri Yngvason 273b280f46 virtual-pointer: Add request for mapping to specific output 2020-02-10 21:01:38 +01:00
Scott Anderson 2fea2fced8 examples: Fix compositor-examples
Due to the way the wlr_output API was changed, these examples would
never get a frame event to start the rendering loop. We now commit the
outputs to start it.
2020-02-08 11:38:44 +01:00
Simon Ser 4b051aa926 meson: fix wayland-server minimum required version
Having 1.16 results in the following error when running the compositor:

    2019-04-27 17:30:50 - [wayland] wl_global_create: implemented version for 'wl_seat' higher than interface version (7 > 6)
    2019-04-27 17:30:50 - [sway/input/seat.c:428] seat_create:could not allocate seat

We require wayland-server >= 1.17 for wl_seat version 7.

Fixes: a671fc51d2 ("Advertise wl_seat version 7")
Fixes: a656e486f4 ("seat: fallback to v6 if libwayland 1.17 isn't available")
2020-02-04 22:44:02 +00:00
Jan Staněk f2943bdf61 Declare wlr_seat globals as extern 2020-01-28 15:45:58 +01:00
Simon Ser 7f2bd0b211 render: unconditionally disable implicit X11 includes
Even if the X11 backend or Xwayland is enabled, we don't rely on
EGL/egl.h including Xlib headers.
2020-01-24 21:33:12 +00:00
Rouven Czerwinski 144d2041ad backend/drm: remove overzealous finish_drm_surface
The previous PR was overzealous in adding a finish_drm_surface call
which was also done by the caller. Remove the call and also move the
comment to the correct code location.
2020-01-24 19:57:09 +01:00
Rouven Czerwinski f0594fb732 backend/drm: add env variable to disable modifiers
In some cases modesets fail if the planes are initialized with
modifiers. Since in this case possibly all planes need to reinitialized,
which is not possible in the current wlroots design, add an environment
variable for affected users.
2020-01-24 19:43:51 +01:00
Simon Ser 1f722f5c80 build: replace version with soversion
This allows us to have a single number to update when doing a release.

This drops WLR_VERSION_API_* definitions.
2020-01-24 11:17:03 +01:00
Scott Anderson 70a084c119 meson: Fix protocol includes for compositor examples 2020-01-24 09:10:28 +00:00
Drew DeVault 57ffb35de0 Update version to 0.10.0 2020-01-22 13:22:04 -05:00
Jan Staněk d9fdd28ac6 Honor the `examples` meson setting 2020-01-20 15:43:08 +01:00
Simon Ser b1a63bcd84 keyboard-group: two NULL keymaps are equal
Previously, creating a keyboard group without any keymap set would
result in an error:

    Device keymap does not match keyboard group's
2020-01-17 12:23:14 -05:00
Simon Ser 7c05933e51 backend/drm: prevent outputs from being destroyed on commit
This would happen if initializing the renderer fails. Instead, we just
mark the output as disabled.

References: https://github.com/swaywm/sway/pull/4917
2020-01-17 06:49:31 -07:00
Simon Ser df972677c1 docs: document XDG_SESSION_ID 2020-01-14 07:38:03 -07:00
Simon Ser a13aeb9711 docs: establish one section per backend 2020-01-14 07:38:03 -07:00
Simon Ser 32fc25f151 docs: remove rootston-specific env vars
rootston isn't part of wlroots anymore.
2020-01-14 07:38:03 -07:00
Scott Anderson 062583ed58 Bump meson version to 0.51.2
There was an issue in 0.51.1 and earlier, where lists of dependencies
and disablers weren't acting like they should. Instead of disabling a
build, it would error out instead.

Changing this logic to work around it is annoying, so just bump the
version instead.
2020-01-14 12:48:41 +01:00
Simon Ser 6d3f3b9300 render/gles2: unbind textures after use
Keeping textures bound results in hard-to-debug situations where some GL
operations incorrectly affect the texture.
2020-01-13 07:52:30 -07:00
Simon Ser 2b04857343 render/egl: remove SURFACELESS_MESA special case
Users can just pass EGL_DEFAULT_DISPLAY themselves.
2020-01-12 10:10:09 -07:00
xdavidwu 3b35043d00 text-input: fix missing destroy signal init 2020-01-12 12:12:26 +01:00
Drew DeVault ebdbe177d6 Drop RDP backend
Users interested in remote access to wlroots compositors should use
wayvnc:

https://github.com/any1/wayvnc
2020-01-10 19:38:39 +01:00
Simon Ser 802ef9da8a backend/wayland: handle display errors more gracefully
Previously, an error on the remote Wayland display would result in an
infinite loop priting:

    2020-01-09 13:39:03 - [wayland] Source dispatch function returned negative value!
    2020-01-09 13:39:03 - [wayland] This would previously accidentally suppress a follow-up dispatch

This happens when the remote compositor disconnects the client because
of a protocol error, for instance.

Handle wl_display_dispatch and wl_display_dispatch_pending returning -1
by terminating the local display and printing an error.
2020-01-09 07:48:30 -07:00
Simon Ser e6fd880686 backend/wayland: listen to wl_buffer.release events
Previously, we just assumed submitting a new frame would make the
compositor release the current one. This isn't always the case, for
instance Sway retains old buffers when a transaction is pending. This
resulted in synchronization issues with clients writing in
front-buffers.

Fix this by un-referencing a wlr_buffer when the parent compositor sends
wl_buffer.release.

Tested by running a fullscreen mpv instance in Sway with the Wayland
backend.
2020-01-09 07:41:50 -07:00
Jason b5cb6de232 Allow WLR_RDP_PORT to be any valid TCP/UDP port number 2020-01-08 10:29:52 +01:00
Drew DeVault a2cbb4e417 Update version to 0.9.1 2020-01-06 09:01:10 -05:00
Ilia Bozhinov c067fbc010 xwm: allow applications to change focus between their own surfaces
Although currently this problem is present in only Steam, and it is
actually a client bug.
2020-01-05 23:17:08 +01:00
Andri Yngvason 51f8c22f4d virtual-pointer: Actually use the value passed to axis_discrete
It turns out that scrolling doesn't work unless this value is set somewhere.
2020-01-03 15:13:07 -07:00
Simon Ser 5bbb44482b backend/wayland: fix frame callback not registered
This got removed in [1]. I probably messed up the rebase.

[1]: https://github.com/swaywm/wlroots/pull/1797/files#diff-3065f86e6de87d143d4a7673a8ee3a2d

Fixes: 5d1ba0f446 ("output: re-introduce atomic mode, enabled, scale and transform")
2020-01-02 12:44:28 -07:00
Drew DeVault 1c5ca793c0 Update version to 0.9.0 2019-12-31 10:11:54 -05:00
Simon Ser 346b43e937 render: guard rendering operations between begin() and end()
Add a wlr_renderer.rendering bool, set it to true between
wlr_renderer_begin() and wlr_renderer_end(). Assert we're rendering when
calling functions that render.
2019-12-31 08:07:44 -07:00
Scott Moreau a9b1d9e838 xwayland: Clean up if Xwayland fails to start
When running wlroots compositors with Xwayland executable bits
unset, if DISPLAY is set to the display number wlroots has set
up, then X and gtk clients (at least) hang when they are ran.
X clients should fail with an error and exit while gtk clients
should fall back to wayland backend and run correctly. This is
because wlroots opened sockets for Xwayland but wasn't closing
them if Xwayland failed to start.
2019-12-31 08:07:16 -07:00
Josef Gajdusek a7b538008b virtual-pointer: Add support for the wlr-virtual-pointer-unstable-v1 2019-12-31 10:29:02 +01:00
Simon Ser 21e1953b61 backend/drm: don't modeset with a NULL mode after TTY switch
This fixes a segfault in drm_connector_set_mode (mode = NULL).

This happens because we set wlr_output.enabled to true if the connector
is attached to the CRTC. When the user disables an output in the
wlroots-based compositor, switches to another VT (enabling the output),
then switches back, wlroots sets wlr_output.enabled to true but
wlr_output.current_mode is NULL.

We should consider not reading properties from KMS after a TTY switch,
disabling all connectors. However this may result in flickering (outputs
being disabled then re-enabled).

Closes: https://github.com/swaywm/wlroots/issues/1874
2019-12-30 14:32:37 -07:00
Simon Ser ff29843d87 output: only advertise current mode
- Regular clients shouldn't care about modes
- Modes exposed are missing metadata such as aspect-ratio, interleaved, etc
- Modes exposed cannot be pruned [1]
- wlr-output-management provides a better API for privileged clients

[1]: https://gitlab.freedesktop.org/wayland/wayland/issues/92

Closes: https://github.com/swaywm/wlroots/issues/1099
2019-12-30 14:25:33 -07:00
Simon Ser 8bb2dc68ea xdg-shell: make wlr_xdg_surface_from_resource reject NULL
Most resources must not be NULL. Make it so callers need to check for
NULL explicitly. This makes it clearer in the handlers code that the
NULL wl_resource case needs to be handled, and allows callers to make a
difference between a NULL wl_resource and an inert resource.
2019-12-30 14:24:35 -07:00
Simon Ser 7e521fed97 xdg-shell: fix inert xdg_surface handling
Closes: https://github.com/swaywm/sway/issues/4834
Closes: https://github.com/swaywm/wlroots/issues/1890
2019-12-30 14:24:35 -07:00
Simon Ser 31f721286a tinywl: enable and commit output when modesetting
While at it, choose the preferred mode instead of the last one.
2019-12-30 11:21:11 -07:00
Simon Ser 8fc16890c7 output: refuse to commit a buffer or modeset a disabled output
References: https://github.com/swaywm/wlroots/issues/1780#issuecomment-518938390
2019-12-30 11:21:11 -07:00
Simon Ser b5597f5b44 output: clear pending bit if pending == current
In case the pending value is the same as the current value, clear the
bit from pending.committed.
2019-12-30 11:21:11 -07:00
Simon Ser f0781cd792 backend/drm: modeset before enabling an output
This saves one modeset in case the previous mode is different.
2019-12-30 11:21:11 -07:00
Simon Ser 5d1ba0f446 output: re-introduce atomic mode, enabled, scale and transform
This reverts commit 01f903874b and re-applies
commit ee5f98ad49.

Updates: https://github.com/swaywm/wlroots/issues/1640 (Atomic output updates issue)
See also: https://github.com/swaywm/wlroots/pull/1762 (Atomic output updates original PR)
See also: https://github.com/swaywm/wlroots/issues/1780 (Issue caused by atomic output updates)
See also: https://github.com/swaywm/sway/issues/4419 (Issue caused by atomic output updates)
See also: https://github.com/swaywm/wlroots/pull/1781 (Revert PR)
2019-12-30 11:21:11 -07:00
Brian Ashworth e0e5a167ed wlr_keyboard_group: fix mem leak in refresh_state
This fixes a memory leak the refresh_state function for
wlr_keyboard_group. The event struct was being dynamically allocated and
never free'd. This changes it to a static allocation.
2019-12-29 23:33:26 +01:00
Simon Ser 471f9a3f6a output-management-v1: use wlr_output.description
Unfortunately, the description isn't mutable yet for this protocol [1].

[1]: https://github.com/swaywm/wlr-protocols/issues/67
2019-12-29 12:35:22 -05:00
Simon Ser 1f799c1cbd xdg-output-v1: use wlr_output.description
Since [1], the xdg-output description is mutable. Listen to output
description changes and send the new output description when updated.

[1]: 048102f21a
2019-12-29 12:35:22 -05:00
Simon Ser 4da4a15d6b output: add description
wlr_output.description is a string containing a human-readable string
identifying the output. Compositors can customise it via
wlr_output_set_description, for instance to make the name more
user-friendly.

References: https://github.com/swaywm/wlroots/issues/1623
2019-12-29 12:35:22 -05:00
Simon Ser a420d2c41e ci: add a build run with all features disabled
Closes: https://github.com/swaywm/wlroots/issues/1255
2019-12-29 12:24:03 -05:00
Simon Ser 18775fda0f Revert "ci: add xorgproto dep to Arch build"
This reverts commit 35bc3e662a.

Per [1], the dependency has been re-added and we shouldn't need to
explicitly install it anymore.

[1]: https://bugs.archlinux.org/task/64914
2019-12-23 07:49:55 -05:00
Scott Anderson acb171804e meson: Remove tag generation
This is insanely fruststating. Since it works off of currently committed
git files, if you ever remove anything, your build will fail until you
commit it.

It doesn't even belong in the build system anyway, as it's only part of
an individual's specific workflow. Use for own scripts for your own
workflow. Go use some git hooks to generate this or something.
2019-12-23 07:48:29 -05:00
Scott Anderson cff1c2f740 meson: Various improvements
Bumps minimum version to 0.51.0

- Remove all intermediate static libraries.
  They serve no purpose and are just add a bunch of boilerplate for
  managing dependencies and options. It's now managed as a list of
  files which are compiled into libwlroots directly.

- Use install_subdir instead of installing headers individually.
  I've changed my mind since I did that. Listing them out is annoying as
  hell, and it's easy to forget to do it.

- Add not_found_message for all of our optional dependencies that have a
  meson option. It gives some hints about what option to pass and what
  the optional dependency is for.

- Move all backend subdirectories into their own meson.build. This
keeps some of the backend-specific build logic (especially rdp and
session) more neatly separated off.

- Don't overlink example clients with code they're not using.
  This was done by merging the protocol dictionaries and setting some
  variables containing the code and client header file.
  Example clients now explicitly mention what extension protocols they
  want to link to.

- Split compositor example logic from client example logic.

- Minor formatting changes
2019-12-23 07:48:29 -05:00
Ting-Wei Lan fc6c0ca12e backend/session/freebsd: Fix the way to get TTY path
Previously, the path of TTY is generated using snprintf with %d format.
It works with TTY 1 to 10, but fails with TTY with greater number
because the number used in the name is in base 32 instead of base 10.
Since there is no standard function to convert a number to a string with
a custom base, this commit adds a function to do it.

Fixes: https://github.com/swaywm/wlroots/issues/1854
2019-12-22 11:23:41 +01:00
Simon Ser 01818ad2c8 render: fix EGL extensions not loaded
Some extensions are only advertised by the EGL implementation with a
non-zero EGLDisplay. That's the case when the extension can only be
enabled when the hardware/driver supports it for instance.

Instead of checking for all extensions without a display, check only for
EGL_EXT_platform_base and EGL_KHR_debug which are used before
eglGetDisplay. Check for all other extensions when we have a display.

Closes: https://github.com/swaywm/wlroots/issues/1955
2019-12-21 08:54:24 -05:00
Simon Ser 35bc3e662a ci: add xorgproto dep to Arch build
Arch Linux maintainers are still figuring out whether they should ask
people to explicitely install it or make it a dependency of libxcb again
[1].  In the meantime, add it as an explicit dependency. I'll revert
this patch if they decide otherwise.

[1]: https://bugs.archlinux.org/task/64914
2019-12-21 08:50:59 -05:00
Rouven Czerwinski be4b9f7f5b backend/drm: print preferred mode
While printing the supported output modes, annotate the preferred mode.
2019-12-20 11:00:21 +01:00
Simon Ser 515679e4fe Refactor EGL/GL API loading
Remove glapi.sh code generation, replace it with hand-written loading
code that checks extension strings before calling eglGetProcAddress.

The GLES2 renderer still uses global state because of:

- {PUSH,POP}_GLES2_DEBUG macros
- wlr_gles2_texture_from_* taking a wlr_egl instead of the renderer
2019-12-20 01:03:34 +00:00
myfreeweb 774548696c Send tablet tool frame on proximity_out
Fixes GTK application crashes
2019-12-18 13:23:07 -05:00
Simon Ser 98cd11c019 output: fix wlr_output_preferred_mode fallback
`mode` points to an invalid pointer (head of the list) when the loop
stops.

Closes: https://github.com/swaywm/sway/issues/4717
2019-12-16 01:24:28 +00:00
Simon Ser 7fc58e704a surface: don't unref the current buffer on failure
If wlr_buffer_create fails, keep the previous buffer.
2019-12-14 09:19:44 -05:00
Simon Ser 96e8e9b098 buffer: improve error handling
In case the texture can't be imported, release the buffer so that the
client can submit another one. In case the allocation fails, disconnect
the client.
2019-12-14 09:19:44 -05:00
Simon Ser efd294ef09 backend/drm: add BenQ to manufacturer list 2019-12-11 08:18:45 -05:00
Manuel Stoeckl 8a5e4768e1 output: fix cursor wl_surface.{enter,leave} tracking
This change ensures that wl_surface.leave is sent when a surface
associated with the cursor is disassociated (when the cursor is
reset).
2019-12-09 15:58:18 +01:00
Simon Ser 6ca82087b1 backend/drm: fix segfault in init_drm_surface
When surf->gbm was previously set, we destroy it without setting it to
NULL. Later on, we only create the GBM surface if surf->gbm is NULL.
This result in a use-after-free when we start using surf->gbm.

Closes: https://github.com/swaywm/wlroots/issues/1868
Closes: https://github.com/swaywm/wlroots/issues/1874
Closes: https://github.com/swaywm/sway/issues/4785
Closes: https://github.com/swaywm/sway/issues/4717
Closes: https://github.com/swaywm/sway/issues/4730
Fixes: 2bdd1d0896 ("backend/drm: use modifiers for our GBM buffers")
2019-12-05 10:49:04 -05:00
Simon Ser 8681e4ab8a backend/drm, backend/libinput: listen to session destroy
This fixes a heap-use-after-free when the session is destroyed before
the backend during wl_display_destroy:

    ==1085==ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000180 at pc 0x7f88e3590c2d bp 0x7ffdc4e33f90 sp 0x7ffdc4e33f80
    READ of size 8 at 0x614000000180 thread T0
        #0 0x7f88e3590c2c in find_device ../subprojects/wlroots/backend/session/session.c:192
        #1 0x7f88e3590e85 in wlr_session_close_file ../subprojects/wlroots/backend/session/session.c:204
        #2 0x7f88e357b80c in libinput_close_restricted ../subprojects/wlroots/backend/libinput/backend.c:24
        #3 0x7f88e21af274  (/lib64/libinput.so.10+0x28274)
        #4 0x7f88e21aff1d  (/lib64/libinput.so.10+0x28f1d)
        #5 0x7f88e219ddac  (/lib64/libinput.so.10+0x16dac)
        #6 0x7f88e21b415d in libinput_unref (/lib64/libinput.so.10+0x2d15d)
        #7 0x7f88e357c9d6 in backend_destroy ../subprojects/wlroots/backend/libinput/backend.c:130
        #8 0x7f88e3545a09 in wlr_backend_destroy ../subprojects/wlroots/backend/backend.c:50
        #9 0x7f88e358981a in multi_backend_destroy ../subprojects/wlroots/backend/multi/backend.c:54
        #10 0x7f88e358a059 in handle_display_destroy ../subprojects/wlroots/backend/multi/backend.c:107
        #11 0x7f88e314acde  (/lib64/libwayland-server.so.0+0x8cde)
        #12 0x7f88e314b466 in wl_display_destroy (/lib64/libwayland-server.so.0+0x9466)
        #13 0x559fefb52385 in main ../main.c:67
        #14 0x7f88e2639152 in __libc_start_main (/lib64/libc.so.6+0x27152)
        #15 0x559fefb4297d in _start (/home/simon/src/glider/build/glider+0x2297d)

    0x614000000180 is located 320 bytes inside of 416-byte region [0x614000000040,0x6140000001e0)
    freed by thread T0 here:
        #0 0x7f88e3d0a6b0 in __interceptor_free /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:122
        #1 0x7f88e35b51fb in logind_session_destroy ../subprojects/wlroots/backend/session/logind.c:270
        #2 0x7f88e35905a4 in wlr_session_destroy ../subprojects/wlroots/backend/session/session.c:156
        #3 0x7f88e358f440 in handle_display_destroy ../subprojects/wlroots/backend/session/session.c:65
        #4 0x7f88e314acde  (/lib64/libwayland-server.so.0+0x8cde)

    previously allocated by thread T0 here:
        #0 0x7f88e3d0acd8 in __interceptor_calloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:153
        #1 0x7f88e35b911c in logind_session_create ../subprojects/wlroots/backend/session/logind.c:746
        #2 0x7f88e358f6b4 in wlr_session_create ../subprojects/wlroots/backend/session/session.c:91
        #3 0x559fefb51ea6 in main ../main.c:20
        #4 0x7f88e2639152 in __libc_start_main (/lib64/libc.so.6+0x27152)
2019-12-01 10:26:12 -05:00
Simon Ser 16f22940d9 keyboard: emit key events without keymap
Sometimes compositors don't need an XKB keymap at all, they just handle
raw keycodes. Emit key events even if no keymap is set.
2019-12-01 10:25:50 -05:00
Simon Ser dc6ef658b6 Revert "output: add block_idle_frame"
This reverts commit cbb2781fed.

In [1], we found issues with block_idle_frame and replaced it with
frame_pending. block_idle_frame is now unused.

[1]: https://github.com/swaywm/sway/pull/4772
2019-12-01 10:25:34 -05:00
n3rdopolis ce5040a5a1 note libinput as well 2019-11-28 09:39:58 -05:00
n3rdopolis e8db36d5d8 Update environment variable documentation to include more backends
Testing with exporting WLR_BACKENDS=drm worked, and it wasn't documented. Checking the backends folder, and it also mentions an RDP backend as well
2019-11-28 09:39:58 -05:00
Dorota Czaplejewicz fadd4706ed virtual_keyboard: Accept keycode 0 2019-11-27 16:49:12 +01:00
Ronan Pigott 5df606d8ab render/gles2: do not set GL_TEXTURE_MAG_FILTER 2019-11-26 09:42:10 -05:00
Simon Ser 5cde35923c Simplify globals implementation by removing destructors
Some globals are static and it doesn't make sense to destroy them before
the wl_display. For instance, wl_compositor should be created before the
display is started and shouldn't be destroyed.

For these globals, we can simplify the code by removing the destructor
and stop keeping track of wl_resources (these will be destroyed with the
wl_display by libwayland).
2019-11-25 09:01:46 -05:00
Simon Ser bcd5f7d259 render: remove return in wlr_texture_get_size
Otherwise this error happens:

    ../subprojects/wlroots/render/wlr_texture.c: In function ‘wlr_texture_get_size’:
    ../subprojects/wlroots/render/wlr_texture.c:47:9: error: ISO C forbids ‘return’ with expression, in function returning void [-Werror=pedantic]
       47 |  return texture->impl->get_size(texture, width, height);
          |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ../subprojects/wlroots/render/wlr_texture.c:45:6: note: declared here
       45 | void wlr_texture_get_size(struct wlr_texture *texture, int *width,
          |      ^~~~~~~~~~~~~~~~~~~~
2019-11-25 08:54:04 -05:00
Timidger 6bd7a62c09 Use layer shell v2 2019-11-23 17:52:00 -05:00
Jason 9cbbfa957c Amend typo 2019-11-23 10:33:47 -05:00
Jason e8855ee462 Amend typos 2019-11-23 10:33:47 -05:00
Simon Ser e959b882d5 backend/wayland: add support for presentation-time 2019-11-21 11:32:30 -05:00
Simon Ser 2122e49bea presentation-time: add helper for common case
Most of the time, compositors just display the surface's current buffer
on an output. Add an helper to make it easy to support presentation-time
in this case.
2019-11-21 11:03:43 -05:00
Simon Ser 533ea6d7ef presentation-time: make API more flexible
The wlr_presentation_feedback struct now tracks presentation feedback
for multiple resources (but still a single surface content update). This
allows the compositor to properly send presentation events even when
there is more than one frame of latency or when it references a
surface's buffer.
2019-11-21 11:03:43 -05:00
Simon Ser 3084cee7bc output: fix off-by-one wlr_output_event_present.commit_seq
Backends not supporting presentation feedback call
wlr_output_send_present with a NULL event in their commit handler. Since
the commit hasn't been applied yet, commit_seq still has its old value.
We need to increment it.

An alternative would be to move commit_seq in wlr_output_state. This
would allow to have a pending and a current commit_seq.
wlr_output_send_present could take the pending commit_seq when called
with a NULL event.
2019-11-21 11:03:43 -05:00
Simon Ser 2d9661f189 output: set wlr_output.commit_seq before firing the commit event
This allows listeners to read the commit sequence number.
2019-11-21 11:03:43 -05:00
Jan Beich d0479cc2bc examples: set mode when creating shm object
$ screencopy
shm_open failed
failed to create buffer

$ posixshmcontrol ls
MODE            OWNER   GROUP   SIZE    PATH
---------       foo     foo     33177600        /wlroots-screencopy
2019-11-20 22:19:48 -05:00
Simon Ser 16e5e9541b Add -Wmissing-prototypes
This requires functions without a prototype definition to be static.
This allows to detect dead code, export less symbols and put shared
functions in headers.
2019-11-20 02:05:03 +00:00
Scott Anderson 685a5a11a9 backend/x11: Revert usage of present extension
This reverts commit 3317134adf.
This reverts commit a3c3b928a3.

There are some serious issues when running this on a real X server, as
opposed to running this on Xwayland, where this was tested.

More investigation needs to be done into why these issues happen and if
our usage of the present extension is correct.
2019-11-19 11:06:38 +01:00
Scott Moreau fe72400bad build: Pass library as first argument to pkgconfig.generate()
Eliminates this warning when building wlroots as a subproject:

subprojects/wlroots/meson.build:216: DEPRECATION: Library wlroots
was passed to the libraries keyword argument of a previous call
to generate() method instead of first positional argument. Adding
wlroots to Requires field, but this is a deprecated behaviour
that will change in a future version of Meson. Please report the
issue if this warning cannot be avoided in your case.
2019-11-19 02:40:07 +01:00
Scott Anderson 3317134adf backend/x11: Drop required present version
We say 1.2, but I don't think we actually use anything that was
introduced past 1.0.
2019-11-18 11:53:17 +01:00
Scott Anderson b58e8451b8 backend: Do not attempt DRM on X11/WL failure
This can really mess with the session if logind is not being used,
and it's going to always fail anyway.
2019-11-18 11:53:17 +01:00
Simon Ser 6c649bab53 output: add wlr_output_event_present.commit_seq
This is set to the value of wlr_output.commit_seq when the frame has
been submitted. This allows tracking presentation with more then 1 full
frame of latency.

References: https://github.com/swaywm/wlroots/issues/1917
2019-11-17 00:12:59 +01:00
Simon Ser cde544de81 backend/wayland: expose remote objects
Expose the remote wl_display, wl_surface and wl_seat used by the Wayland
backend.

This allows compositors to customize the Wayland backend and to have
more freedom. For instance a compositor might want to handle clipboard
and drag-and-drop from the remote Wayland compositor. Another compositor
might want to setup pointer constraints.
2019-11-13 10:15:28 -05:00
Simon Ser 1e568d84df backend/wayland: add support for relative-pointer-unstable-v1
We just send relative motion events alongside absolute motion events.
Compositors can figure out how absolute and relative events are related
(e.g. whether they have been triggered by the same logical event) with
the frame event.
2019-11-13 10:15:19 -05:00
Drew DeVault 4c9423278a Introduce wlr_renderer_get_egl 2019-11-11 19:10:10 +01:00
Alynx Zhou aa9ea95e1f Skip assign when sub_x or sub_y is NULL in wlr_surface_surface_at 2019-11-08 08:48:30 +01:00
Simon Ser 447835afc1 render/gles2: provide public API to access GL texture
Prior to this commit, compositors needed to render the texture to an
intermediate off-screen buffer using wlr_renderer APIs if they wanted to
use a custom rendering path (e.g. render to a 3D scene).

A new wlr_gles2_texture_get_attribs exposes the GL texture target and ID
so that compositors can render wlr_textures with their own shaders. An
example of a compositor doing so is available at [1].

[1]: 3db905b784/src/render.c (L227)
2019-11-07 14:24:03 -05:00
Simon Ser eaa98f6aff render: remove EGL includes from wlr_texture.h 2019-11-06 11:30:57 -05:00
Scott Anderson 85a2ee6d30 render/gles: Simplify textures a bit
We don't need our own enum for types. Instead we just use
GL_TEXTURE_{2D,EXTERNAL_OES}, which already describes usage.

Also fixes a situation where we were using GL_TEXTURE_2D in a situation
we should not have. wl_drm buffers are always GL_TEXTURE_EXTERNAL_OES,
no matter if they're RGB or any other format.
2019-11-06 09:46:01 +01:00
Brian Ashworth f2d3b1000f Introduce wlr_keyboard_group
A wlr_keyboard_group allows for multiple keyboard devices to be
combined into one logical keyboard. Each keyboard device can only be
added to one keyboard group. This helps with the situation where one
physical keyboard is exposed as multiple keyboard devices. It is up to
the compositors on how they group keyboards together, if at all.

Since a wlr_keyboard_group is one logical keyboard, the keys are a set.
This means that if a key is pressed on multiple keyboard devices, the
key event will only be emitted once, but the internal state will count
the number of devices that the key is pressed on. Likewise, the key
release will not be emitted until the key is released from all devices.
If the compositor wants access to which keys are pressed and released
on each keyboard device, the events for those devices can be listened
to, as they currently are, in addition to the group keyboard's events.

Also, all keyboard devices in the group must share the same keymap. If
the keymap's differ, the keyboard device will not be able to be added
to the group. Once in the group, if the keymap or effective layout for
one keyboard device changes, it will be synced to all keyboard devices
in the group. The repeat info and keyboard modifiers are also synced
2019-11-05 20:05:49 +01:00
Scott Anderson 626c98d754 session/logind: Clean up add_signal_matches
The original signal matching was using the old interface before
sd_bus_match_signal was added, which the new code uses.
Change them all to use it now.
2019-11-03 10:13:47 +01:00
Ronan Pigott 3ebf079a9a session/logind: support CanGraphical property 2019-11-03 00:17:23 +00:00
Scott Anderson a3c3b928a3 backend/x11: Give X11 a real rendering loop
Makes use of the present extension to get notified of vsync, and not
require any stupid timer hacks. Also make use of the present version of
ConfigureNotify, because why not?
2019-11-02 12:43:33 +01:00
György Kurucz b81bb2ef30 Fix heap-use-after-free in wlr_send_tablet_v2_tablet_pad_leave
See swaywm/sway#4660
2019-10-27 19:01:16 +01:00
Simon Ser 9971db02ff output-management-v1: add assertion as a safety net
Makes it easier to figure out when the compositor submits an invalid
output state.

References: https://github.com/swaywm/sway/pull/4673
2019-10-27 10:49:53 -04:00
Simon Ser 0e57effd38 backend/drm: add support for custom modes
Use the CVT algorithm to create a drmModeModeInfo.
2019-10-27 10:46:47 -04:00
Simon Ser e97c2c3639 backend/drm: retry without modifiers for the primary plane
On some Intel cards using modifiers can fill the FIFO and prevent
hotplugged outputs from being properly enabled.

Add a fallback without modifiers.

Fixes: 2bdd1d0896 ("backend/drm: use modifiers for our GBM buffers")
References: https://github.com/swaywm/wlroots/issues/1840
Closes: https://github.com/swaywm/wlroots/issues/1852
2019-10-23 09:36:50 +00:00
Simon Ser 51416738ea render/egl: prevent use-after-free when destroying current surface 2019-10-23 09:36:50 +00:00
Simon Ser fd25e2ca11 backend/drm: track gbm_bo during direct scan-out
We need to destroy the gbm_bo we imported and drmModeRmFb.

Closes: https://github.com/swaywm/sway/issues/4662
2019-10-22 21:50:31 +00:00
Andri Yngvason 61a6f2b928 screencopy: Implement damage reporting 2019-10-22 10:41:32 -04:00
Andri Yngvason 2a63f4fc61 protocol/screencopy: Add damage reporting 2019-10-22 10:41:32 -04:00
Andri Yngvason 5d8bd4d343 output: Add commit sequence number
This allows synchronisation between different instances of commit/precommit
callbacks.
2019-10-22 10:41:32 -04:00
Ferdinand Bachmann d113e48a2a Add missing include required by mesa and libglvnd change
eglext.h no longer inludes eglmesaext.h, include it within wlroots
explicitly.

Fixes #1862
2019-10-19 10:50:19 -04:00
Simon Ser 02d664b37f protocol: sync layer-shell with upstream
Fixes this warning:

    ../protocol/wlr-layer-shell-unstable-v1.xml:241: warning: since version not increasing
2019-10-17 09:12:33 -04:00
Timidger 1724261910 layer_shell: set layer of existing surface 2019-10-16 09:41:13 -04:00
Simon Ser 6bb7639a0f render/gles2: don't unset the current EGL surface when destroying texture
When a texture is destroyed between wlr_egl_make_current and
wlr_egl_swap_buffers, it resets the current EGL surface to NULL. This
makes wlr_egl_swap_buffers fail.

If the EGL context is already current, there's no need to reset it.
2019-10-16 09:40:26 -04:00
Simon Ser 5bddb5a909 backend/wayland: add support for direct scan-out
Closes: https://github.com/swaywm/wlroots/issues/1830
2019-10-16 09:40:26 -04:00
Ivan Molodetskikh cbb2781fed output: add block_idle_frame 2019-10-16 09:35:07 -04:00
Danilo Spinella 41cbb80e25 Add new define EGL_NO_X11 for newer mesa library
Define both MESA_EGL_NO_X11_HEADERS and EGL_NO_X11 for
backward combatibility.
2019-10-16 16:29:25 +03:00
Simon Ser 21b75e5d12 build: simplify by using disabler deps 2019-10-16 09:01:27 -04:00
Ilia Bozhinov 480a31ea4e wlr_box: properly calculate closest point for non-positive area boxes
If box->width/height is <= 0, the box doesn't contain any points, and so
there is no closest point. wlr_box_closest_point should return NAN in this
case.

In addition, we need to handle empty boxes in a few other
output-layout-related places, because outputs can have size 0x0 when
they are created or destroyed.
2019-10-11 23:43:13 +03:00
Simon Ser 2bdd1d0896 backend/drm: use modifiers for our GBM buffers 2019-10-11 08:11:48 -04:00
Simon Ser 11bf87d678 render/egl: support formats with zero modifiers 2019-10-11 08:09:08 -04:00
Scott Moreau 3b4824a2fe xwayland: Expose configure request mask
Without this information, compositors have no way to tell whether
or not to consider the position information valid. Most notably,
a compositor needs to know if it should pick a position for the
surface or use the position sent in the configure request.
2019-10-08 19:46:06 +03:00
Simon Ser 9796abcced build: workaround for meson disabler object not working with if not 2019-10-08 12:42:18 -04:00
Drew DeVault b051bb68c2 Update version to 0.8.1 2019-10-07 15:28:20 -04:00
amingin b1b93c2c7e Fixes crash of compositor when unvalidated keycode 0 is passed (#1833)
* Fixes crash of compositor when unvalidated keycode 0 is passed from virtual keyboard

* Style fix
2019-09-27 13:11:30 +03:00
Ivan Molodetskikh 5f78ea20fa drm: use IMPORT_FD for INVALID modifier
GBM_BO_IMPORT_FD_MODIFIER doesn't accept the INVALID modifier.
2019-09-26 23:26:40 +03:00
Markus Ongyerth ebeef0fbe8 Clean up wayland backend tablet support
Mostly address feedback from emersion on PR #1694
Remove const qualifier from char *name, to allow free() call
2019-09-26 19:41:19 +03:00
Markus Ongyerth 2285e36b0c Add zwp-tablet-unstable-v2 client support
This allows wlroots based compositors to properly use graphic tablets
with the wayland backend.
This should be a decent quality of life improvement when working on
tablet related features.
2019-09-26 19:41:19 +03:00
Markus Ongyerth 57babd2e13 Move initialization of wlr_tablet_pad into types/wlr_tablet_pad 2019-09-26 19:41:19 +03:00
Ivan Molodetskikh 020a33e057 presentation_feedback: add the sampled state 2019-09-19 19:44:19 +03:00
Simon Ser c808613287 compositor: disconnect client on OOM in create_surface 2019-09-18 11:28:15 +12:00
Versus Void 2ecfc46b93 xdg-output: send wl_output.done after xdg_output created
xdg-output version 3 requires to send wl_output.done
"after all xdg_output properties have been sent when the object is created"
2019-09-17 15:30:20 +03:00
Scott Anderson 06644575da render/egl: Change KHR_debug log to include error code 2019-09-15 10:06:34 +03:00
Sebastian Krzyszkowiak a14d650864 wlr_seat_touch: Destroy the touchpoint on client destroy
Since e26217c51e3a5e1d7dfc95a8a76299e056497981, touchpoints can outlive
surfaces. This works fine as long as the client stays around, but fails
horribly otherwise; therefore we have to make sure that touchpoints don't
outlive their clients.

Fixes #1788
2019-09-14 16:19:07 +03:00
Ilia Bozhinov 8b0f1bc850 layer-shell: ignore ack_configure() on closed surfaces
When the surface is closed, we destroy all pending serials waiting to be
accepted. This means we need to ignore any future ack events, because we
can have the following events:

1. -> configure()
2. -> close()
3. <- ack_configure()

At point 3, wlroots will error the client because of invalid serial,
however the client hasn't processed close() yet.
2019-09-13 13:58:58 +03:00
Rouven Czerwinski bf90474b74 backend/drm: check for mst: in path property
Instead of checking that the path property is not 0 to determine if the
connector is an MST connector, check if the path contains the mst:
string.

Fixes #1813
2019-09-05 10:25:12 +03:00
Filip Sandborg 734c64a6cc render/gles2: fix calculation for partial gles2 pixel read (#1809) 2019-08-31 23:00:52 +03:00
Drew DeVault a20bb38763 Update version to 0.7.0 2019-08-27 12:32:24 +09:00
Drew DeVault bd6b348feb Add _incr_version to contrib/ 2019-08-27 12:28:37 +09:00
Sebastian Krzyszkowiak cdfe836b03 Revert "wlr_xdg_popup: don't treat all surfaces of grabbing client as grabbing surfaces"
This reverts commit 52037d13f7.

Fixes #1801
2019-08-27 11:12:09 +09:00
Brian Ashworth fa477c77c4 wlr-layer-shell-v1: destroy xdg popups on unmap
This destroys the xdg popups associated with a layer surface when the
layer surface is unmapped. It does not make sense to keep the popups
open when unmapped.
2019-08-24 13:39:38 +09:00
Andri Yngvason e19f48d1e4 backend: touch: Fixup incomplete patch for single touch devices.
All instances of libinput_event_touch_get_slot need to be converted to
libinput_event_touch_get_seat_slot for things to work.
2019-08-19 17:35:30 +03:00
Andri Yngvason 4f4d3cf2a2 backend: touch: Assign good ids to single touch devices
libinput_event_touch_get_slot always returns -1 for single touch devices. Using
libinput_event_touch_get_seat_slot instead ensures that they are assigned actual
slot ids.

Also, this is what Weston does, so this change yields a more consistent
behaviour between different compositors.
2019-08-15 18:46:54 +03:00
Brian Ashworth 9914784594 wlr_xdg_toplevel: reparent on parent unmap
From the xdg-shell specification:
	If the parent is unmapped then its children are managed as
	though the parent of the now-unmapped parent has become the
	parent of this surface. If no parent exists for the now-unmapped
	parent then the children are managed as though they have no
	parent surface.
2019-08-15 11:19:06 +03:00
Simon Ser 8d2ea9544b backend/drm: fix missing plane formats
The loop populating the format list was exiting early if ARGB8888 was found.
2019-08-15 12:33:42 +12:00
Drew DeVault 540e23d102 Revert "render/drm: keep old drm_format if realloc fails"
This reverts commit c1be9b6945.
2019-08-12 19:53:39 +09:00
Antonin Décimo 82f48b8912 examples: remove duplicated condition 2019-08-12 09:37:21 +09:00
Antonin Décimo 8d5f27ef25 xwayland: prevent possible array overrun 2019-08-12 09:37:21 +09:00
Antonin Décimo e7f1aa30dd backend/wayland: check if zxdg_toplevel_decoration_v1 is not NULL 2019-08-12 09:37:21 +09:00
Antonin Décimo 217cf18a4b Avoid loss of a fractional part 2019-08-12 09:37:21 +09:00
Antonin Décimo 39c5d93dea backend/drm: use UINT64_C for uint64_t literals
Prevents an integer promotion bug during the byte-shift.
2019-08-12 09:37:21 +09:00
Antonin Décimo 68b4a5305e backend/session: non-void function should return a value
With assertions disabled, it should make sense to return NULL.
2019-08-12 09:37:21 +09:00
Antonin Décimo 0695324de7 xdg_shell: remove variable self-assignment 2019-08-12 09:37:21 +09:00
Antonin Décimo 8f3d73e3a3 wlr_surface: condition is always false 2019-08-12 09:37:21 +09:00
Antonin Décimo b400c26b23 Simplify check 2019-08-12 09:37:21 +09:00
Antonin Décimo 3fc977d7da Fix memory leak 2019-08-12 09:37:21 +09:00
Antonin Décimo 820800a5ab xcursor: avoid leak and loss of all cursors if cursors realloc fails 2019-08-12 09:37:21 +09:00
Antonin Décimo c1be9b6945 render/drm: keep old drm_format if realloc fails 2019-08-12 09:37:21 +09:00
Sebastian Krzyszkowiak 52037d13f7 wlr_xdg_popup: don't treat all surfaces of grabbing client as grabbing surfaces
Fixes #897
2019-08-12 09:31:49 +09:00
Sebastian Krzyszkowiak 78d96009e4 wlr_xdg_popup: grab touch events alongside pointer and keyboard
Fixes #933
2019-08-12 09:31:49 +09:00
Sebastian Krzyszkowiak 40d17c1305 wlr_seat_touch: add a way for a grab to ignore a touch point 2019-08-12 09:31:49 +09:00
Drew DeVault 94f65e354d Add libinput-1.14 support
This libinput version adds a new tablet tool type.
2019-08-11 19:39:47 +09:00
Drew DeVault 58b2584863 Remove rootston 2019-08-09 08:34:59 +09:00
Sebastian Krzyszkowiak 913cac1835 wlr_input_method_v2: Remove input method's resource from the list on destroy
It's added to manager->input_methods list in manager_get_input_method, but
wasn't removed anywhere, leading to possible use-after-free in
wlr_input_method_manager_v2_destroy.
2019-08-09 08:05:55 +09:00
Rouven Czerwinski 01f903874b Revert "output: atomic mode"
This reverts commit ee5f98ad49.

This intoduced problems where outputs could not be turned off because
they had flips pending.
2019-08-07 16:22:11 +09:00
Rouven Czerwinski 4d36cc86eb backend/drm: destroy output immediately
Instead of waiting for the next pageflip, destroy the output immediately
since we can now handle flips for outputs which no longer exist.
Also demote the missing crtc on flip to debug.

Fixes #1739
2019-08-03 15:44:21 +02:00
Simon Ser 6396710976 xdg-output: add support for xdg-output-unstable-v1 version 3
This adds support for xdg-output-unstable-v1 version 3, added in [1].

The xdg_output.done event is now deprecated and is replaced with
wl_output.done.

[1]: 962dd53537
2019-08-02 10:02:33 -04:00
Simon Ser ee5f98ad49 output: atomic mode, enabled, scale and transform
This commit makes more output properties (mode, enabled, scale and transform)
atomic. This means that they are double-buffered and only applied on commit.

Compositors now need to call wlr_output_commit after setting any of those
properties.

Internally, backends still apply properties sequentially. The behaviour should
be exactly the same as before. Future commits will update some backends to take
advantage of the atomic interface. Some backends are non-atomic by design, e.g.
the X11 backend or the legacy DRM backend.

Updates: https://github.com/swaywm/wlroots/issues/1640
2019-08-02 10:01:29 -04:00
Jason Francis d20aee6c9d export-dmabuf-v1: fix segfault on output disable 2019-08-01 19:00:48 +03:00
Jason Francis 724b5e1b8d screencopy: send failed after output disconnect
This prevents screencopy applications from hanging because a failed
event never got sent when the output was disconnected or disabled after
the call to buffer().
2019-08-01 19:00:48 +03:00
Jason Francis ce3e413e83 screencopy: fix segfault on disabled output
Disconnecting or disabling an output between capture_output() and
ready() could cause either a NULL dereference or an incorrect
attach_render_locks count.
2019-08-01 19:00:48 +03:00
Simon Ser ca45f4490c Remove all wayland-server.h includes
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.

Replacing wayland-server.h with wayland-server-core.h allows us to drop the
WL_HIDE_DEPRECATED declaration.
2019-07-27 15:49:32 -04:00
Simon Ser 76ef089f52 output: drop wlr_output_mode.flags
AFAIK this was always set to zero. Instead, compute wl_output mode flags on the
fly.

Technically this is a breaking change, but I don't think anybody uses this
field.
2019-07-21 12:33:32 -04:00
Manuel Stoeckl bb05617414 Use -fmacro-prefix-map to strip build path
This commit matches sway's 2dc4978d8af326c310057ca8fd22a4c7f5d09335.

To help ensure a reproducible build (when debug info is disabled),
the meson build script now uses the -fmacro-prefix-map command line
argument supported by GCC to strip the build-path dependent bytes
of each __FILE__ string used by wlr_log and related functions.

A rather ugly algorithm is used to compute the relative path between
the build and source folders, because meson has no specific function
for this.

When the compiler does not support -fmacro-prefix-map, fall back
to shifting the start of each __FILE__ string by the length of the
relative path to the source directory.
2019-07-17 21:00:09 -04:00
Guido Günther 9e8f952997 text_input: Don't forget to send enter events
When we move from one surface to another we ought to handle leave
for the old one but also send enter for the new one.
2019-07-17 09:24:13 -04:00
Sebastian Krzyszkowiak 415267ac13 backends/x11: Touch support
Closes #1749
2019-07-17 09:23:10 -04:00
Sebastian Krzyszkowiak 2d4bc66f11 wlr_touch: Declare wlr_touch_impl usage as const
This brings it in line with wlr_keyboard and wlr_pointer
2019-07-17 09:23:10 -04:00
Guido Günther 692a16cef7 layer-shell: Reject requests on gone surfaces
When the surface was destroyed but the resource is still around
we might dereference a null pointer otherwise.
2019-07-16 17:38:11 +03:00
Guido Günther 91752e8285 layer-shell: Remove unused event source 2019-07-16 17:38:11 +03:00
Sebastian Krzyszkowiak 28cc1730e8 xdg_shell(_v6): Take maximize/fullscreen state into account on view init
set_maximized and set_fullscreen calls can come before the view is
constructed and before its signal handlers are registered.
2019-07-15 23:12:50 +03:00
Sebastian Krzyszkowiak 6345000b92 seat: Move focus back to first shell surface when unfocusing layer surface 2019-07-11 12:31:21 -04:00
Guido Günther df3f0ffbb0 wlr_seat_touch: Don't destroy touch point with surface
When the surface is destroyed clear it's reference but wait for the up
event to destroy the touch point via wlr_seat_touch_notify_up().

If the surface is destroyed before the up event we end up with
incomplete sequences sent to the client like

[915821.276] wl_touch@3.down(146, 2475027, wl_surface@38, 0, 236.000000, 515.000000)
[915821.608] wl_touch@3.frame()
[915821.637] wl_touch@3.motion(2475027, 0, 236.000000, 515.000000)
[915821.779] wl_touch@3.frame()

so there's never an up event. While it should be something like

[2461229.051] wl_touch@3.down(81, 3236959, wl_surface@34, 0, 218.000000, 478.000000)
[2461229.435] wl_touch@3.frame()
[2461229.484] wl_touch@3.motion(3236959, 0, 218.000000, 478.000000)
[2461229.636] wl_touch@3.frame()
[2461277.520] wl_touch@3.up(82, 3237007, 0)
[2461277.681] wl_touch@3.frame()

this confuses toolkits intepreting the next down event incorrectly. So
don't destroy the touch point too early.
2019-07-04 14:45:58 +03:00
Manuel Stoeckl c2fb8a84a2 wlr_seat: special-case first serial set use 2019-06-30 15:01:05 -04:00
Manuel Stoeckl 1ef0c03a46 wlr_tablet_v2: Register event serials 2019-06-30 15:01:05 -04:00
Manuel Stoeckl 1d78bae19d wlr_pointer_gestures: Register event serials 2019-06-30 15:01:05 -04:00
Manuel Stoeckl ded441ffd5 wlr_seat: Fix edge cases with serial validation 2019-06-30 15:01:05 -04:00
Manuel Stoeckl edb30a6828 Implement serial validation for selection requests
This change tracks, for each wlr_seat_client, the most recent serial
numbers which were sent to the client. When the client makes a
selection request, wlroots now verifies that the serial number
associated with the selection request was actually provided to that
specific client. This ensures that the client that was most
recently interacted with always has priority for its copy selection
requests, and that no other clients can incorrectly use a larger serial
value and "steal" the role of having the copy selection.

Also, the code used to determine when a given selection is superseded
by a newer request uses < instead of <= to allow clients to make
multiple selection requests with the same serial number and have the
last one hold.

To limit memory use, a ring buffer is used to store runs of sequential
serial numbers, and all serial numbers earlier than the start of the
ring buffer are assumed to be valid. Faking very old serials is
unlikely to be disruptive.

Assuming all clients are correctly written, the only additional
constraint which this patch should impose is that serial numbers
are now bound to seats: clients may not receive a serial number
from an input event on one seat and then use that to request
copy-selection on another seat.
2019-06-30 15:01:05 -04:00
Ilia Bozhinov fb739b8293 output-damage: set needs swap only when we have new damage
An output needs swap when there is new damage. If the damage is only
accumulated from the last frame, we could simply skip drawing.
2019-06-27 21:49:19 +03:00
Rouven Czerwinski d10072e76c backend: drm: switch to pageflip_handler_2
atomic and legacy now both pass the backend as the user data for the
pageflip event. We than retrieve the correct connector by matching on
the crtc_id passed to the page_flip_handler2.

Wlroots also requires the DRM_CRTC_IN_VBLANK_EVENT capability now.

Fixes #1297
2019-06-27 00:17:27 +00:00
Dorota Czaplejewicz 0b1f9439ba virtual_keyboard: Require keymap before accepting keycodes 2019-06-24 17:16:37 +03:00
Scott Anderson 46dc4100d6 backend/drm: Exit-early if 0 crtcs
This fixes an assertion failure if we're using a device that has 0 crtcs
as a renderer.
This would happen on some laptops with discrete GPUs.
2019-06-24 09:09:36 +03:00
Scott Anderson b3f42548d0 backend/drm: Simplify object matching code
We originally used match_obj on planes, but this was largely
unnecessary. Instead, this assigns planes statically at startup.
2019-06-22 11:58:33 +03:00
Greg V d80acadfd8 Support pointer-gestures on Wayland backend 2019-06-21 14:43:28 -04:00
Dorota Czaplejewicz 5027b23dc2 x11: Send a frame event on the pointer after button events
Without the immediate frame event, the button event would not be processed in time: https://source.puri.sm/Librem5/phoc/issues/
2019-06-17 16:01:29 +03:00
Simon Ser ce3f4c3fe1 output: remove wlr_output_impl.transform
The backend doesn't need to handle transform changes, since everything is done
in software. In fact, all of the implementations were all identical and just
set the transform.

We could add support for hardware transforms, but:

- This would require a different field (something like hardware_transform)
- Not all combinations are possible because there often are hardware
  limitations
- The Wayland protocol isn't ready for this (in particular xdg-output, see [1])

This belongs to a different patch series anyway.

[1]: https://patchwork.freedesktop.org/series/52324/
2019-06-16 10:51:49 -04:00
Simon Ser 33127c545b output: reset the pending state on failed commit
It can be surprising for callers to stash pending changes, commit, get a
failure, then set some other pending changes, commit again, and get another
failure because of the previously-pending changes.

Instead, make commit reset the pending state on failure.
2019-06-16 10:51:49 -04:00
Ashkan Kiani 06a13203dd Use a set to track pointer button state.
In addition to `button_count`, we keep track of the current buttons
pressed just as in `wlr_keyboard`.

Add `set_add` and `set_remove` to assist with this. These functions can
only be used with values greater than 0 (such as the button/key masks
for keyboards and pointers).

Partially addresses:
- https://github.com/swaywm/wlroots/issues/1716
- https://github.com/swaywm/wlroots/issues/1593
2019-06-16 00:59:53 +03:00
Scott Anderson b85f0cbff9 Remove WLR_DRM_NO_ATOMIC_GAMMA workaround
This is fixed on amdgpu, so we don't need this anymore.
2019-06-11 08:52:34 +03:00
Simon Ser d201fc3506 backend/drm: add support for multiplanar BOs 2019-06-07 09:06:11 -04:00
Simon Ser e8057bb60c backend/drm: fallback to drmModeAddFB2 2019-06-07 09:06:11 -04:00
Simon Ser e07ffaa249 output-damage: support direct scan-out
In case direct scan-out is used, we still need to accumulate damage for the
render-buffers.
2019-06-07 09:06:11 -04:00
Simon Ser 6c659da98b output: introduce wlr_output_lock_attach_render
This allows screen shooters and screen grabbers to ensure rendering will be
used instead of direct scan-out.
2019-06-07 09:06:11 -04:00
Simon Ser e554c593f9 output: refuse to scan-out if software cursors are used 2019-06-07 09:06:11 -04:00
Simon Ser 67cd84de45 rootston: add support for direct scan-out 2019-06-07 09:06:11 -04:00
Simon Ser d1766547bd backend/drm: reject DMA-BUFs with flags 2019-06-07 09:06:11 -04:00
Simon Ser 44ba603c0e backend/drm: hold buffers while scanning out 2019-06-07 09:06:11 -04:00
Simon Ser ff6b352d75 output: save buffer in pending state 2019-06-07 09:06:11 -04:00
Simon Ser afe7b207d5 output: fix attach_buffer semantics 2019-06-07 09:06:11 -04:00
Simon Ser 1d222309b8 output: change set_dmabuf to attach_buffer 2019-06-07 09:06:11 -04:00
Simon Ser 493804e421 buffer: add wlr_buffer_get_dmabuf 2019-06-07 09:06:11 -04:00
Simon Ser 6dbdf5db34 render/dmabuf: add wlr_dmabuf_attributes_copy 2019-06-07 09:06:11 -04:00
Simon Ser 5c78f1b0d5 backend/drm: strip alpha channel on scan-out 2019-06-07 09:06:11 -04:00
Simon Ser e516ea4c79 backend/drm: check format when scanning out DMA-BUF 2019-06-07 09:06:11 -04:00
emersion 96d6fde5dc backend/drm: add basic support for direct scan-out 2019-06-07 09:06:11 -04:00
Simon Ser 3dec88e455 Remove orbital screenshooter and gamma-control
These are undocumented, outdated protocols that have a better wlr-protocols
equivalent.
2019-06-02 09:30:47 -04:00
Scott Anderson abddd7b4db
Merge pull request #1713 from Emantor/fix/legacy_null_modeset
drm: legacy: issue a NULL modeset on disable
2019-06-02 08:28:47 +00:00
Rouven Czerwinski 96e9c0f9c8 drm: legacy: issue a NULL modeset on disable
The DRM subsystem needs a NULL modeset for connectors which disappear
from the system to disable the hardware pipes, otherwise the pixels get
rendered but are sent nowhere.

The atomic backend does the equivalent by removing the properties and
issuing a commit.

Fixes #1706
2019-06-02 09:50:40 +02:00
Brian Ashworth 6dfe238ff1 zwp_virtual-keyboard: fix mmap error handling
If mmap fails, it will return MAP_FAILED not NULL. Since the error
handling was incorrectly checking for NULL, MAP_FAILED was being passed
to xkb_keymap_new_from_string, on mmap failure, causing a segfault.
This just fixes the error checking.
2019-05-31 07:58:31 +03:00
Rouven Czerwinski a5e32f652b wlr_output: remove idle_done on output destroy
If an output is destroyed while an idle_done event is scheduled, it
results in the following Address Sanitizer Output:

==1469==ERROR: AddressSanitizer: heap-use-after-free on address 0x6170000bb668 at pc 0x7f49aaa0c348 bp 0x7ffed5da35b0 sp 0x7ffed5da35a0
WRITE of size 8 at 0x6170000bb668 thread T0
    #0 0x7f49aaa0c347 in schedule_done_handle_idle_timer ../subprojects/wlroots/types/wlr_output.c:265
    #1 0x7f49aa2f875b in wl_event_loop_dispatch_idle (/usr/lib/libwayland-server.so.0+0xa75b)
    #2 0x7f49aa2f8815 in wl_event_loop_dispatch (/usr/lib/libwayland-server.so.0+0xa815)
    #3 0x7f49aa2f739b in wl_display_run (/usr/lib/libwayland-server.so.0+0x939b)
    #4 0x556622dadd51 in server_run ../sway/server.c:216
    #5 0x556622dac25d in main ../sway/main.c:397
    #6 0x7f49aa0d0ce2 in __libc_start_main (/usr/lib/libc.so.6+0x23ce2)
    #7 0x556622d8d09d in _start (/usr/local/bin/sway+0x3909d)

0x6170000bb668 is located 488 bytes inside of 672-byte region [0x6170000bb480,0x6170000bb720)
freed by thread T0 here:
    #0 0x7f49aabc8f89 in __interceptor_free /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:66
    #1 0x7f49aa968fc2 in drm_connector_destroy ../subprojects/wlroots/backend/drm/drm.c:829
    #2 0x7f49aaa0cc52 in wlr_output_destroy ../subprojects/wlroots/types/wlr_output.c:357
    #3 0x7f49aa96d2e9 in scan_drm_connectors ../subprojects/wlroots/backend/drm/drm.c:1265
    #4 0x7f49aa961a59 in drm_invalidated ../subprojects/wlroots/backend/drm/backend.c:135
    #5 0x7f49aaa2e1e9 in wlr_signal_emit_safe ../subprojects/wlroots/util/signal.c:29
    #6 0x7f49aa98319f in udev_event ../subprojects/wlroots/backend/session/session.c:52
    #7 0x7f49aa2f87f1 in wl_event_loop_dispatch (/usr/lib/libwayland-server.so.0+0xa7f1)

previously allocated by thread T0 here:
    #0 0x7f49aabc95a1 in __interceptor_calloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:95
    #1 0x7f49aa96b7a2 in scan_drm_connectors ../subprojects/wlroots/backend/drm/drm.c:1114
    #2 0x7f49aa961a59 in drm_invalidated ../subprojects/wlroots/backend/drm/backend.c:135
    #3 0x7f49aaa2e1e9 in wlr_signal_emit_safe ../subprojects/wlroots/util/signal.c:29
    #4 0x7f49aa98319f in udev_event ../subprojects/wlroots/backend/session/session.c:52
    #5 0x7f49aa2f87f1 in wl_event_loop_dispatch (/usr/lib/libwayland-server.so.0+0xa7f1)

SUMMARY: AddressSanitizer: heap-use-after-free ../subprojects/wlroots/types/wlr_output.c:265 in schedule_done_handle_idle_timer
Shadow bytes around the buggy address:
  0x0c2e8000f670: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2e8000f680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2e8000f690: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c2e8000f6a0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c2e8000f6b0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x0c2e8000f6c0: fd fd fd fd fd fd fd fd fd fd fd fd fd[fd]fd fd
  0x0c2e8000f6d0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c2e8000f6e0: fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2e8000f6f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2e8000f700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2e8000f710: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb

Remove the idle_done idle timer when the output is destroyed
2019-05-30 09:14:46 +03:00
Scott Anderson 0ab1bb623e
Merge pull request #1705 from rindeal/patch-2
session/logind: check for XDG_SESSION_ID first
2019-05-22 23:42:46 +00:00
Jan Chren 151b7d1d94 session/logind: check for XDG_SESSION_ID first
In order to support compositors running as systemd user units without display manager,
a mechanism for specifying session ID exactly must exist.

Checking for `XDG_SESSION_ID` mimics loginctl behaviour e95be7def2/src/login/loginctl.c (L856).
2019-05-22 15:43:07 +02:00
Scott Anderson aa39dbd1e7
Merge pull request #1704 from rindeal/patch-2
session/logind: specify seat exactly
2019-05-22 12:50:47 +00:00
Jan Chren e04e1ba197
session/logind: specify seat exactly
"/org/freedesktop/login1/seat/self" path triggers seat-finding code path in logind,
which currently relies on getting the session based on caller's PID.
This behaviour is deprecated in logind as it doesn't work eg. with systemd user units,
which run outside of user session.

We check for "seat0" in logind_change_vt() already as introduced in 47985d2dc5,
so hard-coding it here is not a problem, otherwise sd_session_get_seat() could be used.
2019-05-22 14:43:13 +02:00
Silvan Jegen 461c4f58a6 rootston: remove duplicated include 2019-05-19 19:50:14 +03:00
Scott Anderson 522ddd93f1
Merge pull request #1696 from RedSoxFan/logind-stay-active-on-gone
session/logind: keep active for pause_device gone
2019-05-15 04:34:08 +00:00
Michiel a68c7c0c8d Fixes #1689 RDP RemoteFX crash
When using the rdp backend and connecting with xfreerdp ... --rfx, wlroots
crashes in backend/rdp/output.c while attempting to realloc(..., 0).

This commit guards against that and instead returns true, resulting in
no rfx message being sent. This prevents the crash and appears to work, but
it's not obvious if this is correct from a specification perspective.
2019-05-13 23:30:38 +03:00
Ilia Bozhinov 03346cb28f output: clear output->damage on successful commit 2019-05-13 18:45:22 +03:00
Brian Ashworth 90c284bded session/logind: keep active for pause_device gone
This appears to be a quick fix for compositors freezing when a dock is
disconnected. Disconnection of the dock is causing `pause_device` for
the DRM devices associated with the dock. Since these devices major
number is `DRM_MAJOR`, the session was being set to inactive. This just
makes it so the session is not set to inactive when the device's state
is `gone`.
2019-05-13 10:43:35 -04:00
Simon Ser 107a1789ea render/gles2: print GL_RENDERER
This is often the name of the GPU and can help debugging graphics issues.
2019-05-07 10:51:52 -06:00
Simon Ser 947d5ff481 backend/wayland: remove wl_shm
We bind to it but never use it.
2019-05-06 20:51:57 +03:00
Simon Ser 292d20e4c1 backend/wayland: use xdg-decoration-unstable-v1
This allows the toplevel to have proper decorations on compositors that support
xdg-decoration-unstable-v1.
2019-05-06 10:34:41 -06:00
Jan Beich 6ae6b5dbb6 Test RDP backend on FreeBSD as well 2019-05-03 20:00:45 +03:00
Drew DeVault c0305f4f86 Update version to 0.6.0 2019-05-03 11:55:46 -04:00
Simon Ser cf3b083c32 ci: enable Meson auto features
This makes it so we don't miss build failures because the CI misses a
dependency.
2019-04-29 10:49:05 -06:00
Simon Ser 937e0e7937 backend/noop: disallow rendering
Closes: https://github.com/swaywm/wlroots/issues/1662
2019-04-29 10:48:51 -06:00
Jan Beich 27a87ac586 Switch to system epoll-shim.pc in FreeBSD build 2019-04-29 18:51:47 +03:00
Ilia Bozhinov 22dd7d3731 examples: add fullscreening to foreign-toplevel.c 2019-04-29 00:00:53 +03:00
Ilia Bozhinov 8cc0859814 rootston: add support for foreign-toplevel fullscreening 2019-04-29 00:00:53 +03:00
Ilia Bozhinov 4e6c17a7c9 foreign-toplevel: support fullscreen state and request 2019-04-29 00:00:53 +03:00
Simon Ser a656e486f4 seat: fallback to v6 if libwayland 1.17 isn't available
See https://github.com/swaywm/wlroots/pull/1675#issuecomment-487300445
2019-04-27 11:26:34 -06:00
Simon Ser d7ef3a1f38 xdg-output: schedule a wl_output.done event on update
Xwayland expects an xdg_output.done event to always be sent with a
wl_output.done event.
2019-04-27 10:02:21 -06:00
Simon Ser bde5a1f4ea output: introduce wlr_output_schedule_done
This commit introduces wlr_output_schedule_done and refactors the mechanism
used to send wl_output events to clients.

wlr_output_schedule_done schedules a wl_output.done event. This allows clients
to see wlr_output property changes as atomic.

This function is also useful for add-on interfaces like xdg_output which need
to trigger a wl_output.done event to apply their new state.
2019-04-27 10:02:21 -06:00
Simon Ser 8b1220f5a3 rootston: 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.

Fixes: 57d32d03a8
2019-04-27 09:59:01 -06:00
Simon Ser 1dbece74fb output: disambiguate the two types of damage
See https://github.com/swaywm/wlroots/issues/1665
2019-04-26 10:08:08 -06:00
Simon Ser 57d32d03a8 rootston: don't submit too much damage
We only need to damage the parts of the screen that changed since last frame,
we don't need to accumulate damage from previous buffers.

We still need to re-render the accumulated damage.

Fixes https://github.com/swaywm/wlroots/issues/1665
2019-04-26 10:08:08 -06:00
Simon Ser d6e250b389 render/egl: fix swapping with no damage
According to the spec:

> If <n_rects> is 0 then <rects> is ignored and the entire
> surface is implicitly damaged and the behaviour is equivalent
> to calling eglSwapBuffers.

When we want to swap with an empty damage region, set the damage to a single
empty rectangle.
2019-04-26 10:08:08 -06:00
Drew DeVault 736632ad4e Remove wlr_wl_shell 2019-04-26 18:23:25 +03:00
Scott Anderson a671fc51d2 Advertise wl_seat version 7
This does not require any code changes, as we already copy the keymap
data separately for each client.

For details, see 905c0a341d
2019-04-26 07:57:37 -06:00
Simon Ser 8d3369d70f output-damage: fix segfault after destroy
Fixes: 5e6766a165
2019-04-25 11:39:51 +03:00
Simon Ser 933208837d backend/wayland: fix wlr_wl_pointer use-after-free 2019-04-23 14:36:12 -06:00
Simon Ser f42816ce3f render/egl: fix zero-length VLA 2019-04-23 14:36:02 -06:00
Simon Ser 20690346c7 output: rename needs_commit to needs_frame
This new name makes more sense, since it is a request from the backend to get
a new frame. In the future a commit may not convey a new frame.
2019-04-23 14:34:30 -06:00
Simon Ser 5e6766a165 output-damage: refactor API
wlr_output_damage_make_current has been renamed to
wlr_output_damage_attach_render, since it's just a wrapper for
wlr_output_attach_render.

wlr_output_damage_swap_buffers has been removed completely. Instead,
wlr_output_damage now listens to successful wlr_output commits and updates its
internal state accordingly.
2019-04-23 14:34:30 -06:00
Simon Ser 31dcecbfa9 output: rename swap_buffers event to precommit
Also remove damage from the event data since it's no longer tied to commits.
2019-04-23 14:34:30 -06:00
Simon Ser 9a0f8a194c output: refactor backend API
This updates the backend part of the output API. This is mostly renaming:
make_current becomes attach_render and swap_buffers becomes commit.

This also fixes the RDP backend to support NULL damage.
2019-04-23 14:34:30 -06:00
Simon Ser 23e37e7b1d output: refactor frame submission API
This is necessary for direct scan-out and other upcoming features. This patch
changes the output API to look like the wl_surface API.

Outputs now have some double-buffered state: the frame to be submitted
(currently only wlr_renderer frames are supported) and the damaged region.
To attach a pending frame, use wlr_output_attach_render. To set the pending
damaged region, use wlr_output_set_damage.

To submit the pending state, call wlr_output_commit. This will submit the
pending frame to the backend.

To migrate from the old API to the new one:

- Replace wlr_output_make_current calls by wlr_output_attach_render
- Replace wlr_output_swap_buffers calls by wlr_output_set_damage and
  wlr_output_commit
2019-04-23 14:34:30 -06:00
Simon Ser 56ceed38bf rootston: use wlr_output_preferred_mode
Also fix rootston setting the preferred mode when another mode is specified in
the config file.
2019-04-22 11:15:38 -06:00
Simon Ser 8acbf449cc output: introduce wlr_output_preferred_mode 2019-04-22 11:15:38 -06:00
Alyssa Ross 95b22619e0 Fix missing headers when building without X11
The deleted includes are redundant, because other headers will include
the necessary files. Additionally, they cause build failures, because
including EGL/egl.h or EGL/eglext.h directly, instead of through
wlr/render/egl.h or wlr/render/interface.h, will mean that
MESA_EGL_NO_X11_HEADERS will not have been defined, and so the EGL
headers will attempt to pull in unnecessary X11 headers that may not
exist on the system.

For the headers produced by glgen.sh, the includes couldn't simply be
deleted, because no other header would include the EGL headers. Neither
wlr/render/egl.h or wlr/render/interface.h felt appropriate to include,
so I opted instead to copy the MESA_EGL_NO_X11_HEADERS definition before
the EGL includes.
2019-04-22 00:04:08 +03:00
Simon Ser 4207f05030 data-device: ignore accept for selection offers
It doesn't make sense for clients to send "accept" requests to offers that
aren't drag-and-drop. I discussed with Daniel Stone to make it a protocol
error [1] but too many clients send it (e.g. GTK+). Let's just log it for now.

[1]: https://gitlab.freedesktop.org/wayland/wayland/merge_requests/11#note_149710
2019-04-19 13:24:41 -06:00
Ilia Bozhinov d6615e0e84 idle: enable the compositors to add custom idle timeouts (#1655)
* idle: enable the compositors to add custom idle timeouts

* idle: add a private constructor which also creates the resource

* idle: move resource creation to the idle implementation callback
2019-04-17 08:48:43 +03:00
Ilia Bozhinov dc5d1d08ef output: do nothing in output_set_image if backend has no renderer
This is useful when using the noop backend for example.
2019-04-14 19:35:23 +03:00
Ilia Bozhinov 7a2f929201 rootston: remove disabled outputs from the output layout
We should also be careful when using wlr_output_layout_get_box(), since
it may return null.
2019-04-13 19:31:37 +03:00
emersion 1515c56cae output: remove lx, ly
Fixes https://github.com/swaywm/wlroots/issues/1610
2019-04-13 08:31:30 -06:00
emersion 930e37eae9 output-management-v1: add more docs 2019-04-11 09:19:57 -06:00
emersion a800aa3fb4 rootston: disable then enable outputs when applying output-management state 2019-04-11 09:19:57 -06:00
Guido Günther 524f5c8425 Emit preferred mode 2019-04-11 09:19:57 -06:00
emersion ab3446091b output-management-v1: update protocol, add set_custom_mode 2019-04-11 09:19:57 -06:00
emersion fc0ba3ea22 output-management-v1: support outputs without modes 2019-04-11 09:19:57 -06:00
emersion 81e1489e79 output-management-v1: fix wl_fixed_t conversion 2019-04-11 09:19:57 -06:00
emersion 8136605cfb output-management-v1: support applying configuration 2019-04-11 09:19:57 -06:00
emersion d71ebde545 output-management-v1: add support for position, transform and scale 2019-04-11 09:19:57 -06:00
emersion ee77a65fe3 rootston: update output-management-v1 state when output is modeset 2019-04-11 09:19:57 -06:00
emersion ef68d7d4d1 output-management-v1: add support for modes 2019-04-11 09:19:57 -06:00
emersion 54d6ba78c3 rootston: add output-management-v1 support 2019-04-11 09:19:57 -06:00
emersion e873c652bf output-management-v1: various fixes 2019-04-11 09:19:57 -06:00
emersion d695003498 Add apply and test events to manager 2019-04-11 09:19:57 -06:00
emersion 0b64ecc162 Split wlr_output_configuration_head_v1 2019-04-11 09:19:57 -06:00
emersion 3a233b3fcc Add support for wlr-output-management-unstable-v1 2019-04-11 09:19:57 -06:00
Jan Beich b6d0de177a backend: unbreak on 32-bit architectures
backend/headless/output.c:132:3: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat]
                ++backend->last_output_num);
                ^~~~~~~~~~~~~~~~~~~~~~~~~~
backend/noop/output.c:72:3: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat]
                ++backend->last_output_num);
                ^~~~~~~~~~~~~~~~~~~~~~~~~~
backend/wayland/output.c:294:3: error: format specifies type 'unsigned long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat]
                ++backend->last_output_num);
                ^~~~~~~~~~~~~~~~~~~~~~~~~~
backend/x11/output.c:150:3: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat]
                ++x11->last_output_num);
                ^~~~~~~~~~~~~~~~~~~~~~
2019-04-08 14:03:15 -06:00
Ilia Bozhinov 670c787fa7 noop: implement setting a custom mode 2019-04-08 08:14:10 -06:00
Drew DeVault fd0d7d0907 Add FreeRDP backend for remote desktop support 2019-04-08 08:09:07 -06:00
Guido Günther 9faea17c73
Merge pull request #1642 from emersion/format-set
Introduce wlr_format_set
2019-04-08 10:58:18 +02:00
emersion 43bd1d807a util/log: setup wayland log handler 2019-04-07 16:09:00 -06:00
emersion e42178d03f
render: switch wlr_renderer to wlr_drm_format_set 2019-04-01 19:18:04 +03:00
Scott Anderson c01b81c99c
render: introduce wlr_drm_format_set
This types adds a container for formats + modifiers.

A list that is of [format [modifier]] was chosen instead of
[format modifer] because that is how GBM accepts them.

Co-Authored-By: emersion <contact@emersion.fr>
2019-04-01 19:15:56 +03:00
Markus Ongyerth 09b2833dcd send proximity_out in proximity in for tablet_v2
When the proximity_in event is sent for tablet_v2 and there's already a
surface that currently has tablet (tool) focus, it should be told that
this is no longer the case, so we need to send the proximity_out event.
2019-03-31 20:16:03 +03:00
Stuart Dilts 08454adada types/meson.build: Add compile args for libinput to lib_wlr_types (#1636)
* Add compile args for libinput to lib_wlr_types

Fixes the build on openSUSE Tumbleweed

* Remove libinput include from wlr_tablet_v2.c

+ libinput isn't used in the file
+ Also remove libinput dependency from types/meson.build
2019-03-27 19:31:20 +02:00
Brian Ashworth 17f688735f wlr_xdg_toplevel_v6: store pending fullscreen output
Since the fullscreen request may be made before the toplevel's surface
is mapped, the requested fullscreen output needs to be stored so it
can be retrieved on map (along with the existing fullscreen property).

This commit makes the required changes for wlr_xdg_toplevel_v6.
2019-03-27 10:04:10 +02:00
Brian Ashworth 4e614683b7 wlr_xdg_toplevel: store pending fullscreen output
Since the fullscreen request may be made before the toplevel's surface
is mapped, the requested fullscreen output needs to be stored so it
can be retrieved on map (along with the existing fullscreen property).

This commit makes the required changes for wlr_xdg_toplevel.
2019-03-27 10:04:10 +02:00
Guido Günther b4f821ca31 rootston: Also iterate layer shell popups
Broken by 62fd03a7be

Closes: #1631
2019-03-24 09:19:36 -06:00
Ilia Bozhinov 4281c8c566 meson.build: require libinput >= 1.9.0
We use LIBINPUT_SWITCH_TABLET_MODE, which is introduced in 1.9.0
2019-03-22 17:14:04 +02:00
Guido Günther cd60f40bbb wlr_output: Add preferred property (#1625)
* wlr_output: Indicate modes link

* wlr_output: Introduce preferred flag

This indicates an outputs preferred mode.

* drm: Set preferred flag for an outputs preferred mode
2019-03-21 22:12:43 +02:00
Ryan Walklin 4453757fc9 s/lid_switch/switch_device
Rename lid_switch to switch_device to disambiguate lid and tablet mode switches.
2019-03-19 22:45:58 -04:00
Brian Ashworth 6b7f5e4010 backend/noop: improve output number handling
This improves the way the output numbers are handled for the noop
backend. Instead of using the number of active outputs plus one, the
last used number is stored and new outputs will increment it. This
fixes the situation where you start with one output, create a second,
close the first, and create a third. Without this, both outputs will be
NOOP-2, which causes an issue since the identifier will also be
identical. With this, the last output is NOOP-3 and the outputs can be
distinguished.
2019-03-15 18:38:12 +02:00
Brian Ashworth c97f0eb0f2 backend/headless: improve output number handling
This improves the way the output numbers are handled for the headless
backend. Instead of using the number of active outputs plus one, the
last used number is stored and new outputs will increment it. This
fixes the situation where you start with one output, create a second,
close the first, and create a third. Without this, both outputs will be
HEADLESS-2, which causes an issue since the identifier will also be
identical. With this, the last output is HEADLESS-3 and the outputs can
be distinguished.
2019-03-15 18:37:56 +02:00
Brian Ashworth b135599e5a backend/x11: improve output number handling
This improves the way the output numbers are handled for the x11
backend. Instead of using the number of active outputs plus one, the
last used number is stored and new outputs will increment it. This
fixes the situation where you start with one output, create a second,
close the first, and create a third. Without this, both outputs will be
X11-2, which causes an issue since the identifier will also be
identical. With this, the last output is X11-3 and the outputs can be
distinguished.
2019-03-15 18:37:35 +02:00
Brian Ashworth 67523fb228 backend/wayland: improve output number handling
This improves the way the output numbers are handled for the wayland
backend. Instead of using the number of active outputs plus one, the
last used number is stored and new outputs will increment it. This
fixes the situation where you start with one output, create a second,
close the first, and create a third. Without this, both outputs will be
`WL-2`, which causes an issue since the identifier will also be
identical. With this, the last output is `WL-3` and the outputs can be
distinguished.
2019-03-15 09:43:40 +02:00
emersion 408eca7dfa meson: remove -Wredundant-decls
This is causing issues with wayland-scanner generated files. The client and
server headers are declaring the same structs. We include both in the Wayland
backend.

See https://gitlab.freedesktop.org/wayland/wayland/issues/82
2019-03-12 08:16:07 -06:00
Drew DeVault c9137cba69 Update version to 0.5.0 2019-03-11 10:40:18 -04:00
Brian Ashworth aa5c369910 wlr_output_layout_get_box: handle empty layout
If there were no outputs in the output layout,
wlr_output_layout_get_box would return the box:

{
  .x = INT_MIN,
  .y = INT_MIN,
  .width = INT_MIN - INT_MAX,
  .height = INT_MIN - INT_MAX
}

which results in an integer underflow for both the width and height.

This changes the logic to have the box be all zeroes, since an empty
output layout does not have a width or height and the location of
something without a size is irrelevant so this just uses the origin.
2019-03-08 09:45:22 +01:00
Niklas Schulze 2baad6eba6 backend/session: Allow setting a custom tty via WLR_DIRECT_TTY 2019-03-06 13:20:51 +01:00
Scott Anderson a3c31bb875
Merge pull request #1606 from emersion/xwayland-dev-null
xwayland: set CLOEXEC on /dev/null FD
2019-03-06 02:53:12 +00:00
emersion 8363ca8c9f
xwayland: set CLOEXEC on /dev/null FD
This avoids leaking the FD to Xwayland and its children.
2019-03-06 00:32:24 +01:00
Scott Anderson 6a8f17b5f6 backend/drm: Don't fail on failing to find overlay format
Some hardware exists which doesn't support XRGB/ARGB overlays, and we
aren't even using overlay planes, so don't fail on trying to find a
format.
2019-03-05 23:15:20 +01:00
Scott Anderson 680c4c573c
Merge pull request #1604 from emersion/direct-session-cloexec
backend/session: open TTY with O_CLOEXEC for direct session
2019-03-05 21:03:23 +00:00
emersion d02548d87a
backend/session: open TTY with O_CLOEXEC for direct session 2019-03-05 19:19:13 +01:00
emersion 30d3426164 seat: add debug logs when validating grab serials
Makes it easier to debug when something goes wrong, e.g. button_count stuck
to 2 because the compositor ate a button release event.
2019-03-04 21:27:14 -07:00
emersion 9601019192 xwayland: don't set DISPLAY
Let the compositor set it. This allows for multiple Xwayland instances to run
at the same time.

Fixes https://github.com/swaywm/wlroots/issues/1442
2019-03-04 12:54:06 -07:00
Ilia Bozhinov fb106eb979 xwm: fix typos in WM_NORMAL_HINTS handling 2019-03-04 18:49:39 +01:00
Ilia Bozhinov c9b9e48525 xwm: use min size as base size hint if it is missing and vice versa
This is what ICCCM states that a WM should do.
2019-03-03 14:13:55 -07:00
emersion 6a60dafe59 rootston: fix input events for rotated views 2019-03-02 09:37:05 -07:00
emersion c2178d51a8 rootston: split rendering code into render.c 2019-03-02 09:37:05 -07:00
emersion 242e9e3bf0 rootston: fix Xwayland children rendering when fullscreen 2019-03-02 09:37:05 -07:00
emersion bfaf06f04b rootston: fix rotated views rendering 2019-03-02 09:37:05 -07:00
emersion 62fd03a7be rootston: refactor rendering
This implements rootston surface iterators to ease rendering, sending
frame/presentation events and accumulating damage.
2019-03-02 09:37:05 -07:00
emersion 8efeca528f backend/session: add noop session
This is the first step towards being able to run via DRM leasing and on render
nodes.

Test with:

    export WLR_BACKENDS=drm
    export WLR_SESSION=noop
    export WLR_DRM_DEVICES=/dev/dri/renderD128
2019-03-02 08:40:27 -07:00
emersion 755a1c9138 tinywl: send pointer frame events
Fixes https://github.com/swaywm/wlroots/issues/1544
2019-03-02 08:39:41 -07:00
Ian Fan 2e1dd4ae36 seat: fix remaining wlr_button_state enum rename 2019-03-01 21:49:04 +01:00
emersion e8f012c993 seat: only store serial if pressing a button
The grab serial can be used to start a pointer grab. A button pressed event
should be used for this purpose.

Thus, we should only save the grab serial if it's the first button pressed
event we send. This commit makes it so the serial is not saved if a button is
released while another button is still pressed.
2019-03-01 12:36:19 -07:00
emersion 2fde5c95d8 seat: use wlr_button_state enum instead of uint32_t 2019-03-01 12:36:03 -07:00
emersion 5445d8aad0 meson: enable more compiler warnings 2019-03-01 09:20:23 +01:00
emersion 4135fafecd seat: guard against button count corruption
This is still a compositor bug, and bad events will be sent to clients. We'll
need to track each button separately to handle this in wlroots.
2019-02-28 23:00:57 -05:00
Guido Günther f8fcd7f06a Remove glEGLImageTargetTexture2DOES
It's declared via render/glapi.txt.
2019-02-28 21:24:22 +01:00
Guido Günther cf9607f282 wlr_xdg_shell: Remove redundant declaration in the same file 2019-02-28 21:24:22 +01:00
Guido Günther 7da9af31e8 rootston/view: Remove redundant declaration in the same file 2019-02-28 21:24:22 +01:00
Guido Günther d3b48dfcae Unbreak build with '-Wstrict-prototypes' 2019-02-28 16:41:23 +01:00
Drew DeVault b46e097fe2 Update version to 0.4.1 2019-02-27 10:56:05 -05:00
Guido Günther 65f1ec1d5e rootston: Make add_{switch,binding}_config static 2019-02-26 15:48:59 +01:00
athrungithub 2000d52405 clang compile fix #1572
clang consider error no enum handled,
in BSD and Linux
2019-02-26 08:05:17 +01:00
emersion 0b33643175
Bump version to 0.4 2019-02-25 23:23:21 +01:00
438 changed files with 39383 additions and 27811 deletions

View File

@ -2,21 +2,35 @@ image: alpine/edge
packages:
- eudev-dev
- ffmpeg-dev
- libcap-dev
- glslang
- libinput-dev
- libxkbcommon-dev
- mesa-dev
- meson
- pixman-dev
- vulkan-headers
- vulkan-loader-dev
- wayland-dev
- wayland-protocols
- xcb-util-image-dev
- xcb-util-renderutil-dev
- xcb-util-wm-dev
- xwayland
- libseat-dev
sources:
- https://github.com/swaywm/wlroots
- https://gitlab.freedesktop.org/wlroots/wlroots.git
tasks:
- setup: |
cd wlroots
meson build
meson build --fatal-meson-warnings --default-library=both -Dauto_features=enabled -Dxcb-errors=disabled
- build: |
cd wlroots
ninja -C build
sudo ninja -C build install
- build-features-disabled: |
cd wlroots
meson build --reconfigure -Dauto_features=disabled
ninja -C build
- tinywl: |
cd wlroots/tinywl
make

View File

@ -2,7 +2,6 @@ image: archlinux
packages:
- clang
- ffmpeg
- libcap
- libinput
- libxkbcommon
- mesa
@ -10,17 +9,37 @@ packages:
- pixman
- wayland
- wayland-protocols
- xcb-util-errors
- xcb-util-image
- xcb-util-renderutil
- xcb-util-wm
- xorg-xwayland
- seatd
- vulkan-icd-loader
- vulkan-headers
- glslang
sources:
- https://github.com/swaywm/wlroots
- https://gitlab.freedesktop.org/wlroots/wlroots.git
tasks:
- setup: |
cd wlroots
CC=gcc meson build-gcc
CC=clang meson build-clang
CC=gcc meson build-gcc --fatal-meson-warnings --default-library=both -Dauto_features=enabled --prefix /usr -Db_sanitize=address,undefined
CC=clang meson build-clang --fatal-meson-warnings -Dauto_features=enabled
- gcc: |
cd wlroots/build-gcc
ninja
sudo ninja install
cd ../tinywl
CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" make
- clang: |
cd wlroots/build-clang
ninja
- smoke-test: |
cd wlroots/tinywl
sudo modprobe vkms
udevadm settle
export WLR_BACKENDS=drm
export WLR_RENDERER=pixman
export WLR_DRM_DEVICES=/dev/dri/by-path/platform-vkms-card
sudo chmod ugo+rw /dev/dri/by-path/platform-vkms-card
sudo -E seatd-launch -- ./tinywl -s 'kill $PPID' || [ $? = 143 ]

View File

@ -1,42 +1,38 @@
image: freebsd/latest
packages:
- devel/evdev-proto
- devel/libepoll-shim
- devel/libudev-devd
- devel/meson # implies ninja
- devel/pkgconf
- graphics/libdrm
- graphics/mesa-libs
- graphics/png
- graphics/wayland
- graphics/wayland-protocols
- multimedia/ffmpeg
- x11/libX11
- x11/libinput
- x11/libxcb
- x11/libxkbcommon
- x11/pixman
- x11/xcb-util-errors
- x11/xcb-util-wm
- devel/evdev-proto
- devel/libepoll-shim
- devel/libudev-devd
- devel/meson # implies ninja
- devel/pkgconf
- graphics/glslang
- graphics/libdrm
- graphics/mesa-libs
- graphics/png
- graphics/vulkan-headers
- graphics/vulkan-loader
- graphics/wayland
- graphics/wayland-protocols
- multimedia/ffmpeg
- x11/libX11
- x11/libinput
- x11/libxcb
- x11/libxkbcommon
- x11/pixman
- x11/xcb-util-errors
- x11/xcb-util-renderutil
- x11/xcb-util-wm
- x11-servers/xwayland
- sysutils/seatd
- gmake
sources:
- https://github.com/swaywm/wlroots
- https://gitlab.freedesktop.org/wlroots/wlroots.git
tasks:
- 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
- wlroots: |
cd wlroots
meson build
ninja -C build
- wlroots: |
cd wlroots
meson build --fatal-meson-warnings -Dauto_features=enabled
ninja -C build
sudo ninja -C build install
- tinywl: |
cd wlroots/tinywl
gmake

1
.gitignore vendored
View File

@ -7,4 +7,3 @@ build/
build-*/
wayland-*-protocol.*
wlr-example.ini
rootston.ini

7
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,7 @@
include: https://git.sr.ht/~emersion/dalligi/blob/master/templates/multi.yml
alpine:
extends: .dalligi
archlinux:
extends: .dalligi
freebsd:
extends: .dalligi

View File

@ -1,22 +1,21 @@
# Contributing to wlroots
Contributing just involves sending a pull request. You will probably be more
successful with your contribution if you visit
[#sway-devel](https://webchat.freenode.net/?channels=sway-devel) on
irc.freenode.net upfront and discuss your plans.
Contributing just involves sending a merge request. You will probably be more
successful with your contribution if you visit [#sway-devel on Libera Chat]
upfront and discuss your plans.
Note: rules are made to be broken. Adjust or ignore any/all of these as you see
fit, but be prepared to justify it to your peers.
## Pull Requests
## Merge Requests
If you already have your own pull request habits, feel free to use them. If you
If you already have your own merge request habits, feel free to use them. If you
don't, however, allow me to make a suggestion: feature branches pulled from
upstream. Try this:
1. Fork wlroots
2. `git clone https://github.com/username/wlroots && cd wlroots`
3. `git remote add upstream https://github.com/swaywm/wlroots`
2. `git clone git@gitlab.freedesktop.org:<username>/wlroots.git && cd wlroots`
3. `git remote add upstream https://gitlab.freedesktop.org/wlroots/wlroots.git`
You only need to do this once. You're never going to use your fork's master
branch. Instead, when you start working on a feature, do this:
@ -25,42 +24,74 @@ branch. Instead, when you start working on a feature, do this:
2. `git checkout -b add-so-and-so-feature upstream/master`
3. Add and commit your changes
4. `git push -u origin add-so-and-so-feature`
5. Make a pull request from your feature branch
5. Make a merge request from your feature branch
When you submit your pull request, your commit log should do most of the talking
When you submit your merge request, your commit log should do most of the talking
when it comes to describing your changes and their motivation. In addition to
this, your pull request's comments will ideally include a test plan that the
this, your merge request's comments will ideally include a test plan that the
reviewers can use to (1) demonstrate the problem on master, if applicable and
(2) verify that the problem no longer exists with your changes applied (or that
your new features work correctly). Document all of the edge cases you're aware
of so we can adequately test them - then verify the test plan yourself before
submitting.
## Commit Log
Unlike many projects using GitHub and GitLab, wlroots has a [linear, "recipe"
style] history. This means that every commit should be small, digestible,
stand-alone, and functional. Rather than a purely chronological commit history
like this:
```
doc: final docs for view transforms
fix tests when disabled, redo broken doc formatting
better transformed-view iteration (thanks Hannah!)
try to catch more cases in tests
tests: add new spline test
fix compilation on splines
doc: notes on reticulating splines
compositor: add spline reticulation for view transforms
```
We aim to have a clean history which only reflects the final state, broken up
into functional groupings:
```
compositor: add spline reticulation for view transforms
compositor: new iterator for view transforms
tests: add view-transform correctness tests
doc: fix formatting for view transforms
```
This ensures that the final patch series only contains the final state,
without the changes and missteps taken along the development process. A linear
history eases reviewing, cherry-picking and reverting changes.
If you aren't comfortable with manipulating the Git history, have a look at
[git-rebase.io].
## Commit Messages
Please strive to write good commit messages. Here's some guidelines to follow:
The first line should be limited to 50 characters and should be a sentence that
completes the thought [When applied, this commit will...] *"Implement
cmd_move"* or *"Fix #742"* or *"Improve performance of arrange_windows on ARM"*
or similar.
cmd_move"* or *"Improve performance of arrange_windows on ARM"* or similar.
The subsequent lines should be separated from the subject line by a single
blank line, and include optional details. In this you can give justification
for the change, [reference Github
issues](https://help.github.com/articles/closing-issues-via-commit-messages/),
or explain some of the subtler details of your patch. This is important because
when someone finds a line of code they don't understand later, they can use the
`git blame` command to find out what the author was thinking when they wrote
it. It's also easier to review your pull requests if they're separated into
logical commits that have good commit messages and justify themselves in the
extended commit description.
for the change, [reference issues], or explain some of the subtler
details of your patch. This is important because when someone finds a line of
code they don't understand later, they can use the `git blame` command to find
out what the author was thinking when they wrote it. It's also easier to review
your merge requests if they're separated into logical commits that have good
commit messages and justify themselves in the extended commit description.
As a good rule of thumb, anything you might put into the pull request
description on Github is probably fair game for going into the extended commit
As a good rule of thumb, anything you might put into the merge request
description on GitLab is probably fair game for going into the extended commit
message as well.
See [here](https://chris.beams.io/posts/git-commit/) for more details.
See [How to Write a Git Commit Message] for more details.
## Code Review
@ -70,23 +101,29 @@ changes will typically see review from several people. Be prepared to receive
some feedback - you may be asked to make changes to your work. Our code review
process is:
1. **Triage** the pull request. Do the commit messages make sense? Is a test
1. **Triage** the merge request. Do the commit messages make sense? Is a test
plan necessary and/or present? Add anyone as reviewers that you think should
be there (using the relevant GitHub feature, if you have the permissions, or
be there (using the relevant GitLab feature, if you have the permissions, or
with an @mention if necessary).
2. **Review** the code. Look for code style violations, naming convention
violations, buffer overflows, memory leaks, logic errors, non-portable code
(including GNU-isms), etc. For significant changes to the public API, loop in
a couple more people for discussion.
3. **Execute** the test plan, if present.
4. **Merge** the pull request when all reviewers approve.
4. **Merge** the merge request when all reviewers approve.
5. **File** follow-up tickets if appropriate.
## Code of Conduct
Note that as a project hosted on freedesktop.org, wlroots follows its
[Code of Conduct], based on the Contributor Covenant. Please conduct yourself
in a respectful and civilized manner when communicating with community members
on IRC and bug tracker.
## Style Reference
wlroots is written in C with a style similar to the [kernel
style](https://www.kernel.org/doc/Documentation/process/coding-style.rst), but
with a few notable differences.
wlroots is written in C with a style similar to the [kernel style], but with a
few notable differences.
Try to keep your code conforming to C11 and POSIX as much as possible, and do
not use GNU extensions.
@ -163,7 +200,7 @@ zeroed value and exit cleanly; this simplifies error handling a lot.
### Error Codes
For functions not returning a value, they should return a (stdbool.h) bool to
indicated if they succeeded or not.
indicate whether they succeeded or not.
### Macros
@ -242,14 +279,12 @@ at least one struct for each interface in the protocol. For instance,
### Globals
Global interfaces generally have public constructors and destructors. Their
struct has a field holding the `wl_global` itself, a list of resources clients
created by binding to the global, a destroy signal and a `wl_display` destroy
listener. Example:
struct has a field holding the `wl_global` itself, a destroy signal and a
`wl_display` destroy listener. Example:
```c
struct wlr_compositor {
struct wl_global *global;
struct wl_list resources;
struct wl_listener display_destroy;
@ -262,8 +297,9 @@ struct wlr_compositor {
```
When the destructor is called, it should emit the destroy signal, remove the
display destroy listener, destroy the `wl_global`, destroy all bound resources
and then destroy the struct.
display destroy listener, destroy the `wl_global` and then destroy the struct.
The destructor can assume all clients and resources have been already
destroyed.
### Resources
@ -285,13 +321,22 @@ struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource) {
}
```
If a pointer to a `wl_resource` is stored, a resource destroy handler needs to
be registered to clean it up. libwayland will automatically destroy resources
in an arbitrary order when a client is disconnected, the compositor must handle
this correctly.
### Destroying resources
Object structs should only be destroyed when their resource is destroyed, ie.
in the resource destroy handler (set with `wl_resource_set_implementation`).
Destructor requests should only call `wl_resource_destroy`.
The compositor should not destroy resources on its own.
- If the object has a destructor request: the request handler should just call
`wl_resource_destroy` and do nothing else. The compositor must not destroy
resources on its own outside the destructor request handler.
- If the protocol specifies that an object is destroyed when an event is sent:
it's the only case where the compositor is allowed to send the event and then
call `wl_resource_destroy`. An example of this is `wl_callback`.
### Inert resources
@ -354,3 +399,11 @@ static void subsurface_handle_surface_destroy(struct wl_listener *listener,
subsurface_destroy(subsurface);
}
```
[#sway-devel on Libera Chat]: https://web.libera.chat/gamja/?channels=#sway-devel
[linear, "recipe" style]: https://www.bitsnbites.eu/git-history-work-log-vs-recipe/
[git-rebase.io]: https://git-rebase.io/
[reference issues]: https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically
[Code of Conduct]: https://www.freedesktop.org/wiki/CodeOfConduct/
[How to Write a Git Commit Message]: https://chris.beams.io/posts/git-commit/
[kernel style]: https://www.kernel.org/doc/Documentation/process/coding-style.rst

View File

@ -1,26 +1,31 @@
# blankie/wlroots - dkondor-upstream-pr-2551
This branch "frontports" https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/2551,
should be updated when there's a new wlroots version and be unmaintained
when the pull request above is merged
# wlroots
Pluggable, composable, unopinionated modules for building a
[Wayland](http://wayland.freedesktop.org/) compositor; or about 50,000 lines of
code you were going to write anyway.
Pluggable, composable, unopinionated modules for building a [Wayland]
compositor; or about 60,000 lines of code you were going to write anyway.
- wlroots provides backends that abstract the underlying display and input
hardware, including KMS/DRM, libinput, Wayland, X11, and headless backends,
plus any custom backends you choose to write, which can all be created or
destroyed at runtime and used in concert with each other.
hardware, including KMS/DRM, libinput, Wayland, X11, and headless backends,
plus any custom backends you choose to write, which can all be created or
destroyed at runtime and used in concert with each other.
- wlroots provides unopinionated, mostly standalone implementations of many
Wayland interfaces, both from wayland.xml and various protocol extensions.
We also promote the standardization of portable extensions across
many compositors.
Wayland interfaces, both from wayland.xml and various protocol extensions.
We also promote the standardization of portable extensions across
many compositors.
- wlroots provides several powerful, standalone, and optional tools that
implement components common to many compositors, such as the arrangement of
outputs in physical space.
implement components common to many compositors, such as the arrangement of
outputs in physical space.
- wlroots provides an Xwayland abstraction that allows you to have excellent
Xwayland support without worrying about writing your own X11 window manager
on top of writing your compositor.
Xwayland support without worrying about writing your own X11 window manager
on top of writing your compositor.
- wlroots provides a renderer abstraction that simple compositors can use to
avoid writing GL code directly, but which steps out of the way when your
needs demand custom rendering code.
avoid writing GL code directly, but which steps out of the way when your
needs demand custom rendering code.
wlroots implements a huge variety of Wayland compositor features and implements
them *right*, so you can focus on the features that make your compositor
@ -30,13 +35,12 @@ development tools - or any subset of these features you like, because all of
them work independently of one another and freely compose with anything you want
to implement yourself.
Check out our [wiki](https://github.com/swaywm/wlroots/wiki/Getting-started) to
get started with wlroots.
Check out our [wiki] to get started with wlroots. Join our IRC channel:
[#sway-devel on Libera Chat].
wlroots is developed under the direction of the
[sway](https://github.com/swaywm/sway) project. A variety of wrapper libraries
[are available](https://github.com/swaywm) for using it with your favorite
programming language.
wlroots is developed under the direction of the [sway] project. A variety of
[wrapper libraries] are available for using it with your favorite programming
language.
## Building
@ -45,39 +49,41 @@ Install dependencies:
* meson
* wayland
* wayland-protocols
* EGL
* GLESv2
* EGL and GLESv2 (optional, for the GLES2 renderer)
* Vulkan loader, headers and glslang (optional, for the Vulkan renderer)
* libdrm
* GBM
* libinput
* libinput (optional, for the libinput backend)
* xkbcommon
* udev
* pixman
* systemd (optional, for logind support)
* elogind (optional, for logind support on systems without systemd)
* libcap (optional, for capability support)
* [libseat]
If you choose to enable X11 support:
* xcb
* xcb-composite
* xcb-xfixes
* xcb-xinput
* xcb-image
* xcb-render
* x11-xcb
* xcb-errors (optional, for improved error reporting)
* x11-icccm (optional, for improved Xwayland introspection)
* xwayland (build-time only, optional at runtime)
* libxcb
* libxcb-render-util
* libxcb-wm
* libxcb-errors (optional, for improved error reporting)
Run these commands:
meson build
ninja -C build
meson build/
ninja -C build/
Install like so:
sudo ninja -C build install
sudo ninja -C build/ install
## Contributing
See [CONTRIBUTING.md](https://github.com/swaywm/wlroots/blob/master/CONTRIBUTING.md).
See [CONTRIBUTING.md].
[Wayland]: https://wayland.freedesktop.org/
[wiki]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Getting-started
[#sway-devel on Libera Chat]: https://web.libera.chat/gamja/?channels=#sway-devel
[Sway]: https://github.com/swaywm/sway
[wrapper libraries]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Projects-which-use-wlroots#wrapper-libraries
[libseat]: https://git.sr.ht/~kennylevinsen/seatd
[CONTRIBUTING.md]: https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/master/CONTRIBUTING.md

View File

@ -1,27 +1,39 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <errno.h>
#include <libinput.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wayland-server.h>
#include <wlr/backend/drm.h>
#include <wayland-server-core.h>
#include <wlr/backend/headless.h>
#include <wlr/backend/interface.h>
#include <wlr/backend/libinput.h>
#include <wlr/backend/multi.h>
#include <wlr/backend/noop.h>
#include <wlr/backend/session.h>
#include <wlr/backend/wayland.h>
#include <wlr/config.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/util/log.h>
#include "backend/backend.h"
#include "backend/multi.h"
#include "render/allocator/allocator.h"
#include "util/signal.h"
#if WLR_HAS_DRM_BACKEND
#include <wlr/backend/drm.h>
#include "backend/drm/monitor.h"
#endif
#if WLR_HAS_LIBINPUT_BACKEND
#include <wlr/backend/libinput.h>
#endif
#if WLR_HAS_X11_BACKEND
#include <wlr/backend/x11.h>
#endif
#define WAIT_SESSION_TIMEOUT 10000 // ms
void wlr_backend_init(struct wlr_backend *backend,
const struct wlr_backend_impl *impl) {
assert(backend);
@ -31,6 +43,10 @@ void wlr_backend_init(struct wlr_backend *backend,
wl_signal_init(&backend->events.new_output);
}
void wlr_backend_finish(struct wlr_backend *backend) {
wlr_signal_emit_safe(&backend->events.destroy, backend);
}
bool wlr_backend_start(struct wlr_backend *backend) {
if (backend->impl->start) {
return backend->impl->start(backend);
@ -50,13 +66,6 @@ void wlr_backend_destroy(struct wlr_backend *backend) {
}
}
struct wlr_renderer *wlr_backend_get_renderer(struct wlr_backend *backend) {
if (backend->impl->get_renderer) {
return backend->impl->get_renderer(backend);
}
return NULL;
}
struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) {
if (backend->impl->get_session) {
return backend->impl->get_session(backend);
@ -64,6 +73,52 @@ struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) {
return NULL;
}
static uint64_t get_current_time_ms(void) {
struct timespec ts = {0};
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
}
static struct wlr_session *session_create_and_wait(struct wl_display *disp) {
struct wlr_session *session = wlr_session_create(disp);
if (!session) {
wlr_log(WLR_ERROR, "Failed to start a session");
return NULL;
}
if (!session->active) {
wlr_log(WLR_INFO, "Waiting for a session to become active");
uint64_t started_at = get_current_time_ms();
uint64_t timeout = WAIT_SESSION_TIMEOUT;
struct wl_event_loop *event_loop =
wl_display_get_event_loop(session->display);
while (!session->active) {
int ret = wl_event_loop_dispatch(event_loop, (int)timeout);
if (ret < 0) {
wlr_log_errno(WLR_ERROR, "Failed to wait for session active: "
"wl_event_loop_dispatch failed");
return NULL;
}
uint64_t now = get_current_time_ms();
if (now >= started_at + WAIT_SESSION_TIMEOUT) {
break;
}
timeout = started_at + WAIT_SESSION_TIMEOUT - now;
}
if (!session->active) {
wlr_log(WLR_ERROR, "Timeout waiting session to become active");
return NULL;
}
}
return session;
}
clockid_t wlr_backend_get_presentation_clock(struct wlr_backend *backend) {
if (backend->impl->get_presentation_clock) {
return backend->impl->get_presentation_clock(backend);
@ -71,6 +126,21 @@ clockid_t wlr_backend_get_presentation_clock(struct wlr_backend *backend) {
return CLOCK_MONOTONIC;
}
int wlr_backend_get_drm_fd(struct wlr_backend *backend) {
if (!backend->impl->get_drm_fd) {
return -1;
}
return backend->impl->get_drm_fd(backend);
}
uint32_t backend_get_buffer_caps(struct wlr_backend *backend) {
if (!backend->impl->get_buffer_caps) {
return 0;
}
return backend->impl->get_buffer_caps(backend);
}
static size_t parse_outputs_env(const char *name) {
const char *outputs_str = getenv(name);
if (outputs_str == NULL) {
@ -87,9 +157,8 @@ static size_t parse_outputs_env(const char *name) {
return outputs;
}
static struct wlr_backend *attempt_wl_backend(struct wl_display *display,
wlr_renderer_create_func_t create_renderer_func) {
struct wlr_backend *backend = wlr_wl_backend_create(display, NULL, create_renderer_func);
static struct wlr_backend *attempt_wl_backend(struct wl_display *display) {
struct wlr_backend *backend = wlr_wl_backend_create(display, NULL);
if (backend == NULL) {
return NULL;
}
@ -104,8 +173,8 @@ static struct wlr_backend *attempt_wl_backend(struct wl_display *display,
#if WLR_HAS_X11_BACKEND
static struct wlr_backend *attempt_x11_backend(struct wl_display *display,
const char *x11_display, wlr_renderer_create_func_t create_renderer_func) {
struct wlr_backend *backend = wlr_x11_backend_create(display, x11_display, create_renderer_func);
const char *x11_display) {
struct wlr_backend *backend = wlr_x11_backend_create(display, x11_display);
if (backend == NULL) {
return NULL;
}
@ -120,8 +189,8 @@ static struct wlr_backend *attempt_x11_backend(struct wl_display *display,
#endif
static struct wlr_backend *attempt_headless_backend(
struct wl_display *display, wlr_renderer_create_func_t create_renderer_func) {
struct wlr_backend *backend = wlr_headless_backend_create(display, create_renderer_func);
struct wl_display *display) {
struct wlr_backend *backend = wlr_headless_backend_create(display);
if (backend == NULL) {
return NULL;
}
@ -134,33 +203,29 @@ static struct wlr_backend *attempt_headless_backend(
return backend;
}
static struct wlr_backend *attempt_noop_backend(struct wl_display *display) {
struct wlr_backend *backend = wlr_noop_backend_create(display);
if (backend == NULL) {
#if WLR_HAS_DRM_BACKEND
static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
struct wlr_backend *backend, struct wlr_session *session) {
struct wlr_device *gpus[8];
ssize_t num_gpus = wlr_session_find_gpus(session, 8, gpus);
if (num_gpus < 0) {
wlr_log(WLR_ERROR, "Failed to find GPUs");
return NULL;
}
size_t outputs = parse_outputs_env("WLR_NOOP_OUTPUTS");
for (size_t i = 0; i < outputs; ++i) {
wlr_noop_add_output(backend);
if (num_gpus == 0) {
wlr_log(WLR_ERROR, "Found 0 GPUs, cannot create backend");
return NULL;
} else {
wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus);
}
return backend;
}
static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
struct wlr_backend *backend, struct wlr_session *session,
wlr_renderer_create_func_t create_renderer_func) {
int gpus[8];
size_t num_gpus = wlr_session_find_gpus(session, 8, gpus);
struct wlr_backend *primary_drm = NULL;
wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus);
for (size_t i = 0; i < num_gpus; ++i) {
for (size_t i = 0; i < (size_t)num_gpus; ++i) {
struct wlr_backend *drm = wlr_drm_backend_create(display, session,
gpus[i], primary_drm, create_renderer_func);
gpus[i], primary_drm);
if (!drm) {
wlr_log(WLR_ERROR, "Failed to open DRM device %d", gpus[i]);
wlr_log(WLR_ERROR, "Failed to create DRM backend");
continue;
}
@ -170,46 +235,56 @@ static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
wlr_multi_backend_add(backend, drm);
}
if (!primary_drm) {
wlr_log(WLR_ERROR, "Could not successfully create backend on any GPU");
return NULL;
}
return primary_drm;
}
#endif
static struct wlr_backend *attempt_backend_by_name(struct wl_display *display,
struct wlr_backend *backend, struct wlr_session **session,
const char *name, wlr_renderer_create_func_t create_renderer_func) {
static bool attempt_backend_by_name(struct wl_display *display,
struct wlr_multi_backend *multi, char *name) {
struct wlr_backend *backend = NULL;
if (strcmp(name, "wayland") == 0) {
return attempt_wl_backend(display, create_renderer_func);
backend = attempt_wl_backend(display);
#if WLR_HAS_X11_BACKEND
} else if (strcmp(name, "x11") == 0) {
return attempt_x11_backend(display, NULL, create_renderer_func);
backend = attempt_x11_backend(display, NULL);
#endif
} else if (strcmp(name, "headless") == 0) {
return attempt_headless_backend(display, create_renderer_func);
} else if (strcmp(name, "noop") == 0) {
return attempt_noop_backend(display);
backend = attempt_headless_backend(display);
} else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) {
// DRM and libinput need a session
if (!*session) {
*session = wlr_session_create(display);
if (!*session) {
if (multi->session == NULL) {
multi->session = session_create_and_wait(display);
if (multi->session == NULL) {
wlr_log(WLR_ERROR, "failed to start a session");
return NULL;
return false;
}
}
if (strcmp(name, "libinput") == 0) {
return wlr_libinput_backend_create(display, *session);
#if WLR_HAS_LIBINPUT_BACKEND
backend = wlr_libinput_backend_create(display, multi->session);
#endif
} else {
return attempt_drm_backend(display, backend, *session, create_renderer_func);
#if WLR_HAS_DRM_BACKEND
// attempt_drm_backend adds the multi drm backends itself
return attempt_drm_backend(display, &multi->backend,
multi->session) != NULL;
#endif
}
} else {
wlr_log(WLR_ERROR, "unrecognized backend '%s'", name);
return false;
}
wlr_log(WLR_ERROR, "unrecognized backend '%s'", name);
return NULL;
return wlr_multi_backend_add(&multi->backend, backend);
}
struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
wlr_renderer_create_func_t create_renderer_func) {
struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
struct wlr_backend *backend = wlr_multi_backend_create(display);
struct wlr_multi_backend *multi = (struct wlr_multi_backend *)backend;
if (!backend) {
@ -219,6 +294,9 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
char *names = getenv("WLR_BACKENDS");
if (names) {
wlr_log(WLR_INFO, "Loading user-specified backends due to WLR_BACKENDS: %s",
names);
names = strdup(names);
if (names == NULL) {
wlr_log(WLR_ERROR, "allocation failed");
@ -229,17 +307,7 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
char *saveptr;
char *name = strtok_r(names, ",", &saveptr);
while (name != NULL) {
struct wlr_backend *subbackend = attempt_backend_by_name(display,
backend, &multi->session, name, create_renderer_func);
if (subbackend == NULL) {
wlr_log(WLR_ERROR, "failed to start backend '%s'", name);
wlr_session_destroy(multi->session);
wlr_backend_destroy(backend);
free(names);
return NULL;
}
if (!wlr_multi_backend_add(backend, subbackend)) {
if (!attempt_backend_by_name(display, multi, name)) {
wlr_log(WLR_ERROR, "failed to add backend '%s'", name);
wlr_session_destroy(multi->session);
wlr_backend_destroy(backend);
@ -254,36 +322,39 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
return backend;
}
if (getenv("WAYLAND_DISPLAY") || getenv("_WAYLAND_DISPLAY") ||
getenv("WAYLAND_SOCKET")) {
struct wlr_backend *wl_backend = attempt_wl_backend(display,
create_renderer_func);
if (wl_backend) {
wlr_multi_backend_add(backend, wl_backend);
return backend;
if (getenv("WAYLAND_DISPLAY") || getenv("WAYLAND_SOCKET")) {
struct wlr_backend *wl_backend = attempt_wl_backend(display);
if (!wl_backend) {
goto error;
}
wlr_multi_backend_add(backend, wl_backend);
return backend;
}
#if WLR_HAS_X11_BACKEND
const char *x11_display = getenv("DISPLAY");
if (x11_display) {
struct wlr_backend *x11_backend =
attempt_x11_backend(display, x11_display, create_renderer_func);
if (x11_backend) {
wlr_multi_backend_add(backend, x11_backend);
return backend;
attempt_x11_backend(display, x11_display);
if (!x11_backend) {
goto error;
}
wlr_multi_backend_add(backend, x11_backend);
return backend;
}
#endif
// Attempt DRM+libinput
multi->session = wlr_session_create(display);
multi->session = session_create_and_wait(display);
if (!multi->session) {
wlr_log(WLR_ERROR, "Failed to start a DRM session");
wlr_backend_destroy(backend);
return NULL;
}
#if WLR_HAS_LIBINPUT_BACKEND
struct wlr_backend *libinput = wlr_libinput_backend_create(display,
multi->session);
if (!libinput) {
@ -293,16 +364,37 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
return NULL;
}
wlr_multi_backend_add(backend, libinput);
#else
const char *no_devs = getenv("WLR_LIBINPUT_NO_DEVICES");
if (no_devs && strcmp(no_devs, "1") == 0) {
wlr_log(WLR_INFO, "WLR_LIBINPUT_NO_DEVICES is set, "
"starting without libinput backend");
} else {
wlr_log(WLR_ERROR, "libinput support is not compiled in, "
"refusing to start");
wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check");
wlr_session_destroy(multi->session);
wlr_backend_destroy(backend);
return NULL;
}
#endif
struct wlr_backend *primary_drm = attempt_drm_backend(display, backend,
multi->session, create_renderer_func);
#if WLR_HAS_DRM_BACKEND
struct wlr_backend *primary_drm =
attempt_drm_backend(display, backend, multi->session);
if (!primary_drm) {
wlr_log(WLR_ERROR, "Failed to open any DRM device");
wlr_backend_destroy(libinput);
wlr_session_destroy(multi->session);
wlr_backend_destroy(backend);
return NULL;
}
drm_backend_monitor_create(backend, primary_drm, multi->session);
return backend;
#endif
error:
wlr_backend_destroy(backend);
return NULL;
}

View File

@ -1,4 +1,3 @@
#include <gbm.h>
#include <stdlib.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
@ -9,266 +8,290 @@
struct atomic {
drmModeAtomicReq *req;
int cursor;
bool failed;
};
static void atomic_begin(struct wlr_drm_crtc *crtc, struct atomic *atom) {
if (!crtc->atomic) {
crtc->atomic = drmModeAtomicAlloc();
if (!crtc->atomic) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
atom->failed = true;
return;
}
}
static void atomic_begin(struct atomic *atom) {
memset(atom, 0, sizeof(*atom));
atom->req = crtc->atomic;
atom->cursor = drmModeAtomicGetCursor(atom->req);
atom->failed = false;
atom->req = drmModeAtomicAlloc();
if (!atom->req) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
atom->failed = true;
return;
}
}
static bool atomic_end(int drm_fd, struct atomic *atom) {
static bool atomic_commit(struct atomic *atom,
struct wlr_drm_connector *conn, uint32_t flags) {
struct wlr_drm_backend *drm = conn->backend;
if (atom->failed) {
return false;
}
uint32_t flags = DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_NONBLOCK;
if (drmModeAtomicCommit(drm_fd, atom->req, flags, NULL)) {
wlr_log_errno(WLR_ERROR, "Atomic test failed");
drmModeAtomicSetCursor(atom->req, atom->cursor);
int ret = drmModeAtomicCommit(drm->fd, atom->req, flags, drm);
if (ret != 0) {
wlr_drm_conn_log_errno(conn,
(flags & DRM_MODE_ATOMIC_TEST_ONLY) ? WLR_DEBUG : WLR_ERROR,
"Atomic %s failed (%s)",
(flags & DRM_MODE_ATOMIC_TEST_ONLY) ? "test" : "commit",
(flags & DRM_MODE_ATOMIC_ALLOW_MODESET) ? "modeset" : "pageflip");
return false;
}
return true;
}
static bool atomic_commit(int drm_fd, struct atomic *atom,
struct wlr_drm_connector *conn, uint32_t flags, bool modeset) {
if (atom->failed) {
return false;
}
int ret = drmModeAtomicCommit(drm_fd, atom->req, flags, conn);
if (ret) {
wlr_log_errno(WLR_ERROR, "%s: Atomic commit failed (%s)",
conn->output.name, modeset ? "modeset" : "pageflip");
// Try to commit without new changes
drmModeAtomicSetCursor(atom->req, atom->cursor);
if (drmModeAtomicCommit(drm_fd, atom->req, flags, conn)) {
wlr_log_errno(WLR_ERROR,
"%s: Atomic commit without new changes failed (%s)",
conn->output.name, modeset ? "modeset" : "pageflip");
}
}
drmModeAtomicSetCursor(atom->req, 0);
return !ret;
static void atomic_finish(struct atomic *atom) {
drmModeAtomicFree(atom->req);
}
static inline void atomic_add(struct atomic *atom, uint32_t id, uint32_t prop, uint64_t val) {
static void atomic_add(struct atomic *atom, uint32_t id, uint32_t prop, uint64_t val) {
if (!atom->failed && drmModeAtomicAddProperty(atom->req, id, prop, val) < 0) {
wlr_log_errno(WLR_ERROR, "Failed to add atomic DRM property");
atom->failed = true;
}
}
static void set_plane_props(struct atomic *atom, struct wlr_drm_plane *plane,
uint32_t crtc_id, uint32_t fb_id, bool set_crtc_xy) {
uint32_t id = plane->id;
const union wlr_drm_plane_props *props = &plane->props;
// The src_* properties are in 16.16 fixed point
atomic_add(atom, id, props->src_x, 0);
atomic_add(atom, id, props->src_y, 0);
atomic_add(atom, id, props->src_w, (uint64_t)plane->surf.width << 16);
atomic_add(atom, id, props->src_h, (uint64_t)plane->surf.height << 16);
atomic_add(atom, id, props->crtc_w, plane->surf.width);
atomic_add(atom, id, props->crtc_h, plane->surf.height);
atomic_add(atom, id, props->fb_id, fb_id);
atomic_add(atom, id, props->crtc_id, crtc_id);
if (set_crtc_xy) {
atomic_add(atom, id, props->crtc_x, 0);
atomic_add(atom, id, props->crtc_y, 0);
}
}
static bool atomic_crtc_pageflip(struct wlr_drm_backend *drm,
static bool create_mode_blob(struct wlr_drm_backend *drm,
struct wlr_drm_connector *conn,
struct wlr_drm_crtc *crtc,
uint32_t fb_id, drmModeModeInfo *mode) {
if (mode != NULL) {
if (crtc->mode_id != 0) {
drmModeDestroyPropertyBlob(drm->fd, crtc->mode_id);
}
if (drmModeCreatePropertyBlob(drm->fd, mode, sizeof(*mode),
&crtc->mode_id)) {
wlr_log_errno(WLR_ERROR, "Unable to create property blob");
return false;
}
}
uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT;
if (mode != NULL) {
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
} else {
flags |= DRM_MODE_ATOMIC_NONBLOCK;
}
struct atomic atom;
atomic_begin(crtc, &atom);
atomic_add(&atom, conn->id, conn->props.crtc_id, crtc->id);
if (mode != NULL && conn->props.link_status != 0) {
atomic_add(&atom, conn->id, conn->props.link_status,
DRM_MODE_LINK_STATUS_GOOD);
}
atomic_add(&atom, crtc->id, crtc->props.mode_id, crtc->mode_id);
atomic_add(&atom, crtc->id, crtc->props.active, 1);
set_plane_props(&atom, crtc->primary, crtc->id, fb_id, true);
return atomic_commit(drm->fd, &atom, conn, flags, mode);
}
static bool atomic_conn_enable(struct wlr_drm_backend *drm,
struct wlr_drm_connector *conn, bool enable) {
struct wlr_drm_crtc *crtc = conn->crtc;
if (crtc == NULL) {
return !enable;
}
struct atomic atom;
atomic_begin(crtc, &atom);
atomic_add(&atom, crtc->id, crtc->props.active, enable);
if (enable) {
atomic_add(&atom, conn->id, conn->props.crtc_id, crtc->id);
atomic_add(&atom, crtc->id, crtc->props.mode_id, crtc->mode_id);
} else {
atomic_add(&atom, conn->id, conn->props.crtc_id, 0);
atomic_add(&atom, crtc->id, crtc->props.mode_id, 0);
}
return atomic_commit(drm->fd, &atom, conn, DRM_MODE_ATOMIC_ALLOW_MODESET,
true);
}
bool legacy_crtc_set_cursor(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, struct gbm_bo *bo);
static bool atomic_crtc_set_cursor(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, struct gbm_bo *bo) {
if (!crtc || !crtc->cursor) {
const struct wlr_drm_connector_state *state, uint32_t *blob_id) {
if (!state->active) {
*blob_id = 0;
return true;
}
struct wlr_drm_plane *plane = crtc->cursor;
// We can't use atomic operations on fake planes
if (plane->id == 0) {
return legacy_crtc_set_cursor(drm, crtc, bo);
if (drmModeCreatePropertyBlob(drm->fd, &state->mode,
sizeof(drmModeModeInfo), blob_id)) {
wlr_log_errno(WLR_ERROR, "Unable to create mode property blob");
return false;
}
struct atomic atom;
atomic_begin(crtc, &atom);
if (bo) {
uint32_t fb_id = get_fb_for_bo(bo, plane->drm_format);
set_plane_props(&atom, plane, crtc->id, fb_id, false);
} else {
atomic_add(&atom, plane->id, plane->props.fb_id, 0);
atomic_add(&atom, plane->id, plane->props.crtc_id, 0);
}
return atomic_end(drm->fd, &atom);
return true;
}
bool legacy_crtc_move_cursor(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, int x, int y);
static bool atomic_crtc_move_cursor(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, int x, int y) {
if (!crtc || !crtc->cursor) {
static bool create_gamma_lut_blob(struct wlr_drm_backend *drm,
size_t size, const uint16_t *lut, uint32_t *blob_id) {
if (size == 0) {
*blob_id = 0;
return true;
}
struct wlr_drm_plane *plane = crtc->cursor;
// We can't use atomic operations on fake planes
if (plane->id == 0) {
return legacy_crtc_move_cursor(drm, crtc, x, y);
}
struct atomic atom;
atomic_begin(crtc, &atom);
atomic_add(&atom, plane->id, plane->props.crtc_x, x);
atomic_add(&atom, plane->id, plane->props.crtc_y, y);
return atomic_end(drm->fd, &atom);
}
static bool atomic_crtc_set_gamma(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, size_t size,
uint16_t *r, uint16_t *g, uint16_t *b) {
// Fallback to legacy gamma interface when gamma properties are not available
// (can happen on older Intel GPUs that support gamma but not degamma).
// TEMP: This is broken on AMDGPU. Provide a fallback to legacy until they
// get it fixed. Ref https://bugs.freedesktop.org/show_bug.cgi?id=107459
const char *no_atomic_str = getenv("WLR_DRM_NO_ATOMIC_GAMMA");
bool no_atomic = no_atomic_str != NULL && strcmp(no_atomic_str, "1") == 0;
if (crtc->props.gamma_lut == 0 || no_atomic) {
return legacy_iface.crtc_set_gamma(drm, crtc, size, r, g, b);
}
struct drm_color_lut *gamma = malloc(size * sizeof(struct drm_color_lut));
if (gamma == NULL) {
wlr_log(WLR_ERROR, "Failed to allocate gamma table");
return false;
}
const uint16_t *r = lut;
const uint16_t *g = lut + size;
const uint16_t *b = lut + 2 * size;
for (size_t i = 0; i < size; i++) {
gamma[i].red = r[i];
gamma[i].green = g[i];
gamma[i].blue = b[i];
}
if (crtc->gamma_lut != 0) {
drmModeDestroyPropertyBlob(drm->fd, crtc->gamma_lut);
}
if (drmModeCreatePropertyBlob(drm->fd, gamma,
size * sizeof(struct drm_color_lut), &crtc->gamma_lut)) {
size * sizeof(struct drm_color_lut), blob_id) != 0) {
wlr_log_errno(WLR_ERROR, "Unable to create gamma LUT property blob");
free(gamma);
wlr_log_errno(WLR_ERROR, "Unable to create property blob");
return false;
}
free(gamma);
struct atomic atom;
atomic_begin(crtc, &atom);
atomic_add(&atom, crtc->id, crtc->props.gamma_lut, crtc->gamma_lut);
return atomic_end(drm->fd, &atom);
return true;
}
static size_t atomic_crtc_get_gamma_size(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc) {
if (crtc->props.gamma_lut_size == 0) {
return legacy_iface.crtc_get_gamma_size(drm, crtc);
static void commit_blob(struct wlr_drm_backend *drm,
uint32_t *current, uint32_t next) {
if (*current == next) {
return;
}
if (*current != 0) {
drmModeDestroyPropertyBlob(drm->fd, *current);
}
*current = next;
}
static void rollback_blob(struct wlr_drm_backend *drm,
uint32_t *current, uint32_t next) {
if (*current == next) {
return;
}
if (next != 0) {
drmModeDestroyPropertyBlob(drm->fd, next);
}
}
static void plane_disable(struct atomic *atom, struct wlr_drm_plane *plane) {
uint32_t id = plane->id;
const union wlr_drm_plane_props *props = &plane->props;
atomic_add(atom, id, props->fb_id, 0);
atomic_add(atom, id, props->crtc_id, 0);
}
static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm,
struct wlr_drm_plane *plane, uint32_t crtc_id, int32_t x, int32_t y) {
uint32_t id = plane->id;
const union wlr_drm_plane_props *props = &plane->props;
struct wlr_drm_fb *fb = plane_get_next_fb(plane);
if (fb == NULL) {
wlr_log(WLR_ERROR, "Failed to acquire FB");
goto error;
}
uint64_t gamma_lut_size;
if (!get_drm_prop(drm->fd, crtc->id, crtc->props.gamma_lut_size,
&gamma_lut_size)) {
wlr_log(WLR_ERROR, "Unable to get gamma lut size");
return 0;
uint32_t width = fb->wlr_buf->width;
uint32_t height = fb->wlr_buf->height;
// The src_* properties are in 16.16 fixed point
atomic_add(atom, id, props->src_x, 0);
atomic_add(atom, id, props->src_y, 0);
atomic_add(atom, id, props->src_w, (uint64_t)width << 16);
atomic_add(atom, id, props->src_h, (uint64_t)height << 16);
atomic_add(atom, id, props->crtc_w, width);
atomic_add(atom, id, props->crtc_h, height);
atomic_add(atom, id, props->fb_id, fb->id);
atomic_add(atom, id, props->crtc_id, crtc_id);
atomic_add(atom, id, props->crtc_x, (uint64_t)x);
atomic_add(atom, id, props->crtc_y, (uint64_t)y);
return;
error:
wlr_log(WLR_ERROR, "Failed to set plane %"PRIu32" properties", plane->id);
atom->failed = true;
}
static bool atomic_crtc_commit(struct wlr_drm_connector *conn,
const struct wlr_drm_connector_state *state, uint32_t flags,
bool test_only) {
struct wlr_drm_backend *drm = conn->backend;
struct wlr_output *output = &conn->output;
struct wlr_drm_crtc *crtc = conn->crtc;
bool modeset = state->modeset;
bool active = state->active;
uint32_t mode_id = crtc->mode_id;
if (modeset) {
if (!create_mode_blob(drm, conn, state, &mode_id)) {
return false;
}
}
return (size_t)gamma_lut_size;
uint32_t gamma_lut = crtc->gamma_lut;
if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) {
// Fallback to legacy gamma interface when gamma properties are not
// available (can happen on older Intel GPUs that support gamma but not
// degamma).
if (crtc->props.gamma_lut == 0) {
if (!drm_legacy_crtc_set_gamma(drm, crtc,
state->base->gamma_lut_size,
state->base->gamma_lut)) {
return false;
}
} else {
if (!create_gamma_lut_blob(drm, state->base->gamma_lut_size,
state->base->gamma_lut, &gamma_lut)) {
return false;
}
}
}
uint32_t fb_damage_clips = 0;
if ((state->base->committed & WLR_OUTPUT_STATE_DAMAGE) &&
pixman_region32_not_empty((pixman_region32_t *)&state->base->damage) &&
crtc->primary->props.fb_damage_clips != 0) {
int rects_len;
const pixman_box32_t *rects = pixman_region32_rectangles(
(pixman_region32_t *)&state->base->damage, &rects_len);
if (drmModeCreatePropertyBlob(drm->fd, rects,
sizeof(*rects) * rects_len, &fb_damage_clips) != 0) {
wlr_log_errno(WLR_ERROR, "Failed to create FB_DAMAGE_CLIPS property blob");
}
}
bool prev_vrr_enabled =
output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED;
bool vrr_enabled = prev_vrr_enabled;
if ((state->base->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) &&
drm_connector_supports_vrr(conn)) {
vrr_enabled = state->base->adaptive_sync_enabled;
}
if (test_only) {
flags |= DRM_MODE_ATOMIC_TEST_ONLY;
}
if (modeset) {
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
} else if (!test_only) {
flags |= DRM_MODE_ATOMIC_NONBLOCK;
}
struct atomic atom;
atomic_begin(&atom);
atomic_add(&atom, conn->id, conn->props.crtc_id, active ? crtc->id : 0);
if (modeset && active && conn->props.link_status != 0) {
atomic_add(&atom, conn->id, conn->props.link_status,
DRM_MODE_LINK_STATUS_GOOD);
}
atomic_add(&atom, crtc->id, crtc->props.mode_id, mode_id);
atomic_add(&atom, crtc->id, crtc->props.active, active);
if (active) {
if (crtc->props.gamma_lut != 0) {
atomic_add(&atom, crtc->id, crtc->props.gamma_lut, gamma_lut);
}
if (crtc->props.vrr_enabled != 0) {
atomic_add(&atom, crtc->id, crtc->props.vrr_enabled, vrr_enabled);
}
set_plane_props(&atom, drm, crtc->primary, crtc->id, 0, 0);
if (crtc->primary->props.fb_damage_clips != 0) {
atomic_add(&atom, crtc->primary->id,
crtc->primary->props.fb_damage_clips, fb_damage_clips);
}
if (crtc->cursor) {
if (drm_connector_is_cursor_visible(conn)) {
set_plane_props(&atom, drm, crtc->cursor, crtc->id,
conn->cursor_x, conn->cursor_y);
} else {
plane_disable(&atom, crtc->cursor);
}
}
} else {
plane_disable(&atom, crtc->primary);
if (crtc->cursor) {
plane_disable(&atom, crtc->cursor);
}
}
bool ok = atomic_commit(&atom, conn, flags);
atomic_finish(&atom);
if (ok && !test_only) {
commit_blob(drm, &crtc->mode_id, mode_id);
commit_blob(drm, &crtc->gamma_lut, gamma_lut);
if (vrr_enabled != prev_vrr_enabled) {
output->adaptive_sync_status = vrr_enabled ?
WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED :
WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED;
wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s",
vrr_enabled ? "enabled" : "disabled");
}
} else {
rollback_blob(drm, &crtc->mode_id, mode_id);
rollback_blob(drm, &crtc->gamma_lut, gamma_lut);
}
if (fb_damage_clips != 0 &&
drmModeDestroyPropertyBlob(drm->fd, fb_damage_clips) != 0) {
wlr_log_errno(WLR_ERROR, "Failed to destroy FB_DAMAGE_CLIPS property blob");
}
return ok;
}
const struct wlr_drm_interface atomic_iface = {
.conn_enable = atomic_conn_enable,
.crtc_pageflip = atomic_crtc_pageflip,
.crtc_set_cursor = atomic_crtc_set_cursor,
.crtc_move_cursor = atomic_crtc_move_cursor,
.crtc_set_gamma = atomic_crtc_set_gamma,
.crtc_get_gamma_size = atomic_crtc_get_gamma_size,
.crtc_commit = atomic_crtc_commit,
};

View File

@ -1,15 +1,14 @@
#include <assert.h>
#include <errno.h>
#include <drm_fourcc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wlr/backend/interface.h>
#include <wlr/backend/session.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/egl.h>
#include <wlr/types/wlr_list.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#include "backend/drm/drm.h"
@ -23,7 +22,7 @@ struct wlr_drm_backend *get_drm_backend_from_backend(
static bool backend_start(struct wlr_backend *backend) {
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
scan_drm_connectors(drm);
scan_drm_connectors(drm, NULL);
return true;
}
@ -34,35 +33,35 @@ static void backend_destroy(struct wlr_backend *backend) {
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
restore_drm_outputs(drm);
struct wlr_drm_connector *conn, *next;
wl_list_for_each_safe(conn, next, &drm->outputs, link) {
wlr_output_destroy(&conn->output);
destroy_drm_connector(conn);
}
wlr_signal_emit_safe(&backend->events.destroy, backend);
wlr_backend_finish(backend);
struct wlr_drm_fb *fb, *fb_tmp;
wl_list_for_each_safe(fb, fb_tmp, &drm->fbs, link) {
drm_fb_destroy(fb);
}
wl_list_remove(&drm->display_destroy.link);
wl_list_remove(&drm->session_signal.link);
wl_list_remove(&drm->drm_invalidated.link);
finish_drm_resources(drm);
finish_drm_renderer(&drm->renderer);
wlr_session_close_file(drm->session, drm->fd);
wl_event_source_remove(drm->drm_event);
free(drm);
}
static struct wlr_renderer *backend_get_renderer(
struct wlr_backend *backend) {
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
wl_list_remove(&drm->session_destroy.link);
wl_list_remove(&drm->session_active.link);
wl_list_remove(&drm->parent_destroy.link);
wl_list_remove(&drm->dev_change.link);
wl_list_remove(&drm->dev_remove.link);
if (drm->parent) {
return drm->parent->renderer.wlr_rend;
} else {
return drm->renderer.wlr_rend;
finish_drm_renderer(&drm->mgpu_renderer);
}
finish_drm_resources(drm);
free(drm->name);
wlr_session_close_file(drm->session, drm->dev);
wl_event_source_remove(drm->drm_event);
free(drm);
}
static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) {
@ -70,69 +69,95 @@ static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) {
return drm->clock;
}
static struct wlr_backend_impl backend_impl = {
static int backend_get_drm_fd(struct wlr_backend *backend) {
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
if (drm->parent) {
return drm->parent->fd;
} else {
return drm->fd;
}
}
static uint32_t drm_backend_get_buffer_caps(struct wlr_backend *backend) {
return WLR_BUFFER_CAP_DMABUF;
}
static const struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_renderer = backend_get_renderer,
.get_presentation_clock = backend_get_presentation_clock,
.get_drm_fd = backend_get_drm_fd,
.get_buffer_caps = drm_backend_get_buffer_caps,
};
bool wlr_backend_is_drm(struct wlr_backend *b) {
return b->impl == &backend_impl;
}
static void session_signal(struct wl_listener *listener, void *data) {
static void handle_session_active(struct wl_listener *listener, void *data) {
struct wlr_drm_backend *drm =
wl_container_of(listener, drm, session_signal);
struct wlr_session *session = data;
wl_container_of(listener, drm, session_active);
struct wlr_session *session = drm->session;
if (session->active) {
wlr_log(WLR_INFO, "DRM fd resumed");
scan_drm_connectors(drm);
scan_drm_connectors(drm, NULL);
struct wlr_drm_connector *conn;
wl_list_for_each(conn, &drm->outputs, link){
if (conn->output.enabled) {
drm_connector_set_mode(&conn->output,
conn->output.current_mode);
} else {
enable_drm_connector(&conn->output, false);
}
if (!conn->crtc) {
continue;
}
struct wlr_drm_plane *plane = conn->crtc->cursor;
drm->iface->crtc_set_cursor(drm, conn->crtc,
(plane && plane->cursor_enabled) ? plane->surf.back : NULL);
drm->iface->crtc_move_cursor(drm, conn->crtc, conn->cursor_x,
conn->cursor_y);
if (conn->crtc->gamma_table != NULL) {
size_t size = conn->crtc->gamma_table_size;
uint16_t *r = conn->crtc->gamma_table;
uint16_t *g = conn->crtc->gamma_table + size;
uint16_t *b = conn->crtc->gamma_table + 2 * size;
drm->iface->crtc_set_gamma(drm, conn->crtc, size, r, g, b);
} else {
set_drm_connector_gamma(&conn->output, 0, NULL, NULL, NULL);
wl_list_for_each(conn, &drm->outputs, link) {
struct wlr_output_mode *mode = NULL;
uint32_t committed = WLR_OUTPUT_STATE_ENABLED;
if (conn->output.enabled && conn->output.current_mode != NULL) {
committed |= WLR_OUTPUT_STATE_MODE;
mode = conn->output.current_mode;
}
struct wlr_output_state state = {
.committed = committed,
.enabled = mode != NULL,
.mode_type = WLR_OUTPUT_STATE_MODE_FIXED,
.mode = mode,
};
drm_connector_commit_state(conn, &state);
}
} else {
wlr_log(WLR_INFO, "DRM fd paused");
}
}
static void drm_invalidated(struct wl_listener *listener, void *data) {
static void handle_dev_change(struct wl_listener *listener, void *data) {
struct wlr_drm_backend *drm = wl_container_of(listener, drm, dev_change);
struct wlr_device_change_event *change = data;
if (!drm->session->active) {
return;
}
switch (change->type) {
case WLR_DEVICE_HOTPLUG:
wlr_log(WLR_DEBUG, "Received hotplug event for %s", drm->name);
scan_drm_connectors(drm, &change->hotplug);
break;
case WLR_DEVICE_LEASE:
wlr_log(WLR_DEBUG, "Received lease event for %s", drm->name);
scan_drm_leases(drm);
break;
default:
wlr_log(WLR_DEBUG, "Received unknown change event for %s", drm->name);
}
}
static void handle_dev_remove(struct wl_listener *listener, void *data) {
struct wlr_drm_backend *drm = wl_container_of(listener, drm, dev_remove);
wlr_log(WLR_INFO, "Destroying DRM backend for %s", drm->name);
backend_destroy(&drm->backend);
}
static void handle_session_destroy(struct wl_listener *listener, void *data) {
struct wlr_drm_backend *drm =
wl_container_of(listener, drm, drm_invalidated);
char *name = drmGetDeviceNameFromFd2(drm->fd);
wlr_log(WLR_DEBUG, "%s invalidated", name);
free(name);
scan_drm_connectors(drm);
wl_container_of(listener, drm, session_destroy);
backend_destroy(&drm->backend);
}
static void handle_display_destroy(struct wl_listener *listener, void *data) {
@ -141,16 +166,21 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
backend_destroy(&drm->backend);
}
static void handle_parent_destroy(struct wl_listener *listener, void *data) {
struct wlr_drm_backend *drm =
wl_container_of(listener, drm, parent_destroy);
backend_destroy(&drm->backend);
}
struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
struct wlr_session *session, int gpu_fd, struct wlr_backend *parent,
wlr_renderer_create_func_t create_renderer_func) {
assert(display && session && gpu_fd >= 0);
struct wlr_session *session, struct wlr_device *dev,
struct wlr_backend *parent) {
assert(display && session && dev);
assert(!parent || wlr_backend_is_drm(parent));
char *name = drmGetDeviceNameFromFd2(gpu_fd);
drmVersion *version = drmGetVersion(gpu_fd);
char *name = drmGetDeviceNameFromFd2(dev->fd);
drmVersion *version = drmGetVersion(dev->fd);
wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name);
free(name);
drmFreeVersion(version);
struct wlr_drm_backend *drm = calloc(1, sizeof(struct wlr_drm_backend));
@ -161,28 +191,40 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
wlr_backend_init(&drm->backend, &backend_impl);
drm->session = session;
wl_list_init(&drm->fbs);
wl_list_init(&drm->outputs);
drm->fd = gpu_fd;
drm->dev = dev;
drm->fd = dev->fd;
drm->name = name;
if (parent != NULL) {
drm->parent = get_drm_backend_from_backend(parent);
drm->parent_destroy.notify = handle_parent_destroy;
wl_signal_add(&parent->events.destroy, &drm->parent_destroy);
} else {
wl_list_init(&drm->parent_destroy.link);
}
drm->drm_invalidated.notify = drm_invalidated;
wlr_session_signal_add(session, gpu_fd, &drm->drm_invalidated);
drm->dev_change.notify = handle_dev_change;
wl_signal_add(&dev->events.change, &drm->dev_change);
drm->dev_remove.notify = handle_dev_remove;
wl_signal_add(&dev->events.remove, &drm->dev_remove);
drm->display = display;
struct wl_event_loop *event_loop = wl_display_get_event_loop(display);
struct wl_event_loop *event_loop = wl_display_get_event_loop(display);
drm->drm_event = wl_event_loop_add_fd(event_loop, drm->fd,
WL_EVENT_READABLE, handle_drm_event, NULL);
WL_EVENT_READABLE, handle_drm_event, drm);
if (!drm->drm_event) {
wlr_log(WLR_ERROR, "Failed to create DRM event source");
goto error_fd;
}
drm->session_signal.notify = session_signal;
wl_signal_add(&session->session_signal, &drm->session_signal);
drm->session_active.notify = handle_session_active;
wl_signal_add(&session->events.active, &drm->session_active);
if (!check_drm_features(drm)) {
goto error_event;
@ -192,21 +234,54 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
goto error_event;
}
if (!init_drm_renderer(drm, &drm->renderer, create_renderer_func)) {
wlr_log(WLR_ERROR, "Failed to initialize renderer");
goto error_event;
if (drm->parent) {
if (!init_drm_renderer(drm, &drm->mgpu_renderer)) {
wlr_log(WLR_ERROR, "Failed to initialize renderer");
goto error_resources;
}
// We'll perform a multi-GPU copy for all submitted buffers, we need
// to be able to texture from them
struct wlr_renderer *renderer = drm->mgpu_renderer.wlr_rend;
const struct wlr_drm_format_set *texture_formats =
wlr_renderer_get_dmabuf_texture_formats(renderer);
if (texture_formats == NULL) {
wlr_log(WLR_ERROR, "Failed to query renderer texture formats");
goto error_mgpu_renderer;
}
// Force a linear layout. In case explicit modifiers aren't supported,
// the meaning of implicit modifiers changes from one GPU to the other.
// In case explicit modifiers are supported, we still have no guarantee
// that the buffer producer will support these, so they might fallback
// to implicit modifiers.
for (size_t i = 0; i < texture_formats->len; i++) {
const struct wlr_drm_format *fmt = texture_formats->formats[i];
wlr_drm_format_set_add(&drm->mgpu_formats, fmt->format,
DRM_FORMAT_MOD_LINEAR);
}
}
drm->session_destroy.notify = handle_session_destroy;
wl_signal_add(&session->events.destroy, &drm->session_destroy);
drm->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &drm->display_destroy);
return &drm->backend;
error_mgpu_renderer:
finish_drm_renderer(&drm->mgpu_renderer);
error_resources:
finish_drm_resources(drm);
error_event:
wl_list_remove(&drm->session_signal.link);
wl_list_remove(&drm->session_active.link);
wl_event_source_remove(drm->drm_event);
error_fd:
wlr_session_close_file(drm->session, drm->fd);
wl_list_remove(&drm->dev_remove.link);
wl_list_remove(&drm->dev_change.link);
wl_list_remove(&drm->parent_destroy.link);
wlr_session_close_file(drm->session, dev);
free(drm);
return NULL;
}

269
backend/drm/cvt.c Normal file
View File

@ -0,0 +1,269 @@
/*
* Copyright 2005-2006 Luc Verhaegen.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "backend/drm/cvt.h"
/* top/bottom margin size (% of height) - default: 1.8 */
#define CVT_MARGIN_PERCENTAGE 1.8
/* character cell horizontal granularity (pixels) - default 8 */
#define CVT_H_GRANULARITY 8
/* Minimum vertical porch (lines) - default 3 */
#define CVT_MIN_V_PORCH 3
/* Minimum number of vertical back porch lines - default 6 */
#define CVT_MIN_V_BPORCH 6
/* Pixel clock step (kHz) */
#define CVT_CLOCK_STEP 250
/* Minimum time of vertical sync + back porch interval (µs)
* default 550.0 */
#define CVT_MIN_VSYNC_BP 550.0
/* Nominal hsync width (% of line period) - default 8 */
#define CVT_HSYNC_PERCENTAGE 8
/* Definition of Horizontal blanking time limitation */
/* Gradient (%/kHz) - default 600 */
#define CVT_M_FACTOR 600
/* Offset (%) - default 40 */
#define CVT_C_FACTOR 40
/* Blanking time scaling factor - default 128 */
#define CVT_K_FACTOR 128
/* Scaling factor weighting - default 20 */
#define CVT_J_FACTOR 20
#define CVT_M_PRIME CVT_M_FACTOR * CVT_K_FACTOR / 256
#define CVT_C_PRIME (CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \
CVT_J_FACTOR
/* Minimum vertical blanking interval time (µs) - default 460 */
#define CVT_RB_MIN_VBLANK 460.0
/* Fixed number of clocks for horizontal sync */
#define CVT_RB_H_SYNC 32.0
/* Fixed number of clocks for horizontal blanking */
#define CVT_RB_H_BLANK 160.0
/* Fixed number of lines for vertical front porch - default 3 */
#define CVT_RB_VFPORCH 3
/*
* Generate a CVT standard mode from hdisplay, vdisplay and vrefresh.
*
* These calculations are stolen from the CVT calculation spreadsheet written
* by Graham Loveridge. He seems to be claiming no copyright and there seems to
* be no license attached to this. He apparently just wants to see his name
* mentioned.
*
* This file can be found at http://www.vesa.org/Public/CVT/CVTd6r1.xls
*
* Comments and structure corresponds to the comments and structure of the xls.
* This should ease importing of future changes to the standard (not very
* likely though).
*
* This function is borrowed from xorg-xserver's xf86CVTmode.
*/
void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay,
float vrefresh, bool reduced, bool interlaced) {
bool margins = false;
float vfield_rate, hperiod;
int hdisplay_rnd, hmargin;
int vdisplay_rnd, vmargin, vsync;
float interlace; /* Please rename this */
/* CVT default is 60.0Hz */
if (!vrefresh) {
vrefresh = 60.0;
}
/* 1. Required field rate */
if (interlaced) {
vfield_rate = vrefresh * 2;
} else {
vfield_rate = vrefresh;
}
/* 2. Horizontal pixels */
hdisplay_rnd = hdisplay - (hdisplay % CVT_H_GRANULARITY);
/* 3. Determine left and right borders */
if (margins) {
/* right margin is actually exactly the same as left */
hmargin = (((float) hdisplay_rnd) * CVT_MARGIN_PERCENTAGE / 100.0);
hmargin -= hmargin % CVT_H_GRANULARITY;
} else {
hmargin = 0;
}
/* 4. Find total active pixels */
mode->hdisplay = hdisplay_rnd + 2 * hmargin;
/* 5. Find number of lines per field */
if (interlaced) {
vdisplay_rnd = vdisplay / 2;
} else {
vdisplay_rnd = vdisplay;
}
/* 6. Find top and bottom margins */
/* nope. */
if (margins) {
/* top and bottom margins are equal again. */
vmargin = (((float) vdisplay_rnd) * CVT_MARGIN_PERCENTAGE / 100.0);
} else {
vmargin = 0;
}
mode->vdisplay = vdisplay + 2 * vmargin;
/* 7. interlace */
if (interlaced) {
interlace = 0.5;
} else {
interlace = 0.0;
}
/* Determine vsync Width from aspect ratio */
if (!(vdisplay % 3) && ((vdisplay * 4 / 3) == hdisplay)) {
vsync = 4;
} else if (!(vdisplay % 9) && ((vdisplay * 16 / 9) == hdisplay)) {
vsync = 5;
} else if (!(vdisplay % 10) && ((vdisplay * 16 / 10) == hdisplay)) {
vsync = 6;
} else if (!(vdisplay % 4) && ((vdisplay * 5 / 4) == hdisplay)) {
vsync = 7;
} else if (!(vdisplay % 9) && ((vdisplay * 15 / 9) == hdisplay)) {
vsync = 7;
} else { /* Custom */
vsync = 10;
}
if (!reduced) { /* simplified GTF calculation */
float hblank_percentage;
int vsync_and_back_porch, vblank_porch;
int hblank;
/* 8. Estimated Horizontal period */
hperiod = ((float) (1000000.0 / vfield_rate - CVT_MIN_VSYNC_BP)) /
(vdisplay_rnd + 2 * vmargin + CVT_MIN_V_PORCH + interlace);
/* 9. Find number of lines in sync + backporch */
if (((int) (CVT_MIN_VSYNC_BP / hperiod) + 1) <
(vsync + CVT_MIN_V_PORCH)) {
vsync_and_back_porch = vsync + CVT_MIN_V_PORCH;
} else {
vsync_and_back_porch = (int) (CVT_MIN_VSYNC_BP / hperiod) + 1;
}
/* 10. Find number of lines in back porch */
vblank_porch = vsync_and_back_porch - vsync;
(void) vblank_porch;
/* 11. Find total number of lines in vertical field */
mode->vtotal = vdisplay_rnd + 2 * vmargin + vsync_and_back_porch + interlace
+ CVT_MIN_V_PORCH;
/* 12. Find ideal blanking duty cycle from formula */
hblank_percentage = CVT_C_PRIME - CVT_M_PRIME * hperiod / 1000.0;
/* 13. Blanking time */
if (hblank_percentage < 20) {
hblank_percentage = 20;
}
hblank = mode->hdisplay * hblank_percentage / (100.0 - hblank_percentage);
hblank -= hblank % (2 * CVT_H_GRANULARITY);
/* 14. Find total number of pixels in a line. */
mode->htotal = mode->hdisplay + hblank;
/* Fill in hsync values */
mode->hsync_end = mode->hdisplay + hblank / 2;
mode->hsync_start = mode->hsync_end -
(mode->htotal * CVT_HSYNC_PERCENTAGE) / 100;
mode->hsync_start += CVT_H_GRANULARITY -
mode->hsync_start % CVT_H_GRANULARITY;
/* Fill in vsync values */
mode->vsync_start = mode->vdisplay + CVT_MIN_V_PORCH;
mode->vsync_end = mode->vsync_start + vsync;
} else { /* reduced blanking */
int vbi_lines;
/* 8. Estimate Horizontal period. */
hperiod = ((float) (1000000.0 / vfield_rate - CVT_RB_MIN_VBLANK)) /
(vdisplay_rnd + 2 * vmargin);
/* 9. Find number of lines in vertical blanking */
vbi_lines = ((float) CVT_RB_MIN_VBLANK) / hperiod + 1;
/* 10. Check if vertical blanking is sufficient */
if (vbi_lines < (CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH)) {
vbi_lines = CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH;
}
/* 11. Find total number of lines in vertical field */
mode->vtotal = vdisplay_rnd + 2 * vmargin + interlace + vbi_lines;
/* 12. Find total number of pixels in a line */
mode->htotal = mode->hdisplay + CVT_RB_H_BLANK;
/* Fill in hsync values */
mode->hsync_end = mode->hdisplay + CVT_RB_H_BLANK / 2;
mode->hsync_start = mode->hsync_end - CVT_RB_H_SYNC;
/* Fill in vsync values */
mode->vsync_start = mode->vdisplay + CVT_RB_VFPORCH;
mode->vsync_end = mode->vsync_start + vsync;
}
/* 15/13. Find pixel clock frequency (kHz for xf86) */
mode->clock = mode->htotal * 1000.0 / hperiod;
mode->clock -= mode->clock % CVT_CLOCK_STEP;
/* 17/15. Find actual Field rate */
mode->vrefresh = (1000.0 * ((float) mode->clock)) /
((float) (mode->htotal * mode->vtotal));
/* 18/16. Find actual vertical frame frequency */
/* ignore - just set the mode flag for interlaced */
if (interlaced) {
mode->vtotal *= 2;
mode->flags |= DRM_MODE_FLAG_INTERLACE;
}
snprintf(mode->name, sizeof(mode->name), "%dx%d", hdisplay, vdisplay);
if (reduced) {
mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC;
} else {
mode->flags |= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
#include <gbm.h>
#include <assert.h>
#include <stdlib.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
@ -6,78 +7,225 @@
#include "backend/drm/iface.h"
#include "backend/drm/util.h"
static bool legacy_crtc_pageflip(struct wlr_drm_backend *drm,
struct wlr_drm_connector *conn, struct wlr_drm_crtc *crtc,
uint32_t fb_id, drmModeModeInfo *mode) {
if (mode) {
static bool legacy_fb_props_match(struct wlr_drm_fb *fb1,
struct wlr_drm_fb *fb2) {
struct wlr_dmabuf_attributes dmabuf1 = {0}, dmabuf2 = {0};
if (!wlr_buffer_get_dmabuf(fb1->wlr_buf, &dmabuf1) ||
!wlr_buffer_get_dmabuf(fb2->wlr_buf, &dmabuf2)) {
return false;
}
if (dmabuf1.width != dmabuf2.width ||
dmabuf1.height != dmabuf2.height ||
dmabuf1.format != dmabuf2.format ||
dmabuf1.modifier != dmabuf2.modifier ||
dmabuf1.n_planes != dmabuf2.n_planes) {
return false;
}
for (int i = 0; i < dmabuf1.n_planes; i++) {
if (dmabuf1.stride[i] != dmabuf2.stride[i] ||
dmabuf1.offset[i] != dmabuf2.offset[i]) {
return false;
}
}
return true;
}
static bool legacy_crtc_test(struct wlr_drm_connector *conn,
const struct wlr_drm_connector_state *state) {
struct wlr_drm_crtc *crtc = conn->crtc;
if ((state->base->committed & WLR_OUTPUT_STATE_BUFFER) && !state->modeset) {
struct wlr_drm_fb *pending_fb = crtc->primary->pending_fb;
struct wlr_drm_fb *prev_fb = crtc->primary->queued_fb;
if (!prev_fb) {
prev_fb = crtc->primary->current_fb;
}
/* Legacy is only guaranteed to be able to display a FB if it's been
* allocated the same way as the previous one. */
if (prev_fb != NULL && !legacy_fb_props_match(prev_fb, pending_fb)) {
wlr_drm_conn_log(conn, WLR_DEBUG,
"Cannot change scan-out buffer parameters with legacy KMS API");
return false;
}
}
return true;
}
static bool legacy_crtc_commit(struct wlr_drm_connector *conn,
const struct wlr_drm_connector_state *state,
uint32_t flags, bool test_only) {
if (!legacy_crtc_test(conn, state)) {
return false;
}
if (test_only) {
return true;
}
struct wlr_drm_backend *drm = conn->backend;
struct wlr_output *output = &conn->output;
struct wlr_drm_crtc *crtc = conn->crtc;
struct wlr_drm_plane *cursor = crtc->cursor;
uint32_t fb_id = 0;
if (state->active) {
struct wlr_drm_fb *fb = plane_get_next_fb(crtc->primary);
if (fb == NULL) {
wlr_log(WLR_ERROR, "%s: failed to acquire primary FB",
conn->output.name);
return false;
}
fb_id = fb->id;
}
if (state->modeset) {
uint32_t *conns = NULL;
size_t conns_len = 0;
drmModeModeInfo *mode = NULL;
if (state->active) {
conns = &conn->id;
conns_len = 1;
mode = (drmModeModeInfo *)&state->mode;
}
uint32_t dpms = state->active ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF;
if (drmModeConnectorSetProperty(drm->fd, conn->id, conn->props.dpms,
dpms) != 0) {
wlr_drm_conn_log_errno(conn, WLR_ERROR,
"Failed to set DPMS property");
return false;
}
if (drmModeSetCrtc(drm->fd, crtc->id, fb_id, 0, 0,
&conn->id, 1, mode)) {
wlr_log_errno(WLR_ERROR, "%s: Failed to set CRTC", conn->output.name);
conns, conns_len, mode)) {
wlr_drm_conn_log_errno(conn, WLR_ERROR, "Failed to set CRTC");
return false;
}
}
if (drmModePageFlip(drm->fd, crtc->id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, conn)) {
wlr_log_errno(WLR_ERROR, "%s: Failed to page flip", conn->output.name);
return false;
if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) {
if (!drm_legacy_crtc_set_gamma(drm, crtc,
state->base->gamma_lut_size, state->base->gamma_lut)) {
return false;
}
}
return true;
}
static bool legacy_conn_enable(struct wlr_drm_backend *drm,
struct wlr_drm_connector *conn, bool enable) {
int ret = drmModeConnectorSetProperty(drm->fd, conn->id, conn->props.dpms,
enable ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF);
return ret >= 0;
}
bool legacy_crtc_set_cursor(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, struct gbm_bo *bo) {
if (!crtc || !crtc->cursor) {
return true;
if ((state->base->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) &&
drm_connector_supports_vrr(conn)) {
if (drmModeObjectSetProperty(drm->fd, crtc->id, DRM_MODE_OBJECT_CRTC,
crtc->props.vrr_enabled,
state->base->adaptive_sync_enabled) != 0) {
wlr_drm_conn_log_errno(conn, WLR_ERROR,
"drmModeObjectSetProperty(VRR_ENABLED) failed");
return false;
}
output->adaptive_sync_status = state->base->adaptive_sync_enabled ?
WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED :
WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED;
wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s",
state->base->adaptive_sync_enabled ? "enabled" : "disabled");
}
if (!bo) {
if (cursor != NULL && drm_connector_is_cursor_visible(conn)) {
struct wlr_drm_fb *cursor_fb = plane_get_next_fb(cursor);
if (cursor_fb == NULL) {
wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to acquire cursor FB");
return false;
}
drmModeFB *drm_fb = drmModeGetFB(drm->fd, cursor_fb->id);
if (drm_fb == NULL) {
wlr_drm_conn_log_errno(conn, WLR_DEBUG, "Failed to get cursor "
"BO handle: drmModeGetFB failed");
return false;
}
uint32_t cursor_handle = drm_fb->handle;
uint32_t cursor_width = drm_fb->width;
uint32_t cursor_height = drm_fb->height;
drmModeFreeFB(drm_fb);
int ret = drmModeSetCursor(drm->fd, crtc->id, cursor_handle,
cursor_width, cursor_height);
int set_cursor_errno = errno;
if (drmCloseBufferHandle(drm->fd, cursor_handle) != 0) {
wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed");
}
if (ret != 0) {
wlr_drm_conn_log(conn, WLR_DEBUG, "drmModeSetCursor failed: %s",
strerror(set_cursor_errno));
return false;
}
if (drmModeMoveCursor(drm->fd,
crtc->id, conn->cursor_x, conn->cursor_y) != 0) {
wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModeMoveCursor failed");
return false;
}
} else {
if (drmModeSetCursor(drm->fd, crtc->id, 0, 0, 0)) {
wlr_log_errno(WLR_DEBUG, "Failed to clear hardware cursor");
wlr_drm_conn_log_errno(conn, WLR_DEBUG, "drmModeSetCursor failed");
return false;
}
return true;
}
struct wlr_drm_plane *plane = crtc->cursor;
if (drmModeSetCursor(drm->fd, crtc->id, gbm_bo_get_handle(bo).u32,
plane->surf.width, plane->surf.height)) {
wlr_log_errno(WLR_DEBUG, "Failed to set hardware cursor");
return false;
if (flags & DRM_MODE_PAGE_FLIP_EVENT) {
if (drmModePageFlip(drm->fd, crtc->id, fb_id,
DRM_MODE_PAGE_FLIP_EVENT, drm)) {
wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModePageFlip failed");
return false;
}
}
return true;
}
bool legacy_crtc_move_cursor(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, int x, int y) {
return !drmModeMoveCursor(drm->fd, crtc->id, x, y);
}
bool legacy_crtc_set_gamma(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, size_t size,
static void fill_empty_gamma_table(size_t size,
uint16_t *r, uint16_t *g, uint16_t *b) {
return !drmModeCrtcSetGamma(drm->fd, crtc->id, (uint32_t)size, r, g, b);
assert(0xFFFF < UINT64_MAX / (size - 1));
for (uint32_t i = 0; i < size; ++i) {
uint16_t val = (uint64_t)0xFFFF * i / (size - 1);
r[i] = g[i] = b[i] = val;
}
}
size_t legacy_crtc_get_gamma_size(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc) {
return (size_t)crtc->legacy_crtc->gamma_size;
bool drm_legacy_crtc_set_gamma(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, size_t size, uint16_t *lut) {
uint16_t *linear_lut = NULL;
if (size == 0) {
// The legacy interface doesn't offer a way to reset the gamma LUT
size = drm_crtc_get_gamma_lut_size(drm, crtc);
if (size == 0) {
return false;
}
linear_lut = malloc(3 * size * sizeof(uint16_t));
if (linear_lut == NULL) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return false;
}
fill_empty_gamma_table(size, linear_lut, linear_lut + size,
linear_lut + 2 * size);
lut = linear_lut;
}
uint16_t *r = lut, *g = lut + size, *b = lut + 2 * size;
if (drmModeCrtcSetGamma(drm->fd, crtc->id, size, r, g, b) != 0) {
wlr_log_errno(WLR_ERROR, "Failed to set gamma LUT on CRTC %"PRIu32,
crtc->id);
free(linear_lut);
return false;
}
free(linear_lut);
return true;
}
const struct wlr_drm_interface legacy_iface = {
.conn_enable = legacy_conn_enable,
.crtc_pageflip = legacy_crtc_pageflip,
.crtc_set_cursor = legacy_crtc_set_cursor,
.crtc_move_cursor = legacy_crtc_move_cursor,
.crtc_set_gamma = legacy_crtc_set_gamma,
.crtc_get_gamma_size = legacy_crtc_get_gamma_size,
.crtc_commit = legacy_crtc_commit,
};

13
backend/drm/meson.build Normal file
View File

@ -0,0 +1,13 @@
wlr_files += files(
'atomic.c',
'backend.c',
'cvt.c',
'drm.c',
'legacy.c',
'monitor.c',
'properties.c',
'renderer.c',
'util.c',
)
features += { 'drm-backend': true }

94
backend/drm/monitor.c Normal file
View File

@ -0,0 +1,94 @@
#include <wlr/util/log.h>
#include <stdlib.h>
#include "backend/drm/monitor.h"
#include "backend/multi.h"
#include "backend/session/session.h"
static void drm_backend_monitor_destroy(struct wlr_drm_backend_monitor* monitor) {
wl_list_remove(&monitor->session_add_drm_card.link);
wl_list_remove(&monitor->session_destroy.link);
wl_list_remove(&monitor->primary_drm_destroy.link);
wl_list_remove(&monitor->multi_destroy.link);
free(monitor);
}
static void handle_add_drm_card(struct wl_listener *listener, void *data) {
struct wlr_session_add_event *event = data;
struct wlr_drm_backend_monitor *backend_monitor =
wl_container_of(listener, backend_monitor, session_add_drm_card);
struct wlr_device *dev =
session_open_if_kms(backend_monitor->session, event->path);
if (!dev) {
wlr_log(WLR_ERROR, "Unable to open %s as DRM device", event->path);
return;
}
wlr_log(WLR_DEBUG, "Creating DRM backend for %s after hotplug", event->path);
struct wlr_backend *child_drm = wlr_drm_backend_create(
backend_monitor->session->display, backend_monitor->session,
dev, backend_monitor->primary_drm);
if (!child_drm) {
wlr_log(WLR_ERROR, "Failed to create DRM backend after hotplug");
return;
}
if (!wlr_multi_backend_add(backend_monitor->multi, child_drm)) {
wlr_log(WLR_ERROR, "Failed to add new drm backend to multi backend");
wlr_backend_destroy(child_drm);
return;
}
if (!wlr_backend_start(child_drm)) {
wlr_log(WLR_ERROR, "Failed to start new child DRM backend");
wlr_backend_destroy(child_drm);
}
}
static void handle_session_destroy(struct wl_listener *listener, void *data) {
struct wlr_drm_backend_monitor *backend_monitor =
wl_container_of(listener, backend_monitor, session_destroy);
drm_backend_monitor_destroy(backend_monitor);
}
static void handle_primary_drm_destroy(struct wl_listener *listener, void *data) {
struct wlr_drm_backend_monitor *backend_monitor =
wl_container_of(listener, backend_monitor, primary_drm_destroy);
drm_backend_monitor_destroy(backend_monitor);
}
static void handle_multi_destroy(struct wl_listener *listener, void *data) {
struct wlr_drm_backend_monitor *backend_monitor =
wl_container_of(listener, backend_monitor, multi_destroy);
drm_backend_monitor_destroy(backend_monitor);
}
struct wlr_drm_backend_monitor *drm_backend_monitor_create(
struct wlr_backend *multi,
struct wlr_backend *primary_drm,
struct wlr_session *session) {
struct wlr_drm_backend_monitor *monitor =
calloc(1, sizeof(struct wlr_drm_backend_monitor));
if (!monitor) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return NULL;
}
monitor->multi = multi;
monitor->primary_drm = primary_drm;
monitor->session = session;
monitor->session_add_drm_card.notify = handle_add_drm_card;
wl_signal_add(&session->events.add_drm_card, &monitor->session_add_drm_card);
monitor->session_destroy.notify = handle_session_destroy;
wl_signal_add(&session->events.destroy, &monitor->session_destroy);
monitor->primary_drm_destroy.notify = handle_primary_drm_destroy;
wl_signal_add(&primary_drm->events.destroy, &monitor->primary_drm_destroy);
monitor->multi_destroy.notify = handle_multi_destroy;
wl_signal_add(&multi->events.destroy, &monitor->multi_destroy);
return monitor;
}

View File

@ -1,3 +1,4 @@
#define _POSIX_C_SOURCE 200809L
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@ -19,38 +20,44 @@ struct prop_info {
static const struct prop_info connector_info[] = {
#define INDEX(name) (offsetof(union wlr_drm_connector_props, name) / sizeof(uint32_t))
{ "CRTC_ID", INDEX(crtc_id) },
{ "DPMS", INDEX(dpms) },
{ "EDID", INDEX(edid) },
{ "PATH", INDEX(path) },
{ "CRTC_ID", INDEX(crtc_id) },
{ "DPMS", INDEX(dpms) },
{ "EDID", INDEX(edid) },
{ "PATH", INDEX(path) },
{ "link-status", INDEX(link_status) },
{ "non-desktop", INDEX(non_desktop) },
{ "panel orientation", INDEX(panel_orientation) },
{ "subconnector", INDEX(subconnector) },
{ "vrr_capable", INDEX(vrr_capable) },
#undef INDEX
};
static const struct prop_info crtc_info[] = {
#define INDEX(name) (offsetof(union wlr_drm_crtc_props, name) / sizeof(uint32_t))
{ "ACTIVE", INDEX(active) },
{ "GAMMA_LUT", INDEX(gamma_lut) },
{ "ACTIVE", INDEX(active) },
{ "GAMMA_LUT", INDEX(gamma_lut) },
{ "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) },
{ "MODE_ID", INDEX(mode_id) },
{ "rotation", INDEX(rotation) },
{ "scaling mode", INDEX(scaling_mode) },
{ "MODE_ID", INDEX(mode_id) },
{ "VRR_ENABLED", INDEX(vrr_enabled) },
#undef INDEX
};
static const struct prop_info plane_info[] = {
#define INDEX(name) (offsetof(union wlr_drm_plane_props, name) / sizeof(uint32_t))
{ "CRTC_H", INDEX(crtc_h) },
{ "CRTC_H", INDEX(crtc_h) },
{ "CRTC_ID", INDEX(crtc_id) },
{ "CRTC_W", INDEX(crtc_w) },
{ "CRTC_X", INDEX(crtc_x) },
{ "CRTC_Y", INDEX(crtc_y) },
{ "FB_ID", INDEX(fb_id) },
{ "SRC_H", INDEX(src_h) },
{ "SRC_W", INDEX(src_w) },
{ "SRC_X", INDEX(src_x) },
{ "SRC_Y", INDEX(src_y) },
{ "type", INDEX(type) },
{ "CRTC_W", INDEX(crtc_w) },
{ "CRTC_X", INDEX(crtc_x) },
{ "CRTC_Y", INDEX(crtc_y) },
{ "FB_DAMAGE_CLIPS", INDEX(fb_damage_clips) },
{ "FB_ID", INDEX(fb_id) },
{ "IN_FORMATS", INDEX(in_formats) },
{ "SRC_H", INDEX(src_h) },
{ "SRC_W", INDEX(src_w) },
{ "SRC_X", INDEX(src_x) },
{ "SRC_Y", INDEX(src_y) },
{ "rotation", INDEX(rotation) },
{ "type", INDEX(type) },
#undef INDEX
};
@ -149,3 +156,27 @@ void *get_drm_prop_blob(int fd, uint32_t obj, uint32_t prop, size_t *ret_len) {
drmModeFreePropertyBlob(blob);
return ptr;
}
char *get_drm_prop_enum(int fd, uint32_t obj, uint32_t prop_id) {
uint64_t value;
if (!get_drm_prop(fd, obj, prop_id, &value)) {
return NULL;
}
drmModePropertyRes *prop = drmModeGetProperty(fd, prop_id);
if (!prop) {
return NULL;
}
char *str = NULL;
for (int i = 0; i < prop->count_enums; i++) {
if (prop->enums[i].value == value) {
str = strdup(prop->enums[i].name);
break;
}
}
drmModeFreeProperty(prop);
return str;
}

View File

@ -1,54 +1,42 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <gbm.h>
#include <drm_fourcc.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wayland-util.h>
#include <wlr/render/egl.h>
#include <wlr/render/gles2.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/util/log.h>
#include "backend/drm/drm.h"
#include "glapi.h"
#include "backend/drm/util.h"
#include "render/drm_format_set.h"
#include "render/allocator/allocator.h"
#include "render/pixel_format.h"
#include "render/swapchain.h"
#include "render/wlr_renderer.h"
bool init_drm_renderer(struct wlr_drm_backend *drm,
struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_renderer_func) {
renderer->gbm = gbm_create_device(drm->fd);
if (!renderer->gbm) {
wlr_log(WLR_ERROR, "Failed to create GBM device");
struct wlr_drm_renderer *renderer) {
renderer->backend = drm;
renderer->wlr_rend = renderer_autocreate_with_drm_fd(drm->fd);
if (!renderer->wlr_rend) {
wlr_log(WLR_ERROR, "Failed to create renderer");
return false;
}
if (!create_renderer_func) {
create_renderer_func = wlr_renderer_autocreate;
renderer->allocator = allocator_autocreate_with_drm_fd(&drm->backend,
renderer->wlr_rend, drm->fd);
if (renderer->allocator == NULL) {
wlr_log(WLR_ERROR, "Failed to create allocator");
wlr_renderer_destroy(renderer->wlr_rend);
return false;
}
static EGLint config_attribs[] = {
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 1,
EGL_NONE,
};
renderer->gbm_format = GBM_FORMAT_ARGB8888;
renderer->wlr_rend = create_renderer_func(&renderer->egl,
EGL_PLATFORM_GBM_MESA, renderer->gbm,
config_attribs, renderer->gbm_format);
if (!renderer->wlr_rend) {
wlr_log(WLR_ERROR, "Failed to create EGL/WLR renderer");
goto error_gbm;
}
renderer->fd = drm->fd;
return true;
error_gbm:
gbm_device_destroy(renderer->gbm);
return false;
}
void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
@ -56,14 +44,13 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
return;
}
wlr_allocator_destroy(renderer->allocator);
wlr_renderer_destroy(renderer->wlr_rend);
wlr_egl_finish(&renderer->egl);
gbm_device_destroy(renderer->gbm);
}
bool init_drm_surface(struct wlr_drm_surface *surf,
struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
uint32_t format, uint32_t flags) {
const struct wlr_drm_format *drm_format) {
if (surf->width == width && surf->height == height) {
return true;
}
@ -72,189 +59,357 @@ bool init_drm_surface(struct wlr_drm_surface *surf,
surf->width = width;
surf->height = height;
if (surf->gbm) {
if (surf->front) {
gbm_surface_release_buffer(surf->gbm, surf->front);
surf->front = NULL;
}
if (surf->back) {
gbm_surface_release_buffer(surf->gbm, surf->back);
surf->back = NULL;
}
gbm_surface_destroy(surf->gbm);
}
wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl);
wlr_swapchain_destroy(surf->swapchain);
surf->swapchain = NULL;
surf->gbm = gbm_surface_create(renderer->gbm, width, height,
format, GBM_BO_USE_RENDERING | flags);
if (!surf->gbm) {
wlr_log_errno(WLR_ERROR, "Failed to create GBM surface");
goto error_zero;
}
surf->egl = wlr_egl_create_surface(&renderer->egl, surf->gbm);
if (surf->egl == EGL_NO_SURFACE) {
wlr_log(WLR_ERROR, "Failed to create EGL surface");
goto error_gbm;
surf->swapchain = wlr_swapchain_create(renderer->allocator, width, height,
drm_format);
if (surf->swapchain == NULL) {
wlr_log(WLR_ERROR, "Failed to create swapchain");
memset(surf, 0, sizeof(*surf));
return false;
}
return true;
error_gbm:
gbm_surface_destroy(surf->gbm);
error_zero:
memset(surf, 0, sizeof(*surf));
return false;
}
void finish_drm_surface(struct wlr_drm_surface *surf) {
static void finish_drm_surface(struct wlr_drm_surface *surf) {
if (!surf || !surf->renderer) {
return;
}
if (surf->front) {
gbm_surface_release_buffer(surf->gbm, surf->front);
}
if (surf->back) {
gbm_surface_release_buffer(surf->gbm, surf->back);
}
wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl);
if (surf->gbm) {
gbm_surface_destroy(surf->gbm);
}
wlr_swapchain_destroy(surf->swapchain);
memset(surf, 0, sizeof(*surf));
}
bool make_drm_surface_current(struct wlr_drm_surface *surf,
int *buffer_damage) {
return wlr_egl_make_current(&surf->renderer->egl, surf->egl, buffer_damage);
}
struct gbm_bo *swap_drm_surface_buffers(struct wlr_drm_surface *surf,
pixman_region32_t *damage) {
if (surf->front) {
gbm_surface_release_buffer(surf->gbm, surf->front);
}
wlr_egl_swap_buffers(&surf->renderer->egl, surf->egl, damage);
surf->front = surf->back;
surf->back = gbm_surface_lock_front_buffer(surf->gbm);
return surf->back;
}
struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf) {
if (surf->front) {
return surf->front;
}
make_drm_surface_current(surf, NULL);
struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf,
struct wlr_buffer *buffer) {
struct wlr_renderer *renderer = surf->renderer->wlr_rend;
wlr_renderer_begin(renderer, surf->width, surf->height);
wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 1.0 });
if (surf->width != (uint32_t)buffer->width ||
surf->height != (uint32_t)buffer->height) {
wlr_log(WLR_ERROR, "Surface size doesn't match buffer size");
return NULL;
}
struct wlr_texture *tex = wlr_texture_from_buffer(renderer, buffer);
if (tex == NULL) {
return NULL;
}
struct wlr_buffer *dst = wlr_swapchain_acquire(surf->swapchain, NULL);
if (!dst) {
wlr_texture_destroy(tex);
return NULL;
}
float mat[9];
wlr_matrix_identity(mat);
wlr_matrix_scale(mat, surf->width, surf->height);
if (!wlr_renderer_begin_with_buffer(renderer, dst)) {
wlr_buffer_unlock(dst);
wlr_texture_destroy(tex);
return NULL;
}
wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f);
wlr_renderer_end(renderer);
return swap_drm_surface_buffers(surf, NULL);
wlr_texture_destroy(tex);
return dst;
}
void post_drm_surface(struct wlr_drm_surface *surf) {
if (surf->front) {
gbm_surface_release_buffer(surf->gbm, surf->front);
surf->front = NULL;
void drm_plane_finish_surface(struct wlr_drm_plane *plane) {
if (!plane) {
return;
}
drm_fb_clear(&plane->pending_fb);
drm_fb_clear(&plane->queued_fb);
drm_fb_clear(&plane->current_fb);
finish_drm_surface(&plane->mgpu_surf);
}
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs) {
memset(attribs, 0, sizeof(struct wlr_dmabuf_attributes));
attribs->n_planes = gbm_bo_get_plane_count(bo);
if (attribs->n_planes > WLR_DMABUF_MAX_PLANES) {
return false;
struct wlr_drm_format *drm_plane_pick_render_format(
struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer) {
const struct wlr_drm_format_set *render_formats =
wlr_renderer_get_render_formats(renderer->wlr_rend);
if (render_formats == NULL) {
wlr_log(WLR_ERROR, "Failed to get render formats");
return NULL;
}
attribs->width = gbm_bo_get_width(bo);
attribs->height = gbm_bo_get_height(bo);
attribs->format = gbm_bo_get_format(bo);
attribs->modifier = gbm_bo_get_modifier(bo);
const struct wlr_drm_format_set *plane_formats = &plane->formats;
for (int i = 0; i < attribs->n_planes; ++i) {
attribs->offset[i] = gbm_bo_get_offset(bo, i);
attribs->stride[i] = gbm_bo_get_stride_for_plane(bo, i);
attribs->fd[i] = gbm_bo_get_fd(bo);
if (attribs->fd[i] < 0) {
for (int j = 0; j < i; ++j) {
close(attribs->fd[j]);
uint32_t fmt = DRM_FORMAT_ARGB8888;
if (!wlr_drm_format_set_get(&plane->formats, fmt)) {
const struct wlr_pixel_format_info *format_info =
drm_get_pixel_format_info(fmt);
assert(format_info != NULL &&
format_info->opaque_substitute != DRM_FORMAT_INVALID);
fmt = format_info->opaque_substitute;
}
const struct wlr_drm_format *render_format =
wlr_drm_format_set_get(render_formats, fmt);
if (render_format == NULL) {
wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%"PRIX32, fmt);
return NULL;
}
const struct wlr_drm_format *plane_format =
wlr_drm_format_set_get(plane_formats, fmt);
if (plane_format == NULL) {
wlr_log(WLR_DEBUG, "Plane %"PRIu32" doesn't support format 0x%"PRIX32,
plane->id, fmt);
return NULL;
}
struct wlr_drm_format *format =
wlr_drm_format_intersect(plane_format, render_format);
if (format == NULL) {
wlr_log(WLR_DEBUG, "Failed to intersect plane and render "
"modifiers for format 0x%"PRIX32, fmt);
return NULL;
}
return format;
}
void drm_fb_clear(struct wlr_drm_fb **fb_ptr) {
if (*fb_ptr == NULL) {
return;
}
struct wlr_drm_fb *fb = *fb_ptr;
wlr_buffer_unlock(fb->wlr_buf); // may destroy the buffer
*fb_ptr = NULL;
}
static void drm_fb_handle_destroy(struct wlr_addon *addon) {
struct wlr_drm_fb *fb = wl_container_of(addon, fb, addon);
drm_fb_destroy(fb);
}
static const struct wlr_addon_interface fb_addon_impl = {
.name = "wlr_drm_fb",
.destroy = drm_fb_handle_destroy,
};
static uint32_t get_fb_for_bo(struct wlr_drm_backend *drm,
struct wlr_dmabuf_attributes *dmabuf, uint32_t handles[static 4]) {
uint64_t modifiers[4] = {0};
for (int i = 0; i < dmabuf->n_planes; i++) {
// KMS requires all BO planes to have the same modifier
modifiers[i] = dmabuf->modifier;
}
uint32_t id = 0;
if (drm->addfb2_modifiers && dmabuf->modifier != DRM_FORMAT_MOD_INVALID) {
if (drmModeAddFB2WithModifiers(drm->fd, dmabuf->width, dmabuf->height,
dmabuf->format, handles, dmabuf->stride, dmabuf->offset,
modifiers, &id, DRM_MODE_FB_MODIFIERS) != 0) {
wlr_log_errno(WLR_DEBUG, "drmModeAddFB2WithModifiers failed");
}
} else {
if (dmabuf->modifier != DRM_FORMAT_MOD_INVALID &&
dmabuf->modifier != DRM_FORMAT_MOD_LINEAR) {
wlr_log(WLR_ERROR, "Cannot import DRM framebuffer with explicit "
"modifier 0x%"PRIX64, dmabuf->modifier);
return 0;
}
int ret = drmModeAddFB2(drm->fd, dmabuf->width, dmabuf->height,
dmabuf->format, handles, dmabuf->stride, dmabuf->offset, &id, 0);
if (ret != 0 && dmabuf->format == DRM_FORMAT_ARGB8888 &&
dmabuf->n_planes == 1 && dmabuf->offset[0] == 0) {
// Some big-endian machines don't support drmModeAddFB2. Try a
// last-resort fallback for ARGB8888 buffers, like Xorg's
// modesetting driver does.
wlr_log(WLR_DEBUG, "drmModeAddFB2 failed (%s), falling back to "
"legacy drmModeAddFB", strerror(-ret));
uint32_t depth = 32;
uint32_t bpp = 32;
ret = drmModeAddFB(drm->fd, dmabuf->width, dmabuf->height, depth,
bpp, dmabuf->stride[0], handles[0], &id);
if (ret != 0) {
wlr_log_errno(WLR_DEBUG, "drmModeAddFB failed");
}
} else if (ret != 0) {
wlr_log_errno(WLR_DEBUG, "drmModeAddFB2 failed");
}
}
return id;
}
static void close_all_bo_handles(struct wlr_drm_backend *drm,
uint32_t handles[static 4]) {
for (int i = 0; i < 4; ++i) {
if (handles[i] == 0) {
continue;
}
// If multiple planes share the same BO handle, avoid double-closing it
bool already_closed = false;
for (int j = 0; j < i; ++j) {
if (handles[i] == handles[j]) {
already_closed = true;
break;
}
}
if (already_closed) {
continue;
}
if (drmCloseBufferHandle(drm->fd, handles[i]) != 0) {
wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed");
}
}
}
static void drm_poisoned_fb_handle_destroy(struct wlr_addon *addon) {
wlr_addon_finish(addon);
free(addon);
}
static const struct wlr_addon_interface poisoned_fb_addon_impl = {
.name = "wlr_drm_poisoned_fb",
.destroy = drm_poisoned_fb_handle_destroy,
};
static bool is_buffer_poisoned(struct wlr_drm_backend *drm,
struct wlr_buffer *buf) {
return wlr_addon_find(&buf->addons, drm, &poisoned_fb_addon_impl) != NULL;
}
/**
* Mark the buffer as "poisoned", ie. it cannot be imported into KMS. This
* allows us to avoid repeatedly trying to import it when it's not
* scanout-capable.
*/
static void poison_buffer(struct wlr_drm_backend *drm,
struct wlr_buffer *buf) {
struct wlr_addon *addon = calloc(1, sizeof(*addon));
if (addon == NULL) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return;
}
wlr_addon_init(addon, &buf->addons, drm, &poisoned_fb_addon_impl);
wlr_log(WLR_DEBUG, "Poisoning buffer");
}
static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm,
struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) {
struct wlr_dmabuf_attributes attribs;
if (!wlr_buffer_get_dmabuf(buf, &attribs)) {
wlr_log(WLR_DEBUG, "Failed to get DMA-BUF from buffer");
return NULL;
}
if (is_buffer_poisoned(drm, buf)) {
wlr_log(WLR_DEBUG, "Buffer is poisoned");
return NULL;
}
struct wlr_drm_fb *fb = calloc(1, sizeof(*fb));
if (!fb) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return NULL;
}
if (formats && !wlr_drm_format_set_has(formats, attribs.format,
attribs.modifier)) {
// The format isn't supported by the plane. Try stripping the alpha
// channel, if any.
const struct wlr_pixel_format_info *info =
drm_get_pixel_format_info(attribs.format);
if (info != NULL && info->opaque_substitute != DRM_FORMAT_INVALID &&
wlr_drm_format_set_has(formats, info->opaque_substitute, attribs.modifier)) {
attribs.format = info->opaque_substitute;
} else {
wlr_log(WLR_DEBUG, "Buffer format 0x%"PRIX32" with modifier "
"0x%"PRIX64" cannot be scanned out",
attribs.format, attribs.modifier);
goto error_fb;
}
}
uint32_t handles[4] = {0};
for (int i = 0; i < attribs.n_planes; ++i) {
int ret = drmPrimeFDToHandle(drm->fd, attribs.fd[i], &handles[i]);
if (ret != 0) {
wlr_log_errno(WLR_DEBUG, "drmPrimeFDToHandle failed");
goto error_bo_handle;
}
}
fb->id = get_fb_for_bo(drm, &attribs, handles);
if (!fb->id) {
wlr_log(WLR_DEBUG, "Failed to import BO in KMS");
poison_buffer(drm, buf);
goto error_bo_handle;
}
close_all_bo_handles(drm, handles);
fb->backend = drm;
fb->wlr_buf = buf;
wlr_addon_init(&fb->addon, &buf->addons, drm, &fb_addon_impl);
wl_list_insert(&drm->fbs, &fb->link);
return fb;
error_bo_handle:
close_all_bo_handles(drm, handles);
error_fb:
free(fb);
return NULL;
}
void drm_fb_destroy(struct wlr_drm_fb *fb) {
struct wlr_drm_backend *drm = fb->backend;
wl_list_remove(&fb->link);
wlr_addon_finish(&fb->addon);
if (drmModeRmFB(drm->fd, fb->id) != 0) {
wlr_log(WLR_ERROR, "drmModeRmFB failed");
}
free(fb);
}
bool drm_fb_import(struct wlr_drm_fb **fb_ptr, struct wlr_drm_backend *drm,
struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) {
struct wlr_drm_fb *fb;
struct wlr_addon *addon = wlr_addon_find(&buf->addons, drm, &fb_addon_impl);
if (addon != NULL) {
fb = wl_container_of(addon, fb, addon);
} else {
fb = drm_fb_create(drm, buf, formats);
if (!fb) {
return false;
}
}
wlr_buffer_lock(buf);
drm_fb_move(fb_ptr, &fb);
return true;
}
static void free_tex(struct gbm_bo *bo, void *data) {
struct wlr_texture *tex = data;
wlr_texture_destroy(tex);
}
static struct wlr_texture *get_tex_for_bo(struct wlr_drm_renderer *renderer,
struct gbm_bo *bo) {
struct wlr_texture *tex = gbm_bo_get_user_data(bo);
if (tex) {
return tex;
}
struct wlr_dmabuf_attributes attribs;
if (!export_drm_bo(bo, &attribs)) {
return NULL;
}
tex = wlr_texture_from_dmabuf(renderer->wlr_rend, &attribs);
if (tex) {
gbm_bo_set_user_data(bo, tex, free_tex);
}
return tex;
}
struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest,
struct gbm_bo *src) {
make_drm_surface_current(dest, NULL);
struct wlr_texture *tex = get_tex_for_bo(dest->renderer, src);
assert(tex);
float mat[9];
wlr_matrix_projection(mat, 1, 1, WL_OUTPUT_TRANSFORM_NORMAL);
struct wlr_renderer *renderer = dest->renderer->wlr_rend;
wlr_renderer_begin(renderer, dest->width, dest->height);
wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f);
wlr_renderer_end(renderer);
return swap_drm_surface_buffers(dest, NULL);
}
bool init_drm_plane_surfaces(struct wlr_drm_plane *plane,
struct wlr_drm_backend *drm, int32_t width, uint32_t height,
uint32_t format) {
if (!drm->parent) {
return init_drm_surface(&plane->surf, &drm->renderer, width, height,
format, GBM_BO_USE_SCANOUT);
}
if (!init_drm_surface(&plane->surf, &drm->parent->renderer,
width, height, format, GBM_BO_USE_LINEAR)) {
return false;
}
if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer,
width, height, format, GBM_BO_USE_SCANOUT)) {
finish_drm_surface(&plane->surf);
return false;
}
return true;
void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old) {
drm_fb_clear(new);
*new = *old;
*old = NULL;
}

View File

@ -2,7 +2,6 @@
#include <drm_fourcc.h>
#include <drm_mode.h>
#include <drm.h>
#include <gbm.h>
#include <stdio.h>
#include <string.h>
#include <wlr/util/log.h>
@ -39,6 +38,7 @@ static const char *get_manufacturer(uint16_t id) {
case ID('A', 'S', 'K'): return "Ask A/S";
case ID('A', 'V', 'T'): return "Avtek (Electronics) Pty Ltd";
case ID('B', 'N', 'O'): return "Bang & Olufsen";
case ID('B', 'N', 'Q'): return "BenQ Corporation";
case ID('C', 'M', 'N'): return "Chimei Innolux Corporation";
case ID('C', 'M', 'O'): return "Chi Mei Optoelectronics corp.";
case ID('C', 'R', 'O'): return "Extraordinary Technologies PTY Limited";
@ -94,6 +94,7 @@ static const char *get_manufacturer(uint16_t id) {
case ID('V', 'E', 'S'): return "Vestel Elektronik Sanayi ve Ticaret A. S.";
case ID('V', 'I', 'T'): return "Visitech AS";
case ID('V', 'I', 'Z'): return "VIZIO, Inc";
case ID('V', 'L', 'V'): return "Valve";
case ID('V', 'S', 'C'): return "ViewSonic Corporation";
case ID('Y', 'M', 'H'): return "Yamaha Corporation";
default: return "Unknown";
@ -162,52 +163,19 @@ const char *conn_get_name(uint32_t type_id) {
case DRM_MODE_CONNECTOR_eDP: return "eDP";
case DRM_MODE_CONNECTOR_VIRTUAL: return "Virtual";
case DRM_MODE_CONNECTOR_DSI: return "DSI";
#ifdef DRM_MODE_CONNECTOR_DPI
case DRM_MODE_CONNECTOR_DPI: return "DPI";
case DRM_MODE_CONNECTOR_WRITEBACK: return "Writeback";
#ifdef DRM_MODE_CONNECTOR_SPI
case DRM_MODE_CONNECTOR_SPI: return "SPI";
#endif
#ifdef DRM_MODE_CONNECTOR_USB
case DRM_MODE_CONNECTOR_USB: return "USB";
#endif
default: return "Unknown";
}
}
static void free_fb(struct gbm_bo *bo, void *data) {
uint32_t id = (uintptr_t)data;
if (id) {
struct gbm_device *gbm = gbm_bo_get_device(bo);
drmModeRmFB(gbm_device_get_fd(gbm), id);
}
}
uint32_t get_fb_for_bo(struct gbm_bo *bo, uint32_t drm_format) {
uint32_t id = (uintptr_t)gbm_bo_get_user_data(bo);
if (id) {
return id;
}
assert(gbm_bo_get_format(bo) == GBM_FORMAT_ARGB8888);
assert(drm_format == DRM_FORMAT_ARGB8888 ||
drm_format == DRM_FORMAT_XRGB8888);
struct gbm_device *gbm = gbm_bo_get_device(bo);
int fd = gbm_device_get_fd(gbm);
uint32_t width = gbm_bo_get_width(bo);
uint32_t height = gbm_bo_get_height(bo);
uint32_t handles[4] = {gbm_bo_get_handle(bo).u32};
uint32_t pitches[4] = {gbm_bo_get_stride(bo)};
uint32_t offsets[4] = {gbm_bo_get_offset(bo, 0)};
if (drmModeAddFB2(fd, width, height, drm_format,
handles, pitches, offsets, &id, 0)) {
wlr_log_errno(WLR_ERROR, "Unable to add DRM framebuffer");
}
gbm_bo_set_user_data(bo, (void *)(uintptr_t)id, free_fb);
return id;
}
static inline bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) {
static bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) {
for (size_t i = 0; i < n; ++i) {
if (arr[i] == key) {
return true;

View File

@ -2,11 +2,8 @@
#include <stdlib.h>
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/egl.h>
#include <wlr/render/gles2.h>
#include <wlr/util/log.h>
#include "backend/headless.h"
#include "glapi.h"
#include "util/signal.h"
struct wlr_headless_backend *headless_backend_from_backend(
@ -29,8 +26,7 @@ static bool backend_start(struct wlr_backend *wlr_backend) {
}
struct wlr_headless_input_device *input_device;
wl_list_for_each(input_device, &backend->input_devices,
wlr_input_device.link) {
wl_list_for_each(input_device, &backend->input_devices, link) {
wlr_signal_emit_safe(&backend->backend.events.new_input,
&input_device->wlr_input_device);
}
@ -55,28 +51,25 @@ static void backend_destroy(struct wlr_backend *wlr_backend) {
struct wlr_headless_input_device *input_device, *input_device_tmp;
wl_list_for_each_safe(input_device, input_device_tmp,
&backend->input_devices, wlr_input_device.link) {
&backend->input_devices, link) {
wlr_input_device_destroy(&input_device->wlr_input_device);
}
wlr_signal_emit_safe(&wlr_backend->events.destroy, backend);
wlr_backend_finish(wlr_backend);
wlr_renderer_destroy(backend->renderer);
wlr_egl_finish(&backend->egl);
free(backend);
}
static struct wlr_renderer *backend_get_renderer(
struct wlr_backend *wlr_backend) {
struct wlr_headless_backend *backend =
headless_backend_from_backend(wlr_backend);
return backend->renderer;
static uint32_t get_buffer_caps(struct wlr_backend *wlr_backend) {
return WLR_BUFFER_CAP_DATA_PTR
| WLR_BUFFER_CAP_DMABUF
| WLR_BUFFER_CAP_SHM;
}
static const struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_renderer = backend_get_renderer,
.get_buffer_caps = get_buffer_caps,
};
static void handle_display_destroy(struct wl_listener *listener, void *data) {
@ -85,8 +78,7 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
backend_destroy(&backend->backend);
}
struct wlr_backend *wlr_headless_backend_create(struct wl_display *display,
wlr_renderer_create_func_t create_renderer_func) {
struct wlr_backend *wlr_headless_backend_create(struct wl_display *display) {
wlr_log(WLR_INFO, "Creating headless backend");
struct wlr_headless_backend *backend =
@ -95,32 +87,13 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_display *display,
wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend");
return NULL;
}
wlr_backend_init(&backend->backend, &backend_impl);
backend->display = display;
wl_list_init(&backend->outputs);
wl_list_init(&backend->input_devices);
static const EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_ALPHA_SIZE, 0,
EGL_BLUE_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_RED_SIZE, 1,
EGL_NONE,
};
if (!create_renderer_func) {
create_renderer_func = wlr_renderer_autocreate;
}
backend->renderer = create_renderer_func(&backend->egl,
EGL_PLATFORM_SURFACELESS_MESA, NULL, (EGLint*)config_attribs, 0);
if (!backend->renderer) {
wlr_log(WLR_ERROR, "Failed to create renderer");
free(backend);
return NULL;
}
backend->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &backend->display_destroy);

View File

@ -11,7 +11,16 @@
#include "backend/headless.h"
#include "util/signal.h"
static const struct wlr_input_device_impl input_device_impl = { 0 };
static void input_device_destroy(struct wlr_input_device *wlr_dev) {
struct wlr_headless_input_device *dev =
wl_container_of(wlr_dev, dev, wlr_input_device);
wl_list_remove(&dev->link);
free(dev);
}
static const struct wlr_input_device_impl input_device_impl = {
.destroy = input_device_destroy,
};
bool wlr_input_device_is_headless(struct wlr_input_device *wlr_dev) {
return wlr_dev->impl == &input_device_impl;
@ -78,15 +87,15 @@ struct wlr_input_device *wlr_headless_add_input_device(
wlr_tablet_pad_init(wlr_device->tablet_pad, NULL);
break;
case WLR_INPUT_DEVICE_SWITCH:
wlr_device->lid_switch = calloc(1, sizeof(struct wlr_switch));
if (wlr_device->lid_switch == NULL) {
wlr_device->switch_device = calloc(1, sizeof(struct wlr_switch));
if (wlr_device->switch_device == NULL) {
wlr_log(WLR_ERROR, "Unable to allocate wlr_switch");
goto error;
}
wlr_switch_init(wlr_device->lid_switch, NULL);
wlr_switch_init(wlr_device->switch_device, NULL);
}
wl_list_insert(&backend->input_devices, &wlr_device->link);
wl_list_insert(&backend->input_devices, &device->link);
if (backend->started) {
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_device);

View File

@ -0,0 +1,5 @@
wlr_files += files(
'backend.c',
'input_device.c',
'output.c',
)

View File

@ -1,95 +1,89 @@
#include <assert.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <stdlib.h>
#include <stdio.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/util/log.h>
#include "backend/headless.h"
#include "util/signal.h"
static const uint32_t SUPPORTED_OUTPUT_STATE =
WLR_OUTPUT_STATE_BACKEND_OPTIONAL |
WLR_OUTPUT_STATE_BUFFER |
WLR_OUTPUT_STATE_MODE;
static struct wlr_headless_output *headless_output_from_output(
struct wlr_output *wlr_output) {
assert(wlr_output_is_headless(wlr_output));
return (struct wlr_headless_output *)wlr_output;
}
static EGLSurface egl_create_surface(struct wlr_egl *egl, unsigned int width,
unsigned int height) {
EGLint attribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE};
EGLSurface surf = eglCreatePbufferSurface(egl->display, egl->config, attribs);
if (surf == EGL_NO_SURFACE) {
wlr_log(WLR_ERROR, "Failed to create EGL surface");
return EGL_NO_SURFACE;
}
return surf;
}
static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width,
int32_t height, int32_t refresh) {
struct wlr_headless_output *output =
headless_output_from_output(wlr_output);
struct wlr_headless_backend *backend = output->backend;
static bool output_set_custom_mode(struct wlr_headless_output *output,
int32_t width, int32_t height, int32_t refresh) {
if (refresh <= 0) {
refresh = HEADLESS_DEFAULT_REFRESH;
}
wlr_egl_destroy_surface(&backend->egl, output->egl_surface);
output->egl_surface = egl_create_surface(&backend->egl, width, height);
if (output->egl_surface == EGL_NO_SURFACE) {
wlr_log(WLR_ERROR, "Failed to recreate EGL surface");
wlr_output_destroy(wlr_output);
return false;
}
output->frame_delay = 1000000 / refresh;
wlr_output_update_custom_mode(&output->wlr_output, width, height, refresh);
return true;
}
static void output_transform(struct wlr_output *wlr_output,
enum wl_output_transform transform) {
struct wlr_headless_output *output =
headless_output_from_output(wlr_output);
output->wlr_output.transform = transform;
static bool output_test(struct wlr_output *wlr_output) {
uint32_t unsupported =
wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE;
if (unsupported != 0) {
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
unsupported);
return false;
}
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM);
}
return true;
}
static bool output_make_current(struct wlr_output *wlr_output, int *buffer_age) {
static bool output_commit(struct wlr_output *wlr_output) {
struct wlr_headless_output *output =
headless_output_from_output(wlr_output);
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
buffer_age);
}
static bool output_swap_buffers(struct wlr_output *wlr_output,
pixman_region32_t *damage) {
// Nothing needs to be done for pbuffers
wlr_output_send_present(wlr_output, NULL);
if (!output_test(wlr_output)) {
return false;
}
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
if (!output_set_custom_mode(output,
wlr_output->pending.custom_mode.width,
wlr_output->pending.custom_mode.height,
wlr_output->pending.custom_mode.refresh)) {
return false;
}
}
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
struct wlr_output_event_present present_event = {
.commit_seq = wlr_output->commit_seq + 1,
.presented = true,
};
wlr_output_send_present(wlr_output, &present_event);
}
return true;
}
static void output_destroy(struct wlr_output *wlr_output) {
struct wlr_headless_output *output =
headless_output_from_output(wlr_output);
wl_list_remove(&output->link);
wl_event_source_remove(output->frame_timer);
wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface);
free(output);
}
static const struct wlr_output_impl output_impl = {
.set_custom_mode = output_set_custom_mode,
.transform = output_transform,
.destroy = output_destroy,
.make_current = output_make_current,
.swap_buffers = output_swap_buffers,
.commit = output_commit,
};
bool wlr_output_is_headless(struct wlr_output *wlr_output) {
@ -119,26 +113,18 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
backend->display);
struct wlr_output *wlr_output = &output->wlr_output;
output->egl_surface = egl_create_surface(&backend->egl, width, height);
if (output->egl_surface == EGL_NO_SURFACE) {
wlr_log(WLR_ERROR, "Failed to create EGL surface");
goto error;
}
output_set_custom_mode(wlr_output, width, height, 0);
output_set_custom_mode(output, width, height, 0);
strncpy(wlr_output->make, "headless", sizeof(wlr_output->make));
strncpy(wlr_output->model, "headless", sizeof(wlr_output->model));
snprintf(wlr_output->name, sizeof(wlr_output->name), "HEADLESS-%d",
wl_list_length(&backend->outputs) + 1);
if (!wlr_egl_make_current(&output->backend->egl, output->egl_surface,
NULL)) {
goto error;
}
char name[64];
snprintf(name, sizeof(name), "HEADLESS-%zu", ++backend->last_output_num);
wlr_output_set_name(wlr_output, name);
wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 });
wlr_renderer_end(backend->renderer);
char description[128];
snprintf(description, sizeof(description),
"Headless output %zu", backend->last_output_num);
wlr_output_set_description(wlr_output, description);
struct wl_event_loop *ev = wl_display_get_event_loop(backend->display);
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);
@ -152,8 +138,4 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
}
return wlr_output;
error:
wlr_output_destroy(&output->wlr_output);
return NULL;
}

View File

@ -1,6 +1,7 @@
#include <assert.h>
#include <libinput.h>
#include <stdlib.h>
#include <stdio.h>
#include <wlr/backend/interface.h>
#include <wlr/backend/session.h>
#include <wlr/util/log.h>
@ -16,12 +17,27 @@ static struct wlr_libinput_backend *get_libinput_backend_from_backend(
static int libinput_open_restricted(const char *path,
int flags, void *_backend) {
struct wlr_libinput_backend *backend = _backend;
return wlr_session_open_file(backend->session, path);
struct wlr_device *dev = wlr_session_open_file(backend->session, path);
if (dev == NULL) {
return -1;
}
return dev->fd;
}
static void libinput_close_restricted(int fd, void *_backend) {
struct wlr_libinput_backend *backend = _backend;
wlr_session_close_file(backend->session, fd);
struct wlr_device *dev;
bool found = false;
wl_list_for_each(dev, &backend->session->devices, link) {
if (dev->fd == fd) {
found = true;
break;
}
}
if (found) {
wlr_session_close_file(backend->session, dev);
}
}
static const struct libinput_interface libinput_impl = {
@ -31,9 +47,10 @@ static const struct libinput_interface libinput_impl = {
static int handle_libinput_readable(int fd, uint32_t mask, void *_backend) {
struct wlr_libinput_backend *backend = _backend;
if (libinput_dispatch(backend->libinput_context) != 0) {
wlr_log(WLR_ERROR, "Failed to dispatch libinput");
// TODO: some kind of abort?
int ret = libinput_dispatch(backend->libinput_context);
if (ret != 0) {
wlr_log(WLR_ERROR, "Failed to dispatch libinput: %s", strerror(-ret));
wl_display_terminate(backend->display);
return 0;
}
struct libinput_event *event;
@ -44,15 +61,30 @@ static int handle_libinput_readable(int fd, uint32_t mask, void *_backend) {
return 0;
}
static enum wlr_log_importance libinput_log_priority_to_wlr(
enum libinput_log_priority priority) {
switch (priority) {
case LIBINPUT_LOG_PRIORITY_ERROR:
return WLR_ERROR;
case LIBINPUT_LOG_PRIORITY_INFO:
return WLR_INFO;
default:
return WLR_DEBUG;
}
}
static void log_libinput(struct libinput *libinput_context,
enum libinput_log_priority priority, const char *fmt, va_list args) {
_wlr_vlog(WLR_ERROR, fmt, args);
enum wlr_log_importance importance = libinput_log_priority_to_wlr(priority);
static char wlr_fmt[1024];
snprintf(wlr_fmt, sizeof(wlr_fmt), "[libinput] %s", fmt);
_wlr_vlog(importance, wlr_fmt, args);
}
static bool backend_start(struct wlr_backend *wlr_backend) {
struct wlr_libinput_backend *backend =
get_libinput_backend_from_backend(wlr_backend);
wlr_log(WLR_DEBUG, "Initializing libinput");
wlr_log(WLR_DEBUG, "Starting libinput backend");
backend->libinput_context = libinput_udev_create_context(&libinput_impl,
backend, backend->session->udev);
@ -78,9 +110,9 @@ static bool backend_start(struct wlr_backend *wlr_backend) {
no_devs = NULL;
}
}
if (!no_devs && backend->wlr_device_lists.length == 0) {
if (!no_devs && backend->wlr_device_lists.size == 0) {
handle_libinput_readable(libinput_fd, WL_EVENT_READABLE, backend);
if (backend->wlr_device_lists.length == 0) {
if (backend->wlr_device_lists.size == 0) {
wlr_log(WLR_ERROR, "libinput initialization failed, no input devices");
wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check");
return false;
@ -109,21 +141,22 @@ static void backend_destroy(struct wlr_backend *wlr_backend) {
struct wlr_libinput_backend *backend =
get_libinput_backend_from_backend(wlr_backend);
for (size_t i = 0; i < backend->wlr_device_lists.length; i++) {
struct wl_list *wlr_devices = backend->wlr_device_lists.items[i];
struct wlr_input_device *wlr_dev, *next;
wl_list_for_each_safe(wlr_dev, next, wlr_devices, link) {
wlr_input_device_destroy(wlr_dev);
struct wl_list **wlr_devices_ptr;
wl_array_for_each(wlr_devices_ptr, &backend->wlr_device_lists) {
struct wlr_libinput_input_device *dev, *tmp;
wl_list_for_each_safe(dev, tmp, *wlr_devices_ptr, link) {
wlr_input_device_destroy(&dev->wlr_input_device);
}
free(wlr_devices);
free(*wlr_devices_ptr);
}
wlr_signal_emit_safe(&wlr_backend->events.destroy, wlr_backend);
wlr_backend_finish(wlr_backend);
wl_list_remove(&backend->display_destroy.link);
wl_list_remove(&backend->session_destroy.link);
wl_list_remove(&backend->session_signal.link);
wlr_list_finish(&backend->wlr_device_lists);
wl_array_release(&backend->wlr_device_lists);
if (backend->input_event) {
wl_event_source_remove(backend->input_event);
}
@ -143,7 +176,7 @@ bool wlr_backend_is_libinput(struct wlr_backend *b) {
static void session_signal(struct wl_listener *listener, void *data) {
struct wlr_libinput_backend *backend =
wl_container_of(listener, backend, session_signal);
struct wlr_session *session = data;
struct wlr_session *session = backend->session;
if (!backend->libinput_context) {
return;
@ -156,6 +189,12 @@ static void session_signal(struct wl_listener *listener, void *data) {
}
}
static void handle_session_destroy(struct wl_listener *listener, void *data) {
struct wlr_libinput_backend *backend =
wl_container_of(listener, backend, session_destroy);
backend_destroy(&backend->backend);
}
static void handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_libinput_backend *backend =
wl_container_of(listener, backend, display_destroy);
@ -172,24 +211,21 @@ struct wlr_backend *wlr_libinput_backend_create(struct wl_display *display,
}
wlr_backend_init(&backend->backend, &backend_impl);
if (!wlr_list_init(&backend->wlr_device_lists)) {
wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno));
goto error_backend;
}
wl_array_init(&backend->wlr_device_lists);
backend->session = session;
backend->display = display;
backend->session_signal.notify = session_signal;
wl_signal_add(&session->session_signal, &backend->session_signal);
wl_signal_add(&session->events.active, &backend->session_signal);
backend->session_destroy.notify = handle_session_destroy;
wl_signal_add(&session->events.destroy, &backend->session_destroy);
backend->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &backend->display_destroy);
return &backend->backend;
error_backend:
free(backend);
return NULL;
}
struct libinput_device *wlr_libinput_get_device_handle(

View File

@ -7,9 +7,10 @@
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/util/log.h>
#include "backend/libinput.h"
#include "util/array.h"
#include "util/signal.h"
struct wlr_libinput_input_device *get_libinput_device_from_device(
static struct wlr_libinput_input_device *get_libinput_device_from_device(
struct wlr_input_device *wlr_dev) {
assert(wlr_input_device_is_libinput(wlr_dev));
return (struct wlr_libinput_input_device *)wlr_dev;
@ -22,10 +23,10 @@ struct wlr_input_device *get_appropriate_device(
if (!wlr_devices) {
return NULL;
}
struct wlr_input_device *dev;
struct wlr_libinput_input_device *dev;
wl_list_for_each(dev, wlr_devices, link) {
if (dev->type == desired_type) {
return dev;
if (dev->wlr_input_device.type == desired_type) {
return &dev->wlr_input_device;
}
}
return NULL;
@ -35,7 +36,7 @@ static void input_device_destroy(struct wlr_input_device *wlr_dev) {
struct wlr_libinput_input_device *dev =
get_libinput_device_from_device(wlr_dev);
libinput_device_unref(dev->handle);
wl_list_remove(&dev->wlr_input_device.link);
wl_list_remove(&dev->link);
free(dev);
}
@ -62,7 +63,7 @@ static struct wlr_input_device *allocate_device(
if (output_name != NULL) {
wlr_dev->output_name = strdup(output_name);
}
wl_list_insert(wlr_devices, &wlr_dev->link);
wl_list_insert(wlr_devices, &dev->link);
dev->handle = libinput_dev;
libinput_device_ref(libinput_dev);
wlr_input_device_init(wlr_dev, type, &input_device_impl,
@ -174,8 +175,8 @@ static void handle_device_added(struct wlr_libinput_backend *backend,
if (!wlr_dev) {
goto fail;
}
wlr_dev->lid_switch = create_libinput_switch(libinput_dev);
if (!wlr_dev->lid_switch) {
wlr_dev->switch_device = create_libinput_switch(libinput_dev);
if (!wlr_dev->switch_device) {
free(wlr_dev);
goto fail;
}
@ -183,8 +184,13 @@ static void handle_device_added(struct wlr_libinput_backend *backend,
}
if (!wl_list_empty(wlr_devices)) {
struct wl_list **dst = wl_array_add(&backend->wlr_device_lists, sizeof(wlr_devices));
if (!dst) {
goto fail;
}
*dst = wlr_devices;
libinput_device_set_user_data(libinput_dev, wlr_devices);
wlr_list_push(&backend->wlr_device_lists, wlr_devices);
} else {
free(wlr_devices);
}
@ -192,7 +198,7 @@ static void handle_device_added(struct wlr_libinput_backend *backend,
fail:
wlr_log(WLR_ERROR, "Could not allocate new device");
struct wlr_input_device *dev, *tmp_dev;
struct wlr_libinput_input_device *dev, *tmp_dev;
wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) {
free(dev);
}
@ -209,15 +215,20 @@ static void handle_device_removed(struct wlr_libinput_backend *backend,
if (!wlr_devices) {
return;
}
struct wlr_input_device *dev, *tmp_dev;
struct wlr_libinput_input_device *dev, *tmp_dev;
wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) {
wlr_input_device_destroy(dev);
wlr_input_device_destroy(&dev->wlr_input_device);
}
for (size_t i = 0; i < backend->wlr_device_lists.length; i++) {
if (backend->wlr_device_lists.items[i] == wlr_devices) {
wlr_list_del(&backend->wlr_device_lists, i);
size_t i = 0;
struct wl_list **ptr;
wl_array_for_each(ptr, &backend->wlr_device_lists) {
struct wl_list *iter = *ptr;
if (iter == wlr_devices) {
array_remove_at(&backend->wlr_device_lists,
i * sizeof(struct wl_list *), sizeof(struct wl_list *));
break;
}
i++;
}
free(wlr_devices);
}
@ -261,7 +272,7 @@ void handle_libinput_event(struct wlr_libinput_backend *backend,
handle_touch_cancel(event, libinput_dev);
break;
case LIBINPUT_EVENT_TOUCH_FRAME:
// no-op (at least for now)
handle_touch_frame(event, libinput_dev);
break;
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
handle_tablet_tool_axis(event, libinput_dev);
@ -305,6 +316,14 @@ void handle_libinput_event(struct wlr_libinput_backend *backend,
case LIBINPUT_EVENT_GESTURE_PINCH_END:
handle_pointer_pinch_end(event, libinput_dev);
break;
#if LIBINPUT_HAS_HOLD_GESTURES
case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN:
handle_pointer_hold_begin(event, libinput_dev);
break;
case LIBINPUT_EVENT_GESTURE_HOLD_END:
handle_pointer_hold_end(event, libinput_dev);
break;
#endif
default:
wlr_log(WLR_DEBUG, "Unknown libinput event %d", event_type);
break;

View File

@ -72,10 +72,10 @@ void handle_keyboard_key(struct libinput_event *event,
libinput_event_keyboard_get_key_state(kbevent);
switch (state) {
case LIBINPUT_KEY_STATE_RELEASED:
wlr_event.state = WLR_KEY_RELEASED;
wlr_event.state = WL_KEYBOARD_KEY_STATE_RELEASED;
break;
case LIBINPUT_KEY_STATE_PRESSED:
wlr_event.state = WLR_KEY_PRESSED;
wlr_event.state = WL_KEYBOARD_KEY_STATE_PRESSED;
break;
}
wlr_event.update_state = true;

View File

@ -0,0 +1,35 @@
msg = ['Required for libinput backend support.']
if 'libinput' in backends
msg += 'Install "libinput" or disable the libinput backend.'
endif
libinput = dependency(
'libinput',
version: '>=1.14.0',
required: 'libinput' in backends,
not_found_message: '\n'.join(msg),
)
if not libinput.found()
subdir_done()
endif
wlr_files += files(
'backend.c',
'events.c',
'keyboard.c',
'pointer.c',
'switch.c',
'tablet_pad.c',
'tablet_tool.c',
'touch.c',
)
features += { 'libinput-backend': true }
wlr_deps += libinput
# libinput hold gestures are available since 1.19.0
add_project_arguments(
'-DLIBINPUT_HAS_HOLD_GESTURES=@0@'.format(libinput.version().version_compare('>=1.19.0').to_int()),
language: 'c',
)

View File

@ -260,3 +260,41 @@ void handle_pointer_pinch_end(struct libinput_event *event,
};
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_end, &wlr_event);
}
void handle_pointer_hold_begin(struct libinput_event *event,
struct libinput_device *libinput_dev) {
struct wlr_input_device *wlr_dev =
get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev);
if (!wlr_dev) {
wlr_log(WLR_DEBUG, "Got a pointer gesture event for a device with no pointers?");
return;
}
struct libinput_event_gesture *gevent =
libinput_event_get_gesture_event(event);
struct wlr_event_pointer_hold_begin wlr_event = {
.device = wlr_dev,
.time_msec =
usec_to_msec(libinput_event_gesture_get_time_usec(gevent)),
.fingers = libinput_event_gesture_get_finger_count(gevent),
};
wlr_signal_emit_safe(&wlr_dev->pointer->events.hold_begin, &wlr_event);
}
void handle_pointer_hold_end(struct libinput_event *event,
struct libinput_device *libinput_dev) {
struct wlr_input_device *wlr_dev =
get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev);
if (!wlr_dev) {
wlr_log(WLR_DEBUG, "Got a pointer gesture event for a device with no pointers?");
return;
}
struct libinput_event_gesture *gevent =
libinput_event_get_gesture_event(event);
struct wlr_event_pointer_hold_end wlr_event = {
.device = wlr_dev,
.time_msec =
usec_to_msec(libinput_event_gesture_get_time_usec(gevent)),
.cancelled = libinput_event_gesture_get_cancelled(gevent),
};
wlr_signal_emit_safe(&wlr_dev->pointer->events.hold_end, &wlr_event);
}

View File

@ -29,7 +29,7 @@ void handle_switch_toggle(struct libinput_event *event,
wlr_log(WLR_DEBUG, "Got a switch event for a device with no switch?");
return;
}
struct libinput_event_switch *sevent =
struct libinput_event_switch *sevent =
libinput_event_get_switch_event (event);
struct wlr_event_switch_toggle wlr_event = { 0 };
wlr_event.device = wlr_dev;
@ -51,5 +51,5 @@ void handle_switch_toggle(struct libinput_event *event,
}
wlr_event.time_msec =
usec_to_msec(libinput_event_switch_get_time_usec(sevent));
wlr_signal_emit_safe(&wlr_dev->lid_switch->events.toggle, &wlr_event);
wlr_signal_emit_safe(&wlr_dev->switch_device->events.toggle, &wlr_event);
}

View File

@ -75,6 +75,7 @@ struct wlr_tablet_pad *create_libinput_tablet_pad(
wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_pad");
return NULL;
}
wlr_tablet_pad_init(wlr_tablet_pad, NULL);
wlr_tablet_pad->button_count =
libinput_device_tablet_pad_get_num_buttons(libinput_dev);
@ -83,17 +84,15 @@ struct wlr_tablet_pad *create_libinput_tablet_pad(
wlr_tablet_pad->strip_count =
libinput_device_tablet_pad_get_num_strips(libinput_dev);
wlr_list_init(&wlr_tablet_pad->paths);
struct udev_device *udev = libinput_device_get_udev_device(libinput_dev);
wlr_list_push(&wlr_tablet_pad->paths, strdup(udev_device_get_syspath(udev)));
char **dst = wl_array_add(&wlr_tablet_pad->paths, sizeof(char *));
*dst = strdup(udev_device_get_syspath(udev));
wl_list_init(&wlr_tablet_pad->groups);
int groups = libinput_device_tablet_pad_get_num_mode_groups(libinput_dev);
for (int i = 0; i < groups; ++i) {
add_pad_group_from_libinput(wlr_tablet_pad, libinput_dev, i);
}
wlr_tablet_pad_init(wlr_tablet_pad, NULL);
return wlr_tablet_pad;
}

View File

@ -11,9 +11,10 @@
#include <wlr/types/wlr_input_device.h>
#include <wlr/util/log.h>
#include "backend/libinput.h"
#include "util/array.h"
#include "util/signal.h"
static struct wlr_tablet_impl tablet_impl;
static const struct wlr_tablet_impl tablet_impl;
static bool tablet_is_libinput(struct wlr_tablet *tablet) {
return tablet->impl == &tablet_impl;
@ -29,18 +30,9 @@ struct wlr_libinput_tablet_tool {
size_t pad_refs;
};
// TODO: Maybe this should be a wlr_list? Do we keep it, or want to get rid of
// it?
struct tablet_tool_list_elem {
struct wl_list link;
struct wlr_libinput_tablet_tool *tool;
};
struct wlr_libinput_tablet {
struct wlr_tablet wlr_tablet;
struct wl_list tools; // tablet_tool_list_elem::link
struct wl_array tools; // struct wlr_libinput_tablet_tool *
};
static void destroy_tool(struct wlr_libinput_tablet_tool *tool) {
@ -56,22 +48,19 @@ static void destroy_tablet(struct wlr_tablet *wlr_tablet) {
struct wlr_libinput_tablet *tablet =
wl_container_of(wlr_tablet, tablet, wlr_tablet);
struct tablet_tool_list_elem *pos;
struct tablet_tool_list_elem *tmp;
wl_list_for_each_safe(pos, tmp, &tablet->tools, link) {
struct wlr_libinput_tablet_tool *tool = pos->tool;
wl_list_remove(&pos->link);
free(pos);
struct wlr_libinput_tablet_tool **tool_ptr;
wl_array_for_each(tool_ptr, &tablet->tools) {
struct wlr_libinput_tablet_tool *tool = *tool_ptr;
if (--tool->pad_refs == 0) {
destroy_tool(tool);
}
}
wl_array_release(&tablet->tools);
free(tablet);
}
static struct wlr_tablet_impl tablet_impl = {
static const struct wlr_tablet_impl tablet_impl = {
.destroy = destroy_tablet,
};
@ -84,15 +73,18 @@ struct wlr_tablet *create_libinput_tablet(
wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_tool");
return NULL;
}
struct wlr_tablet *wlr_tablet = &libinput_tablet->wlr_tablet;
wlr_list_init(&wlr_tablet->paths);
struct udev_device *udev = libinput_device_get_udev_device(libinput_dev);
wlr_list_push(&wlr_tablet->paths, strdup(udev_device_get_syspath(udev)));
wlr_tablet->name = strdup(libinput_device_get_name(libinput_dev));
wl_list_init(&libinput_tablet->tools);
wlr_tablet_init(wlr_tablet, &tablet_impl);
struct udev_device *udev = libinput_device_get_udev_device(libinput_dev);
char **dst = wl_array_add(&wlr_tablet->paths, sizeof(char *));
*dst = strdup(udev_device_get_syspath(udev));
wlr_tablet->name = strdup(libinput_device_get_name(libinput_dev));
wl_array_init(&libinput_tablet->tools);
return wlr_tablet;
}
@ -113,9 +105,10 @@ static enum wlr_tablet_tool_type wlr_type_from_libinput_type(
return WLR_TABLET_TOOL_TYPE_MOUSE;
case LIBINPUT_TABLET_TOOL_TYPE_LENS:
return WLR_TABLET_TOOL_TYPE_LENS;
case LIBINPUT_TABLET_TOOL_TYPE_TOTEM:
return WLR_TABLET_TOOL_TYPE_TOTEM;
}
assert(false && "UNREACHABLE");
abort(); // unreachable
}
static struct wlr_libinput_tablet_tool *get_wlr_tablet_tool(
@ -159,9 +152,9 @@ static void ensure_tool_reference(struct wlr_libinput_tablet_tool *tool,
struct wlr_libinput_tablet *tablet =
wl_container_of(wlr_dev, tablet, wlr_tablet);
struct tablet_tool_list_elem *pos;
wl_list_for_each(pos, &tablet->tools, link) {
if (pos->tool == tool) { // We already have a ref
struct wlr_libinput_tablet_tool **tool_ptr;
wl_array_for_each(tool_ptr, &tablet->tools) {
if (*tool_ptr == tool) { // We already have a ref
// XXX: We *could* optimize the tool to the front of
// the list here, since we will probably get the next
// couple of events from the same tool.
@ -172,15 +165,13 @@ static void ensure_tool_reference(struct wlr_libinput_tablet_tool *tool,
}
}
struct tablet_tool_list_elem *new =
calloc(1, sizeof(struct tablet_tool_list_elem));
if (!new) {
struct wlr_libinput_tablet_tool **dst =
wl_array_add(&tablet->tools, sizeof(tool));
if (!dst) {
wlr_log(WLR_ERROR, "Failed to allocate memory for tracking tablet tool");
return;
}
new->tool = tool;
wl_list_insert(&tablet->tools, &new->link);
*dst = tool;
++tool->pad_refs;
}
@ -265,6 +256,9 @@ void handle_tablet_tool_proximity(struct libinput_event *event,
wlr_event.device = wlr_dev;
wlr_event.time_msec =
usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
wlr_event.x = libinput_event_tablet_tool_get_x_transformed(tevent, 1);
wlr_event.y = libinput_event_tablet_tool_get_y_transformed(tevent, 1);
switch (libinput_event_tablet_tool_get_proximity_state(tevent)) {
case LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT:
wlr_event.state = WLR_TABLET_TOOL_PROXIMITY_OUT;
@ -290,15 +284,15 @@ void handle_tablet_tool_proximity(struct libinput_event *event,
assert(tablet_is_libinput(wlr_dev->tablet));
struct wlr_libinput_tablet *tablet =
wl_container_of(wlr_dev->tablet, tablet, wlr_tablet);
struct tablet_tool_list_elem *pos;
struct tablet_tool_list_elem *tmp;
wl_list_for_each_safe(pos, tmp, &tablet->tools, link) {
if (pos->tool == tool) {
wl_list_remove(&pos->link);
free(pos);
size_t i = 0;
struct wlr_libinput_tablet_tool **tool_ptr;
wl_array_for_each(tool_ptr, &tablet->tools) {
if (*tool_ptr == tool) {
array_remove_at(&tablet->tools, i * sizeof(tool), sizeof(tool));
break;
}
i++;
}
destroy_tool(tool);
@ -326,6 +320,9 @@ void handle_tablet_tool_tip(struct libinput_event *event,
wlr_event.tool = &tool->wlr_tool;
wlr_event.time_msec =
usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
wlr_event.x = libinput_event_tablet_tool_get_x_transformed(tevent, 1);
wlr_event.y = libinput_event_tablet_tool_get_y_transformed(tevent, 1);
switch (libinput_event_tablet_tool_get_tip_state(tevent)) {
case LIBINPUT_TABLET_TOOL_TIP_UP:
wlr_event.state = WLR_TABLET_TOOL_TIP_UP;

View File

@ -34,7 +34,7 @@ void handle_touch_down(struct libinput_event *event,
wlr_event.device = wlr_dev;
wlr_event.time_msec =
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent);
wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1);
wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1);
wlr_signal_emit_safe(&wlr_dev->touch->events.down, &wlr_event);
@ -54,7 +54,7 @@ void handle_touch_up(struct libinput_event *event,
wlr_event.device = wlr_dev;
wlr_event.time_msec =
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent);
wlr_signal_emit_safe(&wlr_dev->touch->events.up, &wlr_event);
}
@ -72,7 +72,7 @@ void handle_touch_motion(struct libinput_event *event,
wlr_event.device = wlr_dev;
wlr_event.time_msec =
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent);
wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1);
wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1);
wlr_signal_emit_safe(&wlr_dev->touch->events.motion, &wlr_event);
@ -92,6 +92,17 @@ void handle_touch_cancel(struct libinput_event *event,
wlr_event.device = wlr_dev;
wlr_event.time_msec =
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent);
wlr_signal_emit_safe(&wlr_dev->touch->events.cancel, &wlr_event);
}
void handle_touch_frame(struct libinput_event *event,
struct libinput_device *libinput_dev) {
struct wlr_input_device *wlr_dev =
get_appropriate_device(WLR_INPUT_DEVICE_TOUCH, libinput_dev);
if (!wlr_dev) {
wlr_log(WLR_DEBUG, "Got a touch event for a device with no touch?");
return;
}
wlr_signal_emit_safe(&wlr_dev->touch->events.frame, NULL);
}

View File

@ -1,63 +1,21 @@
backend_parts = []
backend_files = files(
'backend.c',
'drm/atomic.c',
'drm/backend.c',
'drm/drm.c',
'drm/legacy.c',
'drm/properties.c',
'drm/renderer.c',
'drm/util.c',
'headless/backend.c',
'headless/input_device.c',
'headless/output.c',
'libinput/backend.c',
'libinput/events.c',
'libinput/keyboard.c',
'libinput/pointer.c',
'libinput/switch.c',
'libinput/tablet_pad.c',
'libinput/tablet_tool.c',
'libinput/touch.c',
'multi/backend.c',
'noop/backend.c',
'noop/output.c',
'session/direct-ipc.c',
'session/session.c',
'wayland/backend.c',
'wayland/output.c',
'wayland/wl_seat.c',
)
wlr_files += files('backend.c')
backend_deps = [
drm,
egl,
gbm,
libinput,
pixman,
xkbcommon,
wayland_server,
wlr_protos,
wlr_render,
]
if host_machine.system().startswith('freebsd')
backend_files += files('session/direct-freebsd.c')
else
backend_files += files('session/direct.c')
all_backends = ['drm', 'libinput', 'x11']
backends = get_option('backends')
if 'auto' in backends and get_option('auto_features').enabled()
backends = all_backends
elif 'auto' in backends and get_option('auto_features').disabled()
backends = []
endif
if logind.found()
backend_files += files('session/logind.c')
backend_deps += logind
endif
foreach backend : all_backends
if backend in backends or 'auto' in backends
subdir(backend)
endif
endforeach
subdir('x11')
subdir('multi')
subdir('wayland')
subdir('headless')
lib_wlr_backend = static_library(
'wlr_backend',
backend_files,
include_directories: wlr_inc,
link_whole: backend_parts,
dependencies: backend_deps,
)
subdir('session')

View File

@ -5,7 +5,9 @@
#include <time.h>
#include <wlr/backend/interface.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/util/log.h>
#include "backend/backend.h"
#include "backend/multi.h"
#include "util/signal.h"
@ -49,30 +51,19 @@ static void multi_backend_destroy(struct wlr_backend *wlr_backend) {
wl_list_remove(&backend->display_destroy.link);
struct subbackend_state *sub, *next;
wl_list_for_each_safe(sub, next, &backend->backends, link) {
// Some backends may depend on other backends, ie. destroying a backend may
// also destroy other backends
while (!wl_list_empty(&backend->backends)) {
struct subbackend_state *sub =
wl_container_of(backend->backends.next, sub, link);
wlr_backend_destroy(sub->backend);
}
// Destroy this backend only after removing all sub-backends
wlr_signal_emit_safe(&wlr_backend->events.destroy, backend);
wlr_backend_finish(wlr_backend);
free(backend);
}
static struct wlr_renderer *multi_backend_get_renderer(
struct wlr_backend *backend) {
struct wlr_multi_backend *multi = multi_backend_from_backend(backend);
struct subbackend_state *sub;
wl_list_for_each(sub, &multi->backends, link) {
struct wlr_renderer *rend = wlr_backend_get_renderer(sub->backend);
if (rend != NULL) {
return rend;
}
}
return NULL;
}
static struct wlr_session *multi_backend_get_session(
struct wlr_backend *_backend) {
struct wlr_multi_backend *backend = multi_backend_from_backend(_backend);
@ -93,12 +84,48 @@ static clockid_t multi_backend_get_presentation_clock(
return CLOCK_MONOTONIC;
}
struct wlr_backend_impl backend_impl = {
static int multi_backend_get_drm_fd(struct wlr_backend *backend) {
struct wlr_multi_backend *multi = multi_backend_from_backend(backend);
struct subbackend_state *sub;
wl_list_for_each(sub, &multi->backends, link) {
if (sub->backend->impl->get_drm_fd) {
return wlr_backend_get_drm_fd(sub->backend);
}
}
return -1;
}
static uint32_t multi_backend_get_buffer_caps(struct wlr_backend *backend) {
struct wlr_multi_backend *multi = multi_backend_from_backend(backend);
if (wl_list_empty(&multi->backends)) {
return 0;
}
uint32_t caps = WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF
| WLR_BUFFER_CAP_SHM;
struct subbackend_state *sub;
wl_list_for_each(sub, &multi->backends, link) {
uint32_t backend_caps = backend_get_buffer_caps(sub->backend);
if (backend_caps != 0) {
// only count backend capable of presenting a buffer
caps = caps & backend_caps;
}
}
return caps;
}
static const struct wlr_backend_impl backend_impl = {
.start = multi_backend_start,
.destroy = multi_backend_destroy,
.get_renderer = multi_backend_get_renderer,
.get_session = multi_backend_get_session,
.get_presentation_clock = multi_backend_get_presentation_clock,
.get_drm_fd = multi_backend_get_drm_fd,
.get_buffer_caps = multi_backend_get_buffer_caps,
};
static void handle_display_destroy(struct wl_listener *listener, void *data) {
@ -162,6 +189,9 @@ static struct subbackend_state *multi_backend_get_subbackend(struct wlr_multi_ba
bool wlr_multi_backend_add(struct wlr_backend *_multi,
struct wlr_backend *backend) {
assert(_multi && backend);
assert(_multi != backend);
struct wlr_multi_backend *multi = multi_backend_from_backend(_multi);
if (multi_backend_get_subbackend(multi, backend)) {
@ -169,21 +199,12 @@ bool wlr_multi_backend_add(struct wlr_backend *_multi,
return true;
}
struct wlr_renderer *multi_renderer =
multi_backend_get_renderer(&multi->backend);
struct wlr_renderer *backend_renderer = wlr_backend_get_renderer(backend);
if (multi_renderer != NULL && backend_renderer != NULL && multi_renderer != backend_renderer) {
wlr_log(WLR_ERROR, "Could not add backend: multiple renderers at the "
"same time aren't supported");
return false;
}
struct subbackend_state *sub = calloc(1, sizeof(struct subbackend_state));
if (sub == NULL) {
wlr_log(WLR_ERROR, "Could not add backend: allocation failed");
return false;
}
wl_list_insert(&multi->backends, &sub->link);
wl_list_insert(multi->backends.prev, &sub->link);
sub->backend = backend;
sub->container = &multi->backend;

View File

@ -0,0 +1 @@
wlr_files += files('backend.c')

View File

@ -1,68 +0,0 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/util/log.h>
#include "backend/noop.h"
#include "util/signal.h"
struct wlr_noop_backend *noop_backend_from_backend(
struct wlr_backend *wlr_backend) {
assert(wlr_backend_is_noop(wlr_backend));
return (struct wlr_noop_backend *)wlr_backend;
}
static bool backend_start(struct wlr_backend *wlr_backend) {
struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend);
wlr_log(WLR_INFO, "Starting noop backend");
struct wlr_noop_output *output;
wl_list_for_each(output, &backend->outputs, link) {
wlr_output_update_enabled(&output->wlr_output, true);
wlr_signal_emit_safe(&backend->backend.events.new_output,
&output->wlr_output);
}
backend->started = true;
return true;
}
static void backend_destroy(struct wlr_backend *wlr_backend) {
struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend);
if (!wlr_backend) {
return;
}
struct wlr_noop_output *output, *output_tmp;
wl_list_for_each_safe(output, output_tmp, &backend->outputs, link) {
wlr_output_destroy(&output->wlr_output);
}
wlr_signal_emit_safe(&wlr_backend->events.destroy, backend);
free(backend);
}
static const struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
};
struct wlr_backend *wlr_noop_backend_create(struct wl_display *display) {
wlr_log(WLR_INFO, "Creating noop backend");
struct wlr_noop_backend *backend =
calloc(1, sizeof(struct wlr_noop_backend));
if (!backend) {
wlr_log(WLR_ERROR, "Failed to allocate wlr_noop_backend");
return NULL;
}
wlr_backend_init(&backend->backend, &backend_impl);
backend->display = display;
wl_list_init(&backend->outputs);
return &backend->backend;
}
bool wlr_backend_is_noop(struct wlr_backend *backend) {
return backend->impl == &backend_impl;
}

View File

@ -1,75 +0,0 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/util/log.h>
#include "backend/noop.h"
#include "util/signal.h"
static struct wlr_noop_output *noop_output_from_output(
struct wlr_output *wlr_output) {
assert(wlr_output_is_noop(wlr_output));
return (struct wlr_noop_output *)wlr_output;
}
static void output_transform(struct wlr_output *wlr_output,
enum wl_output_transform transform) {
// empty
}
static bool output_make_current(struct wlr_output *wlr_output, int *buffer_age) {
return true;
}
static bool output_swap_buffers(struct wlr_output *wlr_output,
pixman_region32_t *damage) {
return true;
}
static void output_destroy(struct wlr_output *wlr_output) {
struct wlr_noop_output *output =
noop_output_from_output(wlr_output);
wl_list_remove(&output->link);
free(output);
}
static const struct wlr_output_impl output_impl = {
.transform = output_transform,
.destroy = output_destroy,
.make_current = output_make_current,
.swap_buffers = output_swap_buffers,
};
bool wlr_output_is_noop(struct wlr_output *wlr_output) {
return wlr_output->impl == &output_impl;
}
struct wlr_output *wlr_noop_add_output(struct wlr_backend *wlr_backend) {
struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend);
struct wlr_noop_output *output = calloc(1, sizeof(struct wlr_noop_output));
if (output == NULL) {
wlr_log(WLR_ERROR, "Failed to allocate wlr_noop_output");
return NULL;
}
output->backend = backend;
wlr_output_init(&output->wlr_output, &backend->backend, &output_impl,
backend->display);
struct wlr_output *wlr_output = &output->wlr_output;
strncpy(wlr_output->make, "noop", sizeof(wlr_output->make));
strncpy(wlr_output->model, "noop", sizeof(wlr_output->model));
snprintf(wlr_output->name, sizeof(wlr_output->name), "NOOP-%d",
wl_list_length(&backend->outputs) + 1);
wl_list_insert(&backend->outputs, &output->link);
if (backend->started) {
wlr_output_update_enabled(wlr_output, true);
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
}
return wlr_output;
}

View File

@ -1,268 +0,0 @@
#include <assert.h>
#include <linux/input.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/consio.h>
#include <sys/ioctl.h>
#include <sys/kbio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#include <wayland-server.h>
#include <wlr/backend/session/interface.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#include "backend/session/direct-ipc.h"
#include "util/signal.h"
const struct session_impl session_direct;
struct direct_session {
struct wlr_session base;
int tty_fd;
int old_tty;
int old_kbmode;
int sock;
pid_t child;
struct wl_event_source *vt_source;
};
static struct direct_session *direct_session_from_session(
struct wlr_session *base) {
assert(base->impl == &session_direct);
return (struct direct_session *)base;
}
static int direct_session_open(struct wlr_session *base, const char *path) {
struct direct_session *session = direct_session_from_session(base);
int fd = direct_ipc_open(session->sock, path);
if (fd < 0) {
wlr_log(WLR_ERROR, "Failed to open %s: %s%s", path, strerror(-fd),
fd == -EINVAL ? "; is another display server running?" : "");
return fd;
}
return fd;
}
static void direct_session_close(struct wlr_session *base, int fd) {
struct direct_session *session = direct_session_from_session(base);
int ev;
struct drm_version dv = {0};
if (ioctl(fd, DRM_IOCTL_VERSION, &dv) == 0) {
direct_ipc_dropmaster(session->sock, fd);
} else if (ioctl(fd, EVIOCGVERSION, &ev) == 0) {
ioctl(fd, EVIOCREVOKE, 0);
}
close(fd);
}
static bool direct_change_vt(struct wlr_session *base, unsigned vt) {
struct direct_session *session = direct_session_from_session(base);
// Only seat0 has VTs associated with it
if (strcmp(session->base.seat, "seat0") != 0) {
return true;
}
return ioctl(session->tty_fd, VT_ACTIVATE, (int)vt) == 0;
}
static void direct_session_destroy(struct wlr_session *base) {
struct direct_session *session = direct_session_from_session(base);
if (strcmp(session->base.seat, "seat0") == 0) {
struct vt_mode mode = {
.mode = VT_AUTO,
};
errno = 0;
ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode);
ioctl(session->tty_fd, KDSETMODE, KD_TEXT);
ioctl(session->tty_fd, VT_SETMODE, &mode);
ioctl(session->tty_fd, VT_ACTIVATE, session->old_tty);
if (errno) {
wlr_log(WLR_ERROR, "Failed to restore tty");
}
wl_event_source_remove(session->vt_source);
close(session->tty_fd);
}
direct_ipc_finish(session->sock, session->child);
close(session->sock);
free(session);
}
static int vt_handler(int signo, void *data) {
struct direct_session *session = data;
struct drm_version dv = {0};
struct wlr_device *dev;
if (session->base.active) {
session->base.active = false;
wlr_signal_emit_safe(&session->base.session_signal, session);
wl_list_for_each(dev, &session->base.devices, link) {
if (ioctl(dev->fd, DRM_IOCTL_VERSION, &dv) == 0) {
direct_ipc_dropmaster(session->sock, dev->fd);
}
}
ioctl(session->tty_fd, VT_RELDISP, 1);
} else {
ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ);
wl_list_for_each(dev, &session->base.devices, link) {
if (ioctl(dev->fd, DRM_IOCTL_VERSION, &dv) == 0) {
direct_ipc_setmaster(session->sock, dev->fd);
}
}
session->base.active = true;
wlr_signal_emit_safe(&session->base.session_signal, session);
}
return 1;
}
static bool setup_tty(struct direct_session *session, struct wl_display *display) {
int fd = -1, tty = -1, tty0_fd = -1, old_tty = 1;
if ((tty0_fd = open("/dev/ttyv0", O_RDWR | O_CLOEXEC)) < 0) {
wlr_log_errno(WLR_ERROR, "Could not open /dev/ttyv0 to find a free vt");
goto error;
}
if (ioctl(tty0_fd, VT_GETACTIVE, &old_tty) != 0) {
wlr_log_errno(WLR_ERROR, "Could not get active vt");
goto error;
}
if (ioctl(tty0_fd, VT_OPENQRY, &tty) != 0) {
wlr_log_errno(WLR_ERROR, "Could not find a free vt");
goto error;
}
close(tty0_fd);
char tty_path[64];
snprintf(tty_path, sizeof(tty_path), "/dev/ttyv%d", tty - 1);
wlr_log(WLR_INFO, "Using tty %s", tty_path);
fd = open(tty_path, O_RDWR | O_NOCTTY | O_CLOEXEC);
if (fd == -1) {
wlr_log_errno(WLR_ERROR, "Cannot open tty");
return false;
}
ioctl(fd, VT_ACTIVATE, tty);
ioctl(fd, VT_WAITACTIVE, tty);
int old_kbmode;
if (ioctl(fd, KDGKBMODE, &old_kbmode)) {
wlr_log_errno(WLR_ERROR, "Failed to read tty %d keyboard mode", tty);
goto error;
}
if (ioctl(fd, KDSKBMODE, K_CODE)) {
wlr_log_errno(WLR_ERROR,
"Failed to set keyboard mode K_CODE on tty %d", tty);
goto error;
}
if (ioctl(fd, KDSETMODE, KD_GRAPHICS)) {
wlr_log_errno(WLR_ERROR, "Failed to set graphics mode on tty %d", tty);
goto error;
}
struct vt_mode mode = {
.mode = VT_PROCESS,
.relsig = SIGUSR2,
.acqsig = SIGUSR2,
.frsig = SIGIO, // has to be set
};
if (ioctl(fd, VT_SETMODE, &mode) < 0) {
wlr_log(WLR_ERROR, "Failed to take control of tty %d", tty);
goto error;
}
struct wl_event_loop *loop = wl_display_get_event_loop(display);
session->vt_source = wl_event_loop_add_signal(loop, SIGUSR2,
vt_handler, session);
if (!session->vt_source) {
goto error;
}
session->base.vtnr = tty;
session->tty_fd = fd;
session->old_tty = old_tty;
session->old_kbmode = old_kbmode;
return true;
error:
// In case we could not get the last active one, drop back to tty 1,
// better than hanging in a useless blank console. Otherwise activate the
// last active.
ioctl(fd, VT_ACTIVATE, old_tty);
close(fd);
return false;
}
static struct wlr_session *direct_session_create(struct wl_display *disp) {
struct direct_session *session = calloc(1, sizeof(*session));
if (!session) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return NULL;
}
session->sock = direct_ipc_init(&session->child);
if (session->sock == -1) {
goto error_session;
}
const char *seat = getenv("XDG_SEAT");
if (!seat) {
seat = "seat0";
}
if (strcmp(seat, "seat0") == 0) {
if (!setup_tty(session, disp)) {
goto error_ipc;
}
} else {
session->base.vtnr = 0;
session->tty_fd = -1;
}
wlr_log(WLR_INFO, "Successfully loaded direct session");
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat);
session->base.impl = &session_direct;
return &session->base;
error_ipc:
direct_ipc_finish(session->sock, session->child);
close(session->sock);
error_session:
free(session);
return NULL;
}
const struct session_impl session_direct = {
.create = direct_session_create,
.destroy = direct_session_destroy,
.open = direct_session_open,
.close = direct_session_close,
.change_vt = direct_change_vt,
};

View File

@ -1,269 +0,0 @@
#define _POSIX_C_SOURCE 200809L
#ifdef __FreeBSD__
#define __BSD_VISIBLE 1
#include <linux/input.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <wlr/config.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#ifdef __linux__
#include <sys/sysmacros.h>
#include <linux/major.h>
#endif
#include "backend/session/direct-ipc.h"
enum { DRM_MAJOR = 226 };
#if WLR_HAS_LIBCAP
#include <sys/capability.h>
static bool have_permissions(void) {
cap_t cap = cap_get_proc();
cap_flag_value_t val;
if (!cap || cap_get_flag(cap, CAP_SYS_ADMIN, CAP_PERMITTED, &val) || val != CAP_SET) {
wlr_log(WLR_ERROR, "Do not have CAP_SYS_ADMIN; cannot become DRM master");
cap_free(cap);
return false;
}
cap_free(cap);
return true;
}
#else
static bool have_permissions(void) {
#ifdef __linux__
if (geteuid() != 0) {
wlr_log(WLR_ERROR, "Do not have root privileges; cannot become DRM master");
return false;
}
#endif
return true;
}
#endif
static void send_msg(int sock, int fd, void *buf, size_t buf_len) {
char control[CMSG_SPACE(sizeof(fd))] = {0};
struct iovec iovec = { .iov_base = buf, .iov_len = buf_len };
struct msghdr msghdr = {0};
if (buf) {
msghdr.msg_iov = &iovec;
msghdr.msg_iovlen = 1;
}
if (fd >= 0) {
msghdr.msg_control = &control;
msghdr.msg_controllen = sizeof(control);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msghdr);
*cmsg = (struct cmsghdr) {
.cmsg_level = SOL_SOCKET,
.cmsg_type = SCM_RIGHTS,
.cmsg_len = CMSG_LEN(sizeof(fd)),
};
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
}
ssize_t ret;
do {
ret = sendmsg(sock, &msghdr, 0);
} while (ret < 0 && errno == EINTR);
}
static ssize_t recv_msg(int sock, int *fd_out, void *buf, size_t buf_len) {
char control[CMSG_SPACE(sizeof(*fd_out))] = {0};
struct iovec iovec = { .iov_base = buf, .iov_len = buf_len };
struct msghdr msghdr = {0};
if (buf) {
msghdr.msg_iov = &iovec;
msghdr.msg_iovlen = 1;
}
if (fd_out) {
msghdr.msg_control = &control;
msghdr.msg_controllen = sizeof(control);
}
ssize_t ret;
do {
ret = recvmsg(sock, &msghdr, MSG_CMSG_CLOEXEC);
} while (ret < 0 && errno == EINTR);
if (fd_out) {
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msghdr);
if (cmsg) {
memcpy(fd_out, CMSG_DATA(cmsg), sizeof(*fd_out));
} else {
*fd_out = -1;
}
}
return ret;
}
enum msg_type {
MSG_OPEN,
MSG_SETMASTER,
MSG_DROPMASTER,
MSG_END,
};
struct msg {
enum msg_type type;
char path[256];
};
static void communicate(int sock) {
struct msg msg;
int drm_fd = -1;
bool running = true;
while (running && recv_msg(sock, &drm_fd, &msg, sizeof(msg)) > 0) {
switch (msg.type) {
case MSG_OPEN:
errno = 0;
// These are the same flags that logind opens files with
int fd = open(msg.path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
int ret = errno;
if (fd == -1) {
goto error;
}
#ifndef __FreeBSD__
struct stat st;
if (fstat(fd, &st) < 0) {
ret = errno;
goto error;
}
uint32_t maj = major(st.st_rdev);
if (maj != INPUT_MAJOR && maj != DRM_MAJOR) {
ret = ENOTSUP;
goto error;
}
if (maj == DRM_MAJOR && drmSetMaster(fd)) {
ret = errno;
}
#else
int ev;
struct drm_version dv = {0};
if (ioctl(fd, EVIOCGVERSION, &ev) == -1 &&
ioctl(fd, DRM_IOCTL_VERSION, &dv) == -1) {
ret = ENOTSUP;
goto error;
}
if (dv.version_major != 0 && drmSetMaster(fd)) {
ret = errno;
}
#endif
error:
send_msg(sock, ret ? -1 : fd, &ret, sizeof(ret));
if (fd >= 0) {
close(fd);
}
break;
case MSG_SETMASTER:
drmSetMaster(drm_fd);
close(drm_fd);
send_msg(sock, -1, NULL, 0);
break;
case MSG_DROPMASTER:
drmDropMaster(drm_fd);
close(drm_fd);
send_msg(sock, -1, NULL, 0);
break;
case MSG_END:
running = false;
send_msg(sock, -1, NULL, 0);
break;
}
}
close(sock);
}
int direct_ipc_open(int sock, const char *path) {
struct msg msg = { .type = MSG_OPEN };
snprintf(msg.path, sizeof(msg.path), "%s", path);
send_msg(sock, -1, &msg, sizeof(msg));
int fd, err, ret;
int retry = 0;
do {
ret = recv_msg(sock, &fd, &err, sizeof(err));
} while (ret == 0 && retry++ < 3);
return err ? -err : fd;
}
void direct_ipc_setmaster(int sock, int fd) {
struct msg msg = { .type = MSG_SETMASTER };
send_msg(sock, fd, &msg, sizeof(msg));
recv_msg(sock, NULL, NULL, 0);
}
void direct_ipc_dropmaster(int sock, int fd) {
struct msg msg = { .type = MSG_DROPMASTER };
send_msg(sock, fd, &msg, sizeof(msg));
recv_msg(sock, NULL, NULL, 0);
}
void direct_ipc_finish(int sock, pid_t pid) {
struct msg msg = { .type = MSG_END };
send_msg(sock, -1, &msg, sizeof(msg));
recv_msg(sock, NULL, NULL, 0);
waitpid(pid, NULL, 0);
}
int direct_ipc_init(pid_t *pid_out) {
if (!have_permissions()) {
return -1;
}
int sock[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) < 0) {
wlr_log_errno(WLR_ERROR, "Failed to create socket pair");
return -1;
}
pid_t pid = fork();
if (pid < 0) {
wlr_log_errno(WLR_ERROR, "Fork failed");
close(sock[0]);
close(sock[1]);
return -1;
} else if (pid == 0) {
close(sock[0]);
communicate(sock[1]);
_Exit(0);
}
close(sock[1]);
*pid_out = pid;
return sock[0];
}

View File

@ -1,278 +0,0 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/kd.h>
#include <linux/major.h>
#include <linux/vt.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#include <wayland-server.h>
#include <wlr/backend/session/interface.h>
#include <wlr/util/log.h>
#include "backend/session/direct-ipc.h"
#include "util/signal.h"
enum { DRM_MAJOR = 226 };
const struct session_impl session_direct;
struct direct_session {
struct wlr_session base;
int tty_fd;
int old_kbmode;
int sock;
pid_t child;
struct wl_event_source *vt_source;
};
static struct direct_session *direct_session_from_session(
struct wlr_session *base) {
assert(base->impl == &session_direct);
return (struct direct_session *)base;
}
static int direct_session_open(struct wlr_session *base, const char *path) {
struct direct_session *session = direct_session_from_session(base);
int fd = direct_ipc_open(session->sock, path);
if (fd < 0) {
wlr_log(WLR_ERROR, "Failed to open %s: %s%s", path, strerror(-fd),
fd == -EINVAL ? "; is another display server running?" : "");
return fd;
}
struct stat st;
if (fstat(fd, &st) < 0) {
close(fd);
return -errno;
}
if (major(st.st_rdev) == DRM_MAJOR) {
direct_ipc_setmaster(session->sock, fd);
}
return fd;
}
static void direct_session_close(struct wlr_session *base, int fd) {
struct direct_session *session = direct_session_from_session(base);
struct stat st;
if (fstat(fd, &st) < 0) {
wlr_log_errno(WLR_ERROR, "Stat failed");
close(fd);
return;
}
if (major(st.st_rdev) == DRM_MAJOR) {
direct_ipc_dropmaster(session->sock, fd);
} else if (major(st.st_rdev) == INPUT_MAJOR) {
ioctl(fd, EVIOCREVOKE, 0);
}
close(fd);
}
static bool direct_change_vt(struct wlr_session *base, unsigned vt) {
struct direct_session *session = direct_session_from_session(base);
// Only seat0 has VTs associated with it
if (strcmp(session->base.seat, "seat0") != 0) {
return true;
}
return ioctl(session->tty_fd, VT_ACTIVATE, (int)vt) == 0;
}
static void direct_session_destroy(struct wlr_session *base) {
struct direct_session *session = direct_session_from_session(base);
if (strcmp(session->base.seat, "seat0") == 0) {
struct vt_mode mode = {
.mode = VT_AUTO,
};
errno = 0;
ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode);
ioctl(session->tty_fd, KDSETMODE, KD_TEXT);
ioctl(session->tty_fd, VT_SETMODE, &mode);
if (errno) {
wlr_log(WLR_ERROR, "Failed to restore tty");
}
wl_event_source_remove(session->vt_source);
close(session->tty_fd);
}
direct_ipc_finish(session->sock, session->child);
close(session->sock);
free(session);
}
static int vt_handler(int signo, void *data) {
struct direct_session *session = data;
if (session->base.active) {
session->base.active = false;
wlr_signal_emit_safe(&session->base.session_signal, session);
struct wlr_device *dev;
wl_list_for_each(dev, &session->base.devices, link) {
if (major(dev->dev) == DRM_MAJOR) {
direct_ipc_dropmaster(session->sock,
dev->fd);
}
}
ioctl(session->tty_fd, VT_RELDISP, 1);
} else {
ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ);
struct wlr_device *dev;
wl_list_for_each(dev, &session->base.devices, link) {
if (major(dev->dev) == DRM_MAJOR) {
direct_ipc_setmaster(session->sock,
dev->fd);
}
}
session->base.active = true;
wlr_signal_emit_safe(&session->base.session_signal, session);
}
return 1;
}
static bool setup_tty(struct direct_session *session, struct wl_display *display) {
int fd = open("/dev/tty", O_RDWR);
if (fd == -1) {
wlr_log_errno(WLR_ERROR, "Cannot open /dev/tty");
return false;
}
struct vt_stat vt_stat;
if (ioctl(fd, VT_GETSTATE, &vt_stat)) {
wlr_log_errno(WLR_ERROR, "Could not get current tty number");
goto error;
}
int tty = vt_stat.v_active;
int ret, kd_mode, old_kbmode;
ret = ioctl(fd, KDGETMODE, &kd_mode);
if (ret) {
wlr_log_errno(WLR_ERROR, "Failed to get tty mode");
goto error;
}
if (kd_mode != KD_TEXT) {
wlr_log(WLR_ERROR,
"tty already in graphics mode; is another display server running?");
goto error;
}
ioctl(fd, VT_ACTIVATE, tty);
ioctl(fd, VT_WAITACTIVE, tty);
if (ioctl(fd, KDGKBMODE, &old_kbmode)) {
wlr_log_errno(WLR_ERROR, "Failed to read keyboard mode");
goto error;
}
if (ioctl(fd, KDSKBMODE, K_OFF)) {
wlr_log_errno(WLR_ERROR, "Failed to set keyboard mode");
goto error;
}
if (ioctl(fd, KDSETMODE, KD_GRAPHICS)) {
wlr_log_errno(WLR_ERROR, "Failed to set graphics mode on tty");
goto error;
}
struct vt_mode mode = {
.mode = VT_PROCESS,
.relsig = SIGUSR2,
.acqsig = SIGUSR2,
};
if (ioctl(fd, VT_SETMODE, &mode) < 0) {
wlr_log(WLR_ERROR, "Failed to take control of tty");
goto error;
}
struct wl_event_loop *loop = wl_display_get_event_loop(display);
session->vt_source = wl_event_loop_add_signal(loop, SIGUSR2,
vt_handler, session);
if (!session->vt_source) {
goto error;
}
session->base.vtnr = tty;
session->tty_fd = fd;
session->old_kbmode = old_kbmode;
return true;
error:
close(fd);
return false;
}
static struct wlr_session *direct_session_create(struct wl_display *disp) {
struct direct_session *session = calloc(1, sizeof(*session));
if (!session) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return NULL;
}
session->sock = direct_ipc_init(&session->child);
if (session->sock == -1) {
goto error_session;
}
const char *seat = getenv("XDG_SEAT");
if (!seat) {
seat = "seat0";
}
if (strcmp(seat, "seat0") == 0) {
if (!setup_tty(session, disp)) {
goto error_ipc;
}
} else {
session->base.vtnr = 0;
session->tty_fd = -1;
}
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat);
session->base.impl = &session_direct;
wlr_log(WLR_INFO, "Successfully loaded direct session");
return &session->base;
error_ipc:
direct_ipc_finish(session->sock, session->child);
close(session->sock);
error_session:
free(session);
return NULL;
}
const struct session_impl session_direct = {
.create = direct_session_create,
.destroy = direct_session_destroy,
.open = direct_session_open,
.close = direct_session_close,
.change_vt = direct_change_vt,
};

View File

@ -1,683 +0,0 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#include <wayland-server.h>
#include <wlr/backend/session/interface.h>
#include <wlr/config.h>
#include <wlr/util/log.h>
#include "util/signal.h"
#if WLR_HAS_SYSTEMD
#include <systemd/sd-bus.h>
#include <systemd/sd-login.h>
#elif WLR_HAS_ELOGIND
#include <elogind/sd-bus.h>
#include <elogind/sd-login.h>
#endif
enum { DRM_MAJOR = 226 };
const struct session_impl session_logind;
struct logind_session {
struct wlr_session base;
sd_bus *bus;
struct wl_event_source *event;
char *id;
char *path;
// specifies whether a drm device was taken
// if so, the session will be (de)activated with the drm fd,
// otherwise with the dbus PropertiesChanged on "active" signal
bool has_drm;
};
static struct logind_session *logind_session_from_session(
struct wlr_session *base) {
assert(base->impl == &session_logind);
return (struct logind_session *)base;
}
static int logind_take_device(struct wlr_session *base, const char *path) {
struct logind_session *session = logind_session_from_session(base);
int fd = -1;
int ret;
sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
struct stat st;
if (stat(path, &st) < 0) {
wlr_log(WLR_ERROR, "Failed to stat '%s'", path);
return -1;
}
if (major(st.st_rdev) == DRM_MAJOR) {
session->has_drm = true;
}
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
session->path, "org.freedesktop.login1.Session", "TakeDevice",
&error, &msg, "uu", major(st.st_rdev), minor(st.st_rdev));
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to take device '%s': %s", path,
error.message);
goto out;
}
int paused = 0;
ret = sd_bus_message_read(msg, "hb", &fd, &paused);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to parse D-Bus response for '%s': %s",
path, strerror(-ret));
goto out;
}
// The original fd seems to be closed when the message is freed
// so we just clone it.
fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
if (fd < 0) {
wlr_log(WLR_ERROR, "Failed to clone file descriptor for '%s': %s",
path, strerror(errno));
goto out;
}
out:
sd_bus_error_free(&error);
sd_bus_message_unref(msg);
return fd;
}
static void logind_release_device(struct wlr_session *base, int fd) {
struct logind_session *session = logind_session_from_session(base);
struct stat st;
if (fstat(fd, &st) < 0) {
wlr_log(WLR_ERROR, "Failed to stat device '%d': %s", fd,
strerror(errno));
return;
}
sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
int ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
session->path, "org.freedesktop.login1.Session", "ReleaseDevice",
&error, &msg, "uu", major(st.st_rdev), minor(st.st_rdev));
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to release device '%d': %s", fd,
error.message);
}
sd_bus_error_free(&error);
sd_bus_message_unref(msg);
close(fd);
}
static bool logind_change_vt(struct wlr_session *base, unsigned vt) {
struct logind_session *session = logind_session_from_session(base);
// Only seat0 has VTs associated with it
if (strcmp(session->base.seat, "seat0") != 0) {
return true;
}
int ret;
sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
"/org/freedesktop/login1/seat/self", "org.freedesktop.login1.Seat", "SwitchTo",
&error, &msg, "u", (uint32_t)vt);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to change to vt '%d'", vt);
}
sd_bus_error_free(&error);
sd_bus_message_unref(msg);
return ret >= 0;
}
static bool find_session_path(struct logind_session *session) {
int ret;
sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
"/org/freedesktop/login1", "org.freedesktop.login1.Manager",
"GetSession", &error, &msg, "s", session->id);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to get session path: %s", error.message);
goto out;
}
const char *path;
ret = sd_bus_message_read(msg, "o", &path);
if (ret < 0) {
wlr_log(WLR_ERROR, "Could not parse session path: %s", error.message);
goto out;
}
session->path = strdup(path);
out:
sd_bus_error_free(&error);
sd_bus_message_unref(msg);
return ret >= 0;
}
static bool session_activate(struct logind_session *session) {
int ret;
sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
session->path, "org.freedesktop.login1.Session", "Activate",
&error, &msg, "");
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to activate session: %s", error.message);
}
sd_bus_error_free(&error);
sd_bus_message_unref(msg);
return ret >= 0;
}
static bool take_control(struct logind_session *session) {
int ret;
sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
session->path, "org.freedesktop.login1.Session", "TakeControl",
&error, &msg, "b", false);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to take control of session: %s",
error.message);
}
sd_bus_error_free(&error);
sd_bus_message_unref(msg);
return ret >= 0;
}
static void release_control(struct logind_session *session) {
int ret;
sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
session->path, "org.freedesktop.login1.Session", "ReleaseControl",
&error, &msg, "");
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to release control of session: %s",
error.message);
}
sd_bus_error_free(&error);
sd_bus_message_unref(msg);
}
static void logind_session_destroy(struct wlr_session *base) {
struct logind_session *session = logind_session_from_session(base);
release_control(session);
wl_event_source_remove(session->event);
sd_bus_unref(session->bus);
free(session->id);
free(session->path);
free(session);
}
static int session_removed(sd_bus_message *msg, void *userdata,
sd_bus_error *ret_error) {
wlr_log(WLR_INFO, "SessionRemoved signal received");
return 0;
}
static struct wlr_device *find_device(struct wlr_session *session,
dev_t devnum) {
struct wlr_device *dev;
wl_list_for_each(dev, &session->devices, link) {
if (dev->dev == devnum) {
return dev;
}
}
wlr_log(WLR_ERROR, "Tried to use dev_t %lu not opened by session",
(unsigned long)devnum);
assert(0);
}
static int pause_device(sd_bus_message *msg, void *userdata,
sd_bus_error *ret_error) {
struct logind_session *session = userdata;
int ret;
uint32_t major, minor;
const char *type;
ret = sd_bus_message_read(msg, "uus", &major, &minor, &type);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to parse D-Bus response for PauseDevice: %s",
strerror(-ret));
goto error;
}
if (major == DRM_MAJOR) {
assert(session->has_drm);
session->base.active = false;
wlr_signal_emit_safe(&session->base.session_signal, session);
}
if (strcmp(type, "pause") == 0) {
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
session->path, "org.freedesktop.login1.Session", "PauseDeviceComplete",
ret_error, &msg, "uu", major, minor);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to send PauseDeviceComplete signal: %s",
strerror(-ret));
}
}
error:
return 0;
}
static int resume_device(sd_bus_message *msg, void *userdata,
sd_bus_error *ret_error) {
struct logind_session *session = userdata;
int ret;
int fd;
uint32_t major, minor;
ret = sd_bus_message_read(msg, "uuh", &major, &minor, &fd);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to parse D-Bus response for ResumeDevice: %s",
strerror(-ret));
goto error;
}
if (major == DRM_MAJOR) {
struct wlr_device *dev = find_device(&session->base, makedev(major, minor));
close(dev->fd);
if (fcntl(fd, F_DUPFD_CLOEXEC, dev->fd) < 0) {
wlr_log_errno(WLR_ERROR, "Failed to duplicate file descriptor");
goto error;
}
if (!session->base.active) {
session->base.active = true;
wlr_signal_emit_safe(&session->base.session_signal, session);
}
}
error:
return 0;
}
static int properties_changed(sd_bus_message *msg, void *userdata,
sd_bus_error *ret_error) {
struct logind_session *session = userdata;
int ret = 0;
// if we have a drm fd we don't depend on this
if (session->has_drm) {
return 0;
}
// PropertiesChanged arg 1: interface
const char *interface;
ret = sd_bus_message_read_basic(msg, 's', &interface); // skip path
if (ret < 0) {
goto error;
}
if (strcmp(interface, "org.freedesktop.login1.Session") != 0) {
// not interesting for us; ignore
wlr_log(WLR_DEBUG, "ignoring PropertiesChanged from %s", interface);
return 0;
}
// PropertiesChanged arg 2: changed properties with values
ret = sd_bus_message_enter_container(msg, 'a', "{sv}");
if (ret < 0) {
goto error;
}
const char *s;
while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) {
ret = sd_bus_message_read_basic(msg, 's', &s);
if (ret < 0) {
goto error;
}
if (strcmp(s, "Active") == 0) {
int ret;
ret = sd_bus_message_enter_container(msg, 'v', "b");
if (ret < 0) {
goto error;
}
bool active;
ret = sd_bus_message_read_basic(msg, 'b', &active);
if (ret < 0) {
goto error;
}
if (session->base.active != active) {
session->base.active = active;
wlr_signal_emit_safe(&session->base.session_signal, session);
}
return 0;
} else {
sd_bus_message_skip(msg, "{sv}");
}
ret = sd_bus_message_exit_container(msg);
if (ret < 0) {
goto error;
}
}
if (ret < 0) {
goto error;
}
ret = sd_bus_message_exit_container(msg);
if (ret < 0) {
goto error;
}
// PropertiesChanged arg 3: changed properties without values
sd_bus_message_enter_container(msg, 'a', "s");
while ((ret = sd_bus_message_read_basic(msg, 's', &s)) > 0) {
if (strcmp(s, "Active") == 0) {
sd_bus_error error = SD_BUS_ERROR_NULL;
bool active;
ret = sd_bus_get_property_trivial(session->bus,
"org.freedesktop.login1", session->path,
"org.freedesktop.login1.Session", "Active", &error,
'b', &active);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to get 'Active' property: %s",
error.message);
return 0;
}
if (session->base.active != active) {
session->base.active = active;
wlr_signal_emit_safe(&session->base.session_signal, session);
}
return 0;
}
}
if (ret < 0) {
goto error;
}
return 0;
error:
wlr_log(WLR_ERROR, "Failed to parse D-Bus PropertiesChanged: %s",
strerror(-ret));
return 0;
}
static bool add_signal_matches(struct logind_session *session) {
int ret;
char str[256];
const char *fmt = "type='signal',"
"sender='org.freedesktop.login1',"
"interface='org.freedesktop.login1.%s',"
"member='%s',"
"path='%s'";
snprintf(str, sizeof(str), fmt, "Manager", "SessionRemoved",
"/org/freedesktop/login1");
ret = sd_bus_add_match(session->bus, NULL, str, session_removed, session);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
return false;
}
snprintf(str, sizeof(str), fmt, "Session", "PauseDevice", session->path);
ret = sd_bus_add_match(session->bus, NULL, str, pause_device, session);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
return false;
}
snprintf(str, sizeof(str), fmt, "Session", "ResumeDevice", session->path);
ret = sd_bus_add_match(session->bus, NULL, str, resume_device, session);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
return false;
}
ret = sd_bus_match_signal(session->bus, NULL, "org.freedesktop.login1",
session->path, "org.freedesktop.DBus.Properties", "PropertiesChanged",
properties_changed, session);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
return false;
}
return true;
}
static int dbus_event(int fd, uint32_t mask, void *data) {
sd_bus *bus = data;
while (sd_bus_process(bus, NULL) > 0) {
// Do nothing.
}
return 1;
}
static bool contains_str(const char *needle, const char **haystack) {
for (int i = 0; haystack[i]; i++) {
if (strcmp(haystack[i], needle) == 0) {
return true;
}
}
return false;
}
static bool get_greeter_session(char **session_id) {
char *class = NULL;
char **user_sessions = NULL;
int user_session_count = sd_uid_get_sessions(getuid(), 1, &user_sessions);
if (user_session_count < 0) {
wlr_log(WLR_ERROR, "Failed to get sessions");
goto out;
}
if (user_session_count == 0) {
wlr_log(WLR_ERROR, "User has no sessions");
goto out;
}
for (int i = 0; i < user_session_count; ++i) {
int ret = sd_session_get_class(user_sessions[i], &class);
if (ret < 0) {
continue;
}
if (strcmp(class, "greeter") == 0) {
*session_id = strdup(user_sessions[i]);
goto out;
}
}
out:
free(class);
for (int i = 0; i < user_session_count; ++i) {
free(user_sessions[i]);
}
free(user_sessions);
return *session_id != NULL;
}
static bool get_display_session(char **session_id) {
assert(session_id != NULL);
int ret;
// If there's a session active for the current process then just use that
ret = sd_pid_get_session(getpid(), session_id);
if (ret == 0) {
return true;
}
char *type = NULL;
char *state = NULL;
// Find any active sessions for the user if the process isn't part of an
// active session itself
ret = sd_uid_get_display(getuid(), session_id);
if (ret < 0 && ret != -ENODATA) {
wlr_log(WLR_ERROR, "Failed to get display: %s", strerror(-ret));
goto error;
}
if (ret != 0 && !get_greeter_session(session_id)) {
wlr_log(WLR_ERROR, "Couldn't find an active session or a greeter session");
goto error;
}
assert(*session_id != NULL);
// Check that the available session is graphical
ret = sd_session_get_type(*session_id, &type);
if (ret < 0) {
wlr_log(WLR_ERROR, "Couldn't get a type for session '%s': %s",
*session_id, strerror(-ret));
goto error;
}
const char *graphical_session_types[] = {"wayland", "x11", "mir", NULL};
if (!contains_str(type, graphical_session_types)) {
wlr_log(WLR_ERROR, "Session '%s' isn't a graphical session (type: '%s')",
*session_id, type);
goto error;
}
// Check that the session is active
ret = sd_session_get_state(*session_id, &state);
if (ret < 0) {
wlr_log(WLR_ERROR, "Couldn't get state for session '%s': %s",
*session_id, strerror(-ret));
goto error;
}
const char *active_states[] = {"active", "online", NULL};
if (!contains_str(state, active_states)) {
wlr_log(WLR_ERROR, "Session '%s' is not active", *session_id);
goto error;
}
free(type);
free(state);
return true;
error:
free(type);
free(state);
free(*session_id);
*session_id = NULL;
return false;
}
static struct wlr_session *logind_session_create(struct wl_display *disp) {
int ret;
struct logind_session *session = calloc(1, sizeof(*session));
if (!session) {
wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno));
return NULL;
}
if (!get_display_session(&session->id)) {
goto error;
}
char *seat;
ret = sd_session_get_seat(session->id, &seat);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to get seat id: %s", strerror(-ret));
goto error;
}
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat);
if (strcmp(seat, "seat0") == 0) {
ret = sd_session_get_vt(session->id, &session->base.vtnr);
if (ret < 0) {
wlr_log(WLR_ERROR, "Session not running in virtual terminal");
goto error;
}
}
free(seat);
ret = sd_bus_default_system(&session->bus);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s", strerror(-ret));
goto error;
}
if (!find_session_path(session)) {
sd_bus_unref(session->bus);
goto error;
}
if (!add_signal_matches(session)) {
goto error_bus;
}
struct wl_event_loop *event_loop = wl_display_get_event_loop(disp);
session->event = wl_event_loop_add_fd(event_loop, sd_bus_get_fd(session->bus),
WL_EVENT_READABLE, dbus_event, session->bus);
if (!session_activate(session)) {
goto error_bus;
}
if (!take_control(session)) {
goto error_bus;
}
wlr_log(WLR_INFO, "Successfully loaded logind session");
session->base.impl = &session_logind;
return &session->base;
error_bus:
sd_bus_unref(session->bus);
free(session->path);
error:
free(session->id);
return NULL;
}
const struct session_impl session_logind = {
.create = logind_session_create,
.destroy = logind_session_destroy,
.open = logind_take_device,
.close = logind_release_device,
.change_vt = logind_change_vt,
};

View File

@ -0,0 +1,7 @@
libseat = dependency('libseat',
version: '>=0.2.0',
fallback: ['seatd', 'libseat'],
default_options: ['server=disabled', 'man-pages=disabled'],
)
wlr_files += files('session.c')
wlr_deps += libseat

View File

@ -1,32 +1,174 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <libudev.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <wayland-server.h>
#include <unistd.h>
#include <time.h>
#include <wayland-server-core.h>
#include <wlr/backend/session.h>
#include <wlr/backend/session/interface.h>
#include <wlr/config.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "backend/session/session.h"
#include "util/signal.h"
extern const struct session_impl session_logind;
extern const struct session_impl session_direct;
#include <libseat.h>
static const struct session_impl *impls[] = {
#if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND
&session_logind,
#endif
&session_direct,
NULL,
#define WAIT_GPU_TIMEOUT 10000 // ms
static void handle_enable_seat(struct libseat *seat, void *data) {
struct wlr_session *session = data;
session->active = true;
wlr_signal_emit_safe(&session->events.active, NULL);
}
static void handle_disable_seat(struct libseat *seat, void *data) {
struct wlr_session *session = data;
session->active = false;
wlr_signal_emit_safe(&session->events.active, NULL);
libseat_disable_seat(session->seat_handle);
}
static int libseat_event(int fd, uint32_t mask, void *data) {
struct wlr_session *session = data;
if (libseat_dispatch(session->seat_handle, 0) == -1) {
wlr_log_errno(WLR_ERROR, "Failed to dispatch libseat");
wl_display_terminate(session->display);
}
return 1;
}
static struct libseat_seat_listener seat_listener = {
.enable_seat = handle_enable_seat,
.disable_seat = handle_disable_seat,
};
static int udev_event(int fd, uint32_t mask, void *data) {
static enum wlr_log_importance libseat_log_level_to_wlr(
enum libseat_log_level level) {
switch (level) {
case LIBSEAT_LOG_LEVEL_ERROR:
return WLR_ERROR;
case LIBSEAT_LOG_LEVEL_INFO:
return WLR_INFO;
default:
return WLR_DEBUG;
}
}
static void log_libseat(enum libseat_log_level level,
const char *fmt, va_list args) {
enum wlr_log_importance importance = libseat_log_level_to_wlr(level);
static char wlr_fmt[1024];
snprintf(wlr_fmt, sizeof(wlr_fmt), "[libseat] %s", fmt);
_wlr_vlog(importance, wlr_fmt, args);
}
static int libseat_session_init(struct wlr_session *session, struct wl_display *disp) {
libseat_set_log_handler(log_libseat);
libseat_set_log_level(LIBSEAT_LOG_LEVEL_INFO);
// libseat will take care of updating the logind state if necessary
setenv("XDG_SESSION_TYPE", "wayland", 1);
session->seat_handle = libseat_open_seat(&seat_listener, session);
if (session->seat_handle == NULL) {
wlr_log_errno(WLR_ERROR, "Unable to create seat");
return -1;
}
const char *seat_name = libseat_seat_name(session->seat_handle);
if (seat_name == NULL) {
wlr_log_errno(WLR_ERROR, "Unable to get seat info");
goto error;
}
snprintf(session->seat, sizeof(session->seat), "%s", seat_name);
struct wl_event_loop *event_loop = wl_display_get_event_loop(disp);
session->libseat_event = wl_event_loop_add_fd(event_loop, libseat_get_fd(session->seat_handle),
WL_EVENT_READABLE, libseat_event, session);
if (session->libseat_event == NULL) {
wlr_log(WLR_ERROR, "Failed to create libseat event source");
goto error;
}
// We may have received enable_seat immediately after the open_seat result,
// so, dispatch once without timeout to speed up activation.
if (libseat_dispatch(session->seat_handle, 0) == -1) {
wlr_log_errno(WLR_ERROR, "libseat dispatch failed");
goto error_dispatch;
}
wlr_log(WLR_INFO, "Successfully loaded libseat session");
return 0;
error_dispatch:
wl_event_source_remove(session->libseat_event);
session->libseat_event = NULL;
error:
libseat_close_seat(session->seat_handle);
session->seat_handle = NULL;
return -1;
}
static void libseat_session_finish(struct wlr_session *session) {
libseat_close_seat(session->seat_handle);
wl_event_source_remove(session->libseat_event);
session->seat_handle = NULL;
session->libseat_event = NULL;
}
static bool is_drm_card(const char *sysname) {
const char prefix[] = DRM_PRIMARY_MINOR_NAME;
if (strncmp(sysname, prefix, strlen(prefix)) != 0) {
return false;
}
for (size_t i = strlen(prefix); sysname[i] != '\0'; i++) {
if (sysname[i] < '0' || sysname[i] > '9') {
return false;
}
}
return true;
}
static void read_udev_change_event(struct wlr_device_change_event *event,
struct udev_device *udev_dev) {
const char *hotplug = udev_device_get_property_value(udev_dev, "HOTPLUG");
if (hotplug != NULL && strcmp(hotplug, "1") == 0) {
event->type = WLR_DEVICE_HOTPLUG;
struct wlr_device_hotplug_event *hotplug = &event->hotplug;
const char *connector =
udev_device_get_property_value(udev_dev, "CONNECTOR");
if (connector != NULL) {
hotplug->connector_id = strtoul(connector, NULL, 10);
}
const char *prop =
udev_device_get_property_value(udev_dev, "PROPERTY");
if (prop != NULL) {
hotplug->prop_id = strtoul(prop, NULL, 10);
}
return;
}
const char *lease = udev_device_get_property_value(udev_dev, "LEASE");
if (lease != NULL && strcmp(lease, "1") == 0) {
event->type = WLR_DEVICE_LEASE;
return;
}
}
static int handle_udev_event(int fd, uint32_t mask, void *data) {
struct wlr_session *session = data;
struct udev_device *udev_dev = udev_monitor_receive_device(session->mon);
@ -34,21 +176,48 @@ static int udev_event(int fd, uint32_t mask, void *data) {
return 1;
}
const char *sysname = udev_device_get_sysname(udev_dev);
const char *devnode = udev_device_get_devnode(udev_dev);
const char *action = udev_device_get_action(udev_dev);
wlr_log(WLR_DEBUG, "udev event for %s (%s)", sysname, action);
wlr_log(WLR_DEBUG, "udev event for %s (%s)",
udev_device_get_sysname(udev_dev), action);
if (!action || strcmp(action, "change") != 0) {
if (!is_drm_card(sysname) || !action || !devnode) {
goto out;
}
dev_t devnum = udev_device_get_devnum(udev_dev);
struct wlr_device *dev;
const char *seat = udev_device_get_property_value(udev_dev, "ID_SEAT");
if (!seat) {
seat = "seat0";
}
if (session->seat[0] != '\0' && strcmp(session->seat, seat) != 0) {
goto out;
}
wl_list_for_each(dev, &session->devices, link) {
if (dev->dev == devnum) {
wlr_signal_emit_safe(&dev->signal, session);
if (strcmp(action, "add") == 0) {
wlr_log(WLR_DEBUG, "DRM device %s added", sysname);
struct wlr_session_add_event event = {
.path = devnode,
};
wlr_signal_emit_safe(&session->events.add_drm_card, &event);
} else if (strcmp(action, "change") == 0 || strcmp(action, "remove") == 0) {
dev_t devnum = udev_device_get_devnum(udev_dev);
struct wlr_device *dev;
wl_list_for_each(dev, &session->devices, link) {
if (dev->dev != devnum) {
continue;
}
if (strcmp(action, "change") == 0) {
wlr_log(WLR_DEBUG, "DRM device %s changed", sysname);
struct wlr_device_change_event event = {0};
read_udev_change_event(&event, udev_dev);
wlr_signal_emit_safe(&dev->events.change, &event);
} else if (strcmp(action, "remove") == 0) {
wlr_log(WLR_DEBUG, "DRM device %s removed", sysname);
wlr_signal_emit_safe(&dev->events.remove, NULL);
} else {
assert(0);
}
break;
}
}
@ -65,38 +234,22 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
}
struct wlr_session *wlr_session_create(struct wl_display *disp) {
struct wlr_session *session = NULL;
const char *env_wlr_session = getenv("WLR_SESSION");
if (env_wlr_session) {
if (!strcmp(env_wlr_session, "logind") || !strcmp(env_wlr_session, "systemd")) {
#if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND
session = session_logind.create(disp);
#else
wlr_log(WLR_ERROR, "wlroots is not compiled with logind support");
#endif
} else if (!strcmp(env_wlr_session, "direct")) {
session = session_direct.create(disp);
} else {
wlr_log(WLR_ERROR, "WLR_SESSION has an invalid value: %s", env_wlr_session);
}
} else {
const struct session_impl **iter;
for (iter = impls; !session && *iter; ++iter) {
session = (*iter)->create(disp);
}
}
struct wlr_session *session = calloc(1, sizeof(*session));
if (!session) {
wlr_log(WLR_ERROR, "Failed to load session backend");
wlr_log_errno(WLR_ERROR, "Allocation failed");
return NULL;
}
session->active = true;
wl_signal_init(&session->session_signal);
wl_signal_init(&session->events.active);
wl_signal_init(&session->events.add_drm_card);
wl_signal_init(&session->events.destroy);
wl_list_init(&session->devices);
if (libseat_session_init(session, disp) == -1) {
wlr_log(WLR_ERROR, "Failed to load session backend");
goto error_open;
}
session->udev = udev_new();
if (!session->udev) {
wlr_log_errno(WLR_ERROR, "Failed to create udev context");
@ -116,12 +269,14 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) {
int fd = udev_monitor_get_fd(session->mon);
session->udev_event = wl_event_loop_add_fd(event_loop, fd,
WL_EVENT_READABLE, udev_event, session);
WL_EVENT_READABLE, handle_udev_event, session);
if (!session->udev_event) {
wlr_log_errno(WLR_ERROR, "Failed to create udev event source");
goto error_mon;
}
session->display = disp;
session->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(disp, &session->display_destroy);
@ -132,7 +287,9 @@ error_mon:
error_udev:
udev_unref(session->udev);
error_session:
session->impl->destroy(session);
libseat_session_finish(session);
error_open:
free(session);
return NULL;
}
@ -148,13 +305,22 @@ void wlr_session_destroy(struct wlr_session *session) {
udev_monitor_unref(session->mon);
udev_unref(session->udev);
session->impl->destroy(session);
struct wlr_device *dev, *tmp_dev;
wl_list_for_each_safe(dev, tmp_dev, &session->devices, link) {
wlr_session_close_file(session, dev);
}
libseat_session_finish(session);
free(session);
}
int wlr_session_open_file(struct wlr_session *session, const char *path) {
int fd = session->impl->open(session, path);
if (fd < 0) {
return fd;
struct wlr_device *wlr_session_open_file(struct wlr_session *session,
const char *path) {
int fd;
int device_id = libseat_open_device(session->seat_handle, path, &fd);
if (device_id == -1) {
wlr_log_errno(WLR_ERROR, "Failed to open device: '%s'", path);
return NULL;
}
struct wlr_device *dev = malloc(sizeof(*dev));
@ -171,86 +337,65 @@ int wlr_session_open_file(struct wlr_session *session, const char *path) {
dev->fd = fd;
dev->dev = st.st_rdev;
wl_signal_init(&dev->signal);
dev->device_id = device_id;
wl_signal_init(&dev->events.change);
wl_signal_init(&dev->events.remove);
wl_list_insert(&session->devices, &dev->link);
return fd;
return dev;
error:
libseat_close_device(session->seat_handle, device_id);
free(dev);
return fd;
close(fd);
return NULL;
}
static struct wlr_device *find_device(struct wlr_session *session, int fd) {
struct wlr_device *dev;
wl_list_for_each(dev, &session->devices, link) {
if (dev->fd == fd) {
return dev;
}
void wlr_session_close_file(struct wlr_session *session,
struct wlr_device *dev) {
if (libseat_close_device(session->seat_handle, dev->device_id) == -1) {
wlr_log_errno(WLR_ERROR, "Failed to close device %d", dev->device_id);
}
wlr_log(WLR_ERROR, "Tried to use fd %d not opened by session", fd);
assert(0);
}
void wlr_session_close_file(struct wlr_session *session, int fd) {
struct wlr_device *dev = find_device(session, fd);
session->impl->close(session, fd);
close(dev->fd);
wl_list_remove(&dev->link);
free(dev);
}
void wlr_session_signal_add(struct wlr_session *session, int fd,
struct wl_listener *listener) {
struct wlr_device *dev = find_device(session, fd);
wl_signal_add(&dev->signal, listener);
}
bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) {
if (!session) {
return false;
}
return session->impl->change_vt(session, vt);
return libseat_switch_session(session->seat_handle, vt) == 0;
}
/* Tests if 'path' is KMS compatible by trying to open it.
* It leaves the open device in *fd_out it it succeeds.
*/
static int open_if_kms(struct wlr_session *restrict session, const char *restrict path) {
int fd;
/* Tests if 'path' is KMS compatible by trying to open it. Returns the opened
* device on success. */
struct wlr_device *session_open_if_kms(struct wlr_session *restrict session,
const char *restrict path) {
if (!path) {
return -1;
return NULL;
}
fd = wlr_session_open_file(session, path);
if (fd < 0) {
return -1;
struct wlr_device *dev = wlr_session_open_file(session, path);
if (!dev) {
return NULL;
}
drmModeRes *res = drmModeGetResources(fd);
if (!res) {
goto out_fd;
if (!drmIsKMS(dev->fd)) {
wlr_log(WLR_DEBUG, "Ignoring '%s': not a KMS device", path);
wlr_session_close_file(session, dev);
return NULL;
}
drmModeFreeResources(res);
return fd;
out_fd:
wlr_session_close_file(session, fd);
return -1;
return dev;
}
static size_t explicit_find_gpus(struct wlr_session *session,
size_t ret_len, int ret[static ret_len], const char *str) {
static ssize_t explicit_find_gpus(struct wlr_session *session,
size_t ret_len, struct wlr_device *ret[static ret_len], const char *str) {
char *gpus = strdup(str);
if (!gpus) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return 0;
return -1;
}
size_t i = 0;
@ -261,8 +406,8 @@ static size_t explicit_find_gpus(struct wlr_session *session,
break;
}
ret[i] = open_if_kms(session, ptr);
if (ret[i] < 0) {
ret[i] = session_open_if_kms(session, ptr);
if (!ret[i]) {
wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr);
} else {
++i;
@ -273,30 +418,92 @@ static size_t explicit_find_gpus(struct wlr_session *session,
return i;
}
static struct udev_enumerate *enumerate_drm_cards(struct udev *udev) {
struct udev_enumerate *en = udev_enumerate_new(udev);
if (!en) {
wlr_log(WLR_ERROR, "udev_enumerate_new failed");
return NULL;
}
udev_enumerate_add_match_subsystem(en, "drm");
udev_enumerate_add_match_sysname(en, DRM_PRIMARY_MINOR_NAME "[0-9]*");
if (udev_enumerate_scan_devices(en) != 0) {
wlr_log(WLR_ERROR, "udev_enumerate_scan_devices failed");
udev_enumerate_unref(en);
return NULL;
}
return en;
}
static uint64_t get_current_time_ms(void) {
struct timespec ts = {0};
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
}
struct find_gpus_add_handler {
bool added;
struct wl_listener listener;
};
static void find_gpus_handle_add(struct wl_listener *listener, void *data) {
struct find_gpus_add_handler *handler =
wl_container_of(listener, handler, listener);
handler->added = true;
}
/* Tries to find the primary GPU by checking for the "boot_vga" attribute.
* If it's not found, it returns the first valid GPU it finds.
*/
size_t wlr_session_find_gpus(struct wlr_session *session,
size_t ret_len, int *ret) {
ssize_t wlr_session_find_gpus(struct wlr_session *session,
size_t ret_len, struct wlr_device **ret) {
const char *explicit = getenv("WLR_DRM_DEVICES");
if (explicit) {
return explicit_find_gpus(session, ret_len, ret, explicit);
}
#ifdef __FreeBSD__
// XXX: libudev-devd does not return any GPUs (yet?)
return explicit_find_gpus(session, ret_len, ret, "/dev/drm/0");
#else
struct udev_enumerate *en = udev_enumerate_new(session->udev);
struct udev_enumerate *en = enumerate_drm_cards(session->udev);
if (!en) {
wlr_log(WLR_ERROR, "Failed to create udev enumeration");
return -1;
}
udev_enumerate_add_match_subsystem(en, "drm");
udev_enumerate_add_match_sysname(en, "card[0-9]*");
udev_enumerate_scan_devices(en);
if (udev_enumerate_get_list_entry(en) == NULL) {
udev_enumerate_unref(en);
wlr_log(WLR_INFO, "Waiting for a DRM card device");
struct find_gpus_add_handler handler = {0};
handler.listener.notify = find_gpus_handle_add;
wl_signal_add(&session->events.add_drm_card, &handler.listener);
uint64_t started_at = get_current_time_ms();
uint64_t timeout = WAIT_GPU_TIMEOUT;
struct wl_event_loop *event_loop =
wl_display_get_event_loop(session->display);
while (!handler.added) {
int ret = wl_event_loop_dispatch(event_loop, (int)timeout);
if (ret < 0) {
wlr_log_errno(WLR_ERROR, "Failed to wait for DRM card device: "
"wl_event_loop_dispatch failed");
udev_enumerate_unref(en);
return -1;
}
uint64_t now = get_current_time_ms();
if (now >= started_at + WAIT_GPU_TIMEOUT) {
break;
}
timeout = started_at + WAIT_GPU_TIMEOUT - now;
}
wl_list_remove(&handler.listener.link);
en = enumerate_drm_cards(session->udev);
if (!en) {
return -1;
}
}
struct udev_list_entry *entry;
size_t i = 0;
@ -334,17 +541,18 @@ size_t wlr_session_find_gpus(struct wlr_session *session,
}
}
int fd = open_if_kms(session, udev_device_get_devnode(dev));
if (fd < 0) {
struct wlr_device *wlr_dev =
session_open_if_kms(session, udev_device_get_devnode(dev));
if (!wlr_dev) {
udev_device_unref(dev);
continue;
}
udev_device_unref(dev);
ret[i] = fd;
ret[i] = wlr_dev;
if (is_boot_vga) {
int tmp = ret[0];
struct wlr_device *tmp = ret[0];
ret[0] = ret[i];
ret[i] = tmp;
}
@ -355,5 +563,4 @@ size_t wlr_session_find_gpus(struct wlr_session *session,
udev_enumerate_unref(en);
return i;
#endif
}

View File

@ -1,24 +1,51 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <wlr/config.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <wayland-server.h>
#include <drm_fourcc.h>
#include <wayland-server-core.h>
#include <xf86drm.h>
#include <wlr/backend/interface.h>
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/egl.h>
#include <wlr/render/gles2.h>
#include <wlr/util/log.h>
#include "backend/wayland.h"
#include "render/drm_format_set.h"
#include "render/pixel_format.h"
#include "util/signal.h"
#include "drm-client-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "pointer-gestures-unstable-v1-client-protocol.h"
#include "presentation-time-client-protocol.h"
#include "xdg-activation-v1-client-protocol.h"
#include "xdg-decoration-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
#include "tablet-unstable-v2-client-protocol.h"
#include "relative-pointer-unstable-v1-client-protocol.h"
struct wlr_wl_linux_dmabuf_feedback_v1 {
struct wlr_wl_backend *backend;
dev_t main_device_id;
struct wlr_wl_linux_dmabuf_v1_table_entry *format_table;
size_t format_table_size;
dev_t tranche_target_device_id;
};
struct wlr_wl_linux_dmabuf_v1_table_entry {
uint32_t format;
uint32_t pad; /* unused */
uint64_t modifier;
};
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend) {
assert(wlr_backend_is_wl(backend));
@ -29,24 +56,31 @@ static int dispatch_events(int fd, uint32_t mask, void *data) {
struct wlr_wl_backend *wl = data;
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
if (mask & WL_EVENT_ERROR) {
wlr_log(WLR_ERROR, "Failed to read from remote Wayland display");
}
wl_display_terminate(wl->local_display);
return 0;
}
int count = 0;
if (mask & WL_EVENT_READABLE) {
return wl_display_dispatch(wl->remote_display);
count = wl_display_dispatch(wl->remote_display);
}
if (mask & WL_EVENT_WRITABLE) {
wl_display_flush(wl->remote_display);
return 0;
}
if (mask == 0) {
int count = wl_display_dispatch_pending(wl->remote_display);
count = wl_display_dispatch_pending(wl->remote_display);
wl_display_flush(wl->remote_display);
return count;
}
return 0;
if (count < 0) {
wlr_log(WLR_ERROR, "Failed to dispatch remote Wayland display");
wl_display_terminate(wl->local_display);
return 0;
}
return count;
}
static void xdg_wm_base_handle_ping(void *data,
@ -58,26 +92,302 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = {
xdg_wm_base_handle_ping,
};
static void presentation_handle_clock_id(void *data,
struct wp_presentation *presentation, uint32_t clock) {
struct wlr_wl_backend *wl = data;
wl->presentation_clock = clock;
}
static const struct wp_presentation_listener presentation_listener = {
.clock_id = presentation_handle_clock_id,
};
static void linux_dmabuf_v1_handle_format(void *data,
struct zwp_linux_dmabuf_v1 *linux_dmabuf_v1, uint32_t format) {
// Note, this event is deprecated
struct wlr_wl_backend *wl = data;
wlr_drm_format_set_add(&wl->linux_dmabuf_v1_formats, format,
DRM_FORMAT_MOD_INVALID);
}
static void linux_dmabuf_v1_handle_modifier(void *data,
struct zwp_linux_dmabuf_v1 *linux_dmabuf_v1, uint32_t format,
uint32_t modifier_hi, uint32_t modifier_lo) {
struct wlr_wl_backend *wl = data;
uint64_t modifier = ((uint64_t)modifier_hi << 32) | modifier_lo;
wlr_drm_format_set_add(&wl->linux_dmabuf_v1_formats, format, modifier);
}
static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_v1_listener = {
.format = linux_dmabuf_v1_handle_format,
.modifier = linux_dmabuf_v1_handle_modifier,
};
static void linux_dmabuf_feedback_v1_handle_done(void *data,
struct zwp_linux_dmabuf_feedback_v1 *feedback) {
// This space is intentionally left blank
}
static void linux_dmabuf_feedback_v1_handle_format_table(void *data,
struct zwp_linux_dmabuf_feedback_v1 *feedback, int fd, uint32_t size) {
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
feedback_data->format_table = NULL;
void *table_data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (table_data == MAP_FAILED) {
wlr_log_errno(WLR_ERROR, "failed to mmap DMA-BUF format table");
} else {
feedback_data->format_table = table_data;
feedback_data->format_table_size = size;
}
close(fd);
}
static void linux_dmabuf_feedback_v1_handle_main_device(void *data,
struct zwp_linux_dmabuf_feedback_v1 *feedback,
struct wl_array *dev_id_arr) {
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
dev_t dev_id;
assert(dev_id_arr->size == sizeof(dev_id));
memcpy(&dev_id, dev_id_arr->data, sizeof(dev_id));
feedback_data->main_device_id = dev_id;
drmDevice *device = NULL;
if (drmGetDeviceFromDevId(dev_id, 0, &device) != 0) {
wlr_log_errno(WLR_ERROR, "drmGetDeviceFromDevId failed");
return;
}
const char *name = NULL;
if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
name = device->nodes[DRM_NODE_RENDER];
} else {
// Likely a split display/render setup. Pick the primary node and hope
// Mesa will open the right render node under-the-hood.
assert(device->available_nodes & (1 << DRM_NODE_PRIMARY));
name = device->nodes[DRM_NODE_PRIMARY];
wlr_log(WLR_DEBUG, "DRM device %s has no render node, "
"falling back to primary node", name);
}
feedback_data->backend->drm_render_name = strdup(name);
drmFreeDevice(&device);
}
static void linux_dmabuf_feedback_v1_handle_tranche_done(void *data,
struct zwp_linux_dmabuf_feedback_v1 *feedback) {
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
feedback_data->tranche_target_device_id = 0;
}
static void linux_dmabuf_feedback_v1_handle_tranche_target_device(void *data,
struct zwp_linux_dmabuf_feedback_v1 *feedback,
struct wl_array *dev_id_arr) {
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
dev_t dev_id;
assert(dev_id_arr->size == sizeof(dev_id));
memcpy(&dev_id, dev_id_arr->data, sizeof(dev_id));
feedback_data->tranche_target_device_id = dev_id;
}
static void linux_dmabuf_feedback_v1_handle_tranche_formats(void *data,
struct zwp_linux_dmabuf_feedback_v1 *feedback,
struct wl_array *indices_arr) {
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
if (feedback_data->format_table == NULL) {
return;
}
if (feedback_data->tranche_target_device_id != feedback_data->main_device_id) {
return;
}
size_t table_cap = feedback_data->format_table_size /
sizeof(struct wlr_wl_linux_dmabuf_v1_table_entry);
uint16_t *index_ptr;
wl_array_for_each(index_ptr, indices_arr) {
assert(*index_ptr < table_cap);
const struct wlr_wl_linux_dmabuf_v1_table_entry *entry =
&feedback_data->format_table[*index_ptr];
wlr_drm_format_set_add(&feedback_data->backend->linux_dmabuf_v1_formats,
entry->format, entry->modifier);
}
}
static void linux_dmabuf_feedback_v1_handle_tranche_flags(void *data,
struct zwp_linux_dmabuf_feedback_v1 *feedback, uint32_t flags) {
// TODO: handle SCANOUT flag
}
static const struct zwp_linux_dmabuf_feedback_v1_listener
linux_dmabuf_feedback_v1_listener = {
.done = linux_dmabuf_feedback_v1_handle_done,
.format_table = linux_dmabuf_feedback_v1_handle_format_table,
.main_device = linux_dmabuf_feedback_v1_handle_main_device,
.tranche_done = linux_dmabuf_feedback_v1_handle_tranche_done,
.tranche_target_device = linux_dmabuf_feedback_v1_handle_tranche_target_device,
.tranche_formats = linux_dmabuf_feedback_v1_handle_tranche_formats,
.tranche_flags = linux_dmabuf_feedback_v1_handle_tranche_flags,
};
static bool device_has_name(const drmDevice *device, const char *name) {
for (size_t i = 0; i < DRM_NODE_MAX; i++) {
if (!(device->available_nodes & (1 << i))) {
continue;
}
if (strcmp(device->nodes[i], name) == 0) {
return true;
}
}
return false;
}
static char *get_render_name(const char *name) {
uint32_t flags = 0;
int devices_len = drmGetDevices2(flags, NULL, 0);
if (devices_len < 0) {
wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len));
return NULL;
}
drmDevice **devices = calloc(devices_len, sizeof(drmDevice *));
if (devices == NULL) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return NULL;
}
devices_len = drmGetDevices2(flags, devices, devices_len);
if (devices_len < 0) {
free(devices);
wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len));
return NULL;
}
const drmDevice *match = NULL;
for (int i = 0; i < devices_len; i++) {
if (device_has_name(devices[i], name)) {
match = devices[i];
break;
}
}
char *render_name = NULL;
if (match == NULL) {
wlr_log(WLR_ERROR, "Cannot find DRM device %s", name);
} else if (!(match->available_nodes & (1 << DRM_NODE_RENDER))) {
// Likely a split display/render setup. Pick the primary node and hope
// Mesa will open the right render node under-the-hood.
wlr_log(WLR_DEBUG, "DRM device %s has no render node, "
"falling back to primary node", name);
assert(match->available_nodes & (1 << DRM_NODE_PRIMARY));
render_name = strdup(match->nodes[DRM_NODE_PRIMARY]);
} else {
render_name = strdup(match->nodes[DRM_NODE_RENDER]);
}
for (int i = 0; i < devices_len; i++) {
drmFreeDevice(&devices[i]);
}
free(devices);
return render_name;
}
static void legacy_drm_handle_device(void *data, struct wl_drm *drm,
const char *name) {
struct wlr_wl_backend *wl = data;
wl->drm_render_name = get_render_name(name);
}
static void legacy_drm_handle_format(void *data, struct wl_drm *drm,
uint32_t format) {
// This space is intentionally left blank
}
static void legacy_drm_handle_authenticated(void *data, struct wl_drm *drm) {
// This space is intentionally left blank
}
static void legacy_drm_handle_capabilities(void *data, struct wl_drm *drm,
uint32_t caps) {
// This space is intentionally left blank
}
static const struct wl_drm_listener legacy_drm_listener = {
.device = legacy_drm_handle_device,
.format = legacy_drm_handle_format,
.authenticated = legacy_drm_handle_authenticated,
.capabilities = legacy_drm_handle_capabilities,
};
static void shm_handle_format(void *data, struct wl_shm *shm,
uint32_t shm_format) {
struct wlr_wl_backend *wl = data;
uint32_t drm_format = convert_wl_shm_format_to_drm(shm_format);
wlr_drm_format_set_add(&wl->shm_formats, drm_format, DRM_FORMAT_MOD_INVALID);
}
static const struct wl_shm_listener shm_listener = {
.format = shm_handle_format,
};
static void registry_global(void *data, struct wl_registry *registry,
uint32_t name, const char *iface, uint32_t version) {
struct wlr_wl_backend *wl = data;
wlr_log(WLR_DEBUG, "Remote wayland global: %s v%d", iface, version);
wlr_log(WLR_DEBUG, "Remote wayland global: %s v%" PRIu32, iface, version);
if (strcmp(iface, wl_compositor_interface.name) == 0) {
wl->compositor = wl_registry_bind(registry, name,
&wl_compositor_interface, 4);
} else if (strcmp(iface, wl_seat_interface.name) == 0) {
wl->seat = wl_registry_bind(registry, name,
struct wl_seat *wl_seat = wl_registry_bind(registry, name,
&wl_seat_interface, 5);
wl_seat_add_listener(wl->seat, &seat_listener, wl);
} else if (strcmp(iface, wl_shm_interface.name) == 0) {
wl->shm = wl_registry_bind(registry, name,
&wl_shm_interface, 1);
if (!create_wl_seat(wl_seat, wl)) {
wl_seat_destroy(wl_seat);
}
} else if (strcmp(iface, xdg_wm_base_interface.name) == 0) {
wl->xdg_wm_base = wl_registry_bind(registry, name,
&xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(wl->xdg_wm_base, &xdg_wm_base_listener, NULL);
} else if (strcmp(iface, zxdg_decoration_manager_v1_interface.name) == 0) {
wl->zxdg_decoration_manager_v1 = wl_registry_bind(registry, name,
&zxdg_decoration_manager_v1_interface, 1);
} else if (strcmp(iface, zwp_pointer_gestures_v1_interface.name) == 0) {
wl->zwp_pointer_gestures_v1 = wl_registry_bind(registry, name,
&zwp_pointer_gestures_v1_interface, version < 3 ? version : 3);
} else if (strcmp(iface, wp_presentation_interface.name) == 0) {
wl->presentation = wl_registry_bind(registry, name,
&wp_presentation_interface, 1);
wp_presentation_add_listener(wl->presentation,
&presentation_listener, wl);
} else if (strcmp(iface, zwp_tablet_manager_v2_interface.name) == 0) {
wl->tablet_manager = wl_registry_bind(registry, name,
&zwp_tablet_manager_v2_interface, 1);
} else if (strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0 &&
version >= 3) {
wl->zwp_linux_dmabuf_v1 = wl_registry_bind(registry, name,
&zwp_linux_dmabuf_v1_interface, version >= 4 ? 4 : version);
zwp_linux_dmabuf_v1_add_listener(wl->zwp_linux_dmabuf_v1,
&linux_dmabuf_v1_listener, wl);
} else if (strcmp(iface, zwp_relative_pointer_manager_v1_interface.name) == 0) {
wl->zwp_relative_pointer_manager_v1 = wl_registry_bind(registry, name,
&zwp_relative_pointer_manager_v1_interface, 1);
} else if (strcmp(iface, wl_drm_interface.name) == 0) {
wl->legacy_drm = wl_registry_bind(registry, name, &wl_drm_interface, 1);
wl_drm_add_listener(wl->legacy_drm, &legacy_drm_listener, wl);
} else if (strcmp(iface, wl_shm_interface.name) == 0) {
wl->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
wl_shm_add_listener(wl->shm, &shm_listener, wl);
} else if (strcmp(iface, xdg_activation_v1_interface.name) == 0) {
wl->activation_v1 = wl_registry_bind(registry, name,
&xdg_activation_v1_interface, 1);
}
}
@ -98,12 +408,19 @@ static const struct wl_registry_listener registry_listener = {
*/
static bool backend_start(struct wlr_backend *backend) {
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
wlr_log(WLR_INFO, "Initializating wayland backend");
wlr_log(WLR_INFO, "Starting Wayland backend");
wl->started = true;
if (wl->keyboard) {
create_wl_keyboard(wl->keyboard, wl);
struct wlr_wl_seat *seat;
wl_list_for_each(seat, &wl->seats, link) {
if (seat->keyboard) {
create_wl_keyboard(seat);
}
if (wl->tablet_manager) {
wl_add_tablet_seat(wl->tablet_manager, seat);
}
}
for (size_t i = 0; i < wl->requested_outputs; ++i) {
@ -125,47 +442,78 @@ static void backend_destroy(struct wlr_backend *backend) {
wlr_output_destroy(&output->wlr_output);
}
struct wlr_input_device *input_device, *tmp_input_device;
struct wlr_wl_input_device *input_device, *tmp_input_device;
wl_list_for_each_safe(input_device, tmp_input_device, &wl->devices, link) {
wlr_input_device_destroy(input_device);
wlr_input_device_destroy(&input_device->wlr_input_device);
}
wlr_signal_emit_safe(&wl->backend.events.destroy, &wl->backend);
struct wlr_wl_buffer *buffer, *tmp_buffer;
wl_list_for_each_safe(buffer, tmp_buffer, &wl->buffers, link) {
destroy_wl_buffer(buffer);
}
wlr_backend_finish(backend);
wl_list_remove(&wl->local_display_destroy.link);
free(wl->seat_name);
wl_event_source_remove(wl->remote_display_src);
wlr_renderer_destroy(wl->renderer);
wlr_egl_finish(&wl->egl);
close(wl->drm_fd);
if (wl->pointer) {
wl_pointer_destroy(wl->pointer);
wlr_drm_format_set_finish(&wl->shm_formats);
wlr_drm_format_set_finish(&wl->linux_dmabuf_v1_formats);
destroy_wl_seats(wl);
if (wl->zxdg_decoration_manager_v1) {
zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager_v1);
}
if (wl->seat) {
wl_seat_destroy(wl->seat);
if (wl->zwp_pointer_gestures_v1) {
zwp_pointer_gestures_v1_destroy(wl->zwp_pointer_gestures_v1);
}
if (wl->presentation) {
wp_presentation_destroy(wl->presentation);
}
if (wl->zwp_linux_dmabuf_v1) {
zwp_linux_dmabuf_v1_destroy(wl->zwp_linux_dmabuf_v1);
}
if (wl->shm) {
wl_shm_destroy(wl->shm);
}
if (wl->zwp_relative_pointer_manager_v1) {
zwp_relative_pointer_manager_v1_destroy(wl->zwp_relative_pointer_manager_v1);
}
free(wl->drm_render_name);
free(wl->activation_token);
xdg_wm_base_destroy(wl->xdg_wm_base);
wl_compositor_destroy(wl->compositor);
wl_registry_destroy(wl->registry);
wl_display_flush(wl->remote_display);
wl_display_disconnect(wl->remote_display);
free(wl);
}
static struct wlr_renderer *backend_get_renderer(struct wlr_backend *backend) {
static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) {
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
return wl->renderer;
return wl->presentation_clock;
}
static struct wlr_backend_impl backend_impl = {
static int backend_get_drm_fd(struct wlr_backend *backend) {
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
return wl->drm_fd;
}
static uint32_t get_buffer_caps(struct wlr_backend *backend) {
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
return (wl->zwp_linux_dmabuf_v1 ? WLR_BUFFER_CAP_DMABUF : 0)
| (wl->shm ? WLR_BUFFER_CAP_SHM : 0);
}
static const struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_renderer = backend_get_renderer,
.get_presentation_clock = backend_get_presentation_clock,
.get_drm_fd = backend_get_drm_fd,
.get_buffer_caps = get_buffer_caps,
};
bool wlr_backend_is_wl(struct wlr_backend *b) {
@ -179,7 +527,7 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
}
struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
const char *remote, wlr_renderer_create_func_t create_renderer_func) {
const char *remote) {
wlr_log(WLR_INFO, "Creating wayland backend");
struct wlr_wl_backend *wl = calloc(1, sizeof(*wl));
@ -193,6 +541,9 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
wl->local_display = display;
wl_list_init(&wl->devices);
wl_list_init(&wl->outputs);
wl_list_init(&wl->seats);
wl_list_init(&wl->buffers);
wl->presentation_clock = CLOCK_MONOTONIC;
wl->remote_display = wl_display_connect(remote);
if (!wl->remote_display) {
@ -205,10 +556,9 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
wlr_log_errno(WLR_ERROR, "Could not obtain reference to remote registry");
goto error_display;
}
wl_registry_add_listener(wl->registry, &registry_listener, wl);
wl_display_dispatch(wl->remote_display);
wl_display_roundtrip(wl->remote_display);
wl_display_roundtrip(wl->remote_display); // get globals
if (!wl->compositor) {
wlr_log(WLR_ERROR,
@ -221,10 +571,38 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
goto error_registry;
}
struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback_v1 = NULL;
struct wlr_wl_linux_dmabuf_feedback_v1 feedback_data = { .backend = wl };
if (wl->zwp_linux_dmabuf_v1 != NULL &&
zwp_linux_dmabuf_v1_get_version(wl->zwp_linux_dmabuf_v1) >=
ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) {
linux_dmabuf_feedback_v1 =
zwp_linux_dmabuf_v1_get_default_feedback(wl->zwp_linux_dmabuf_v1);
if (linux_dmabuf_feedback_v1 == NULL) {
wlr_log(WLR_ERROR, "Allocation failed");
goto error_registry;
}
zwp_linux_dmabuf_feedback_v1_add_listener(linux_dmabuf_feedback_v1,
&linux_dmabuf_feedback_v1_listener, &feedback_data);
if (wl->legacy_drm != NULL) {
wl_drm_destroy(wl->legacy_drm);
wl->legacy_drm = NULL;
}
}
wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats
if (feedback_data.format_table != NULL) {
munmap(feedback_data.format_table, feedback_data.format_table_size);
}
if (linux_dmabuf_feedback_v1 != NULL) {
zwp_linux_dmabuf_feedback_v1_destroy(linux_dmabuf_feedback_v1);
}
struct wl_event_loop *loop = wl_display_get_event_loop(wl->local_display);
int fd = wl_display_get_fd(wl->remote_display);
int events = WL_EVENT_READABLE | WL_EVENT_ERROR | WL_EVENT_HANGUP;
wl->remote_display_src = wl_event_loop_add_fd(loop, fd, events,
wl->remote_display_src = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
dispatch_events, wl);
if (!wl->remote_display_src) {
wlr_log(WLR_ERROR, "Failed to create event source");
@ -232,35 +610,33 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
}
wl_event_source_check(wl->remote_display_src);
static EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 1,
EGL_NONE,
};
if (!create_renderer_func) {
create_renderer_func = wlr_renderer_autocreate;
}
wl->renderer = create_renderer_func(&wl->egl, EGL_PLATFORM_WAYLAND_EXT,
wl->remote_display, config_attribs, WL_SHM_FORMAT_ARGB8888);
if (!wl->renderer) {
wlr_log(WLR_ERROR, "Could not create renderer");
goto error_event;
if (wl->drm_render_name != NULL) {
wlr_log(WLR_DEBUG, "Opening DRM render node %s", wl->drm_render_name);
wl->drm_fd = open(wl->drm_render_name, O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (wl->drm_fd < 0) {
wlr_log_errno(WLR_ERROR, "Failed to open DRM render node %s",
wl->drm_render_name);
goto error_remote_display_src;
}
} else {
wl->drm_fd = -1;
}
wl->local_display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &wl->local_display_destroy);
const char *token = getenv("XDG_ACTIVATION_TOKEN");
if (token != NULL) {
wl->activation_token = strdup(token);
unsetenv("XDG_ACTIVATION_TOKEN");
}
return &wl->backend;
error_event:
error_remote_display_src:
wl_event_source_remove(wl->remote_display_src);
error_registry:
free(wl->drm_render_name);
if (wl->compositor) {
wl_compositor_destroy(wl->compositor);
}
@ -271,6 +647,12 @@ error_registry:
error_display:
wl_display_disconnect(wl->remote_display);
error_wl:
wlr_backend_finish(&wl->backend);
free(wl);
return NULL;
}
struct wl_display *wlr_wl_backend_get_remote_display(struct wlr_backend *backend) {
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
return wl->remote_display;
}

View File

@ -0,0 +1,28 @@
wayland_client = dependency('wayland-client',
fallback: ['wayland', 'wayland_client_dep'],
default_options: wayland_project_options,
)
wlr_deps += wayland_client
wlr_files += files(
'backend.c',
'output.c',
'seat.c',
'tablet_v2.c',
)
client_protos = [
'drm',
'linux-dmabuf-unstable-v1',
'pointer-gestures-unstable-v1',
'presentation-time',
'relative-pointer-unstable-v1',
'tablet-unstable-v2',
'xdg-activation-v1',
'xdg-decoration-unstable-v1',
'xdg-shell',
]
foreach proto : client_protos
wlr_files += protocols_client_header[proto]
endforeach

View File

@ -7,6 +7,7 @@
#include <sys/types.h>
#include <unistd.h>
#include <drm_fourcc.h>
#include <wayland-client.h>
#include <wlr/interfaces/wlr_output.h>
@ -15,17 +16,28 @@
#include <wlr/util/log.h>
#include "backend/wayland.h"
#include "render/pixel_format.h"
#include "render/swapchain.h"
#include "render/wlr_renderer.h"
#include "util/signal.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "presentation-time-client-protocol.h"
#include "xdg-activation-v1-client-protocol.h"
#include "xdg-decoration-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
static const uint32_t SUPPORTED_OUTPUT_STATE =
WLR_OUTPUT_STATE_BACKEND_OPTIONAL |
WLR_OUTPUT_STATE_BUFFER |
WLR_OUTPUT_STATE_MODE;
static struct wlr_wl_output *get_wl_output_from_output(
struct wlr_output *wlr_output) {
assert(wlr_output_is_wl(wlr_output));
return (struct wlr_wl_output *)wlr_output;
}
static struct wl_callback_listener frame_listener;
static void surface_frame_callback(void *data, struct wl_callback *cb,
uint32_t time) {
struct wlr_wl_output *output = data;
@ -36,78 +48,319 @@ static void surface_frame_callback(void *data, struct wl_callback *cb,
wlr_output_send_frame(&output->wlr_output);
}
static struct wl_callback_listener frame_listener = {
static const struct wl_callback_listener frame_listener = {
.done = surface_frame_callback
};
static void presentation_feedback_destroy(
struct wlr_wl_presentation_feedback *feedback) {
wl_list_remove(&feedback->link);
wp_presentation_feedback_destroy(feedback->feedback);
free(feedback);
}
static void presentation_feedback_handle_sync_output(void *data,
struct wp_presentation_feedback *feedback, struct wl_output *output) {
// This space is intentionally left blank
}
static void presentation_feedback_handle_presented(void *data,
struct wp_presentation_feedback *wp_feedback, uint32_t tv_sec_hi,
uint32_t tv_sec_lo, uint32_t tv_nsec, uint32_t refresh_ns,
uint32_t seq_hi, uint32_t seq_lo, uint32_t flags) {
struct wlr_wl_presentation_feedback *feedback = data;
struct timespec t = {
.tv_sec = ((uint64_t)tv_sec_hi << 32) | tv_sec_lo,
.tv_nsec = tv_nsec,
};
struct wlr_output_event_present event = {
.commit_seq = feedback->commit_seq,
.presented = true,
.when = &t,
.seq = ((uint64_t)seq_hi << 32) | seq_lo,
.refresh = refresh_ns,
.flags = flags,
};
wlr_output_send_present(&feedback->output->wlr_output, &event);
presentation_feedback_destroy(feedback);
}
static void presentation_feedback_handle_discarded(void *data,
struct wp_presentation_feedback *wp_feedback) {
struct wlr_wl_presentation_feedback *feedback = data;
struct wlr_output_event_present event = {
.commit_seq = feedback->commit_seq,
.presented = false,
};
wlr_output_send_present(&feedback->output->wlr_output, &event);
presentation_feedback_destroy(feedback);
}
static const struct wp_presentation_feedback_listener
presentation_feedback_listener = {
.sync_output = presentation_feedback_handle_sync_output,
.presented = presentation_feedback_handle_presented,
.discarded = presentation_feedback_handle_discarded,
};
static bool output_set_custom_mode(struct wlr_output *wlr_output,
int32_t width, int32_t height, int32_t refresh) {
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
wlr_egl_swap_buffers(&output->backend->egl, output->egl_surface, NULL);
wl_egl_window_resize(output->egl_window, width, height, 0, 0);
wlr_output_update_custom_mode(&output->wlr_output, width, height, 0);
wlr_output_update_custom_mode(wlr_output, width, height, 0);
return true;
}
void destroy_wl_buffer(struct wlr_wl_buffer *buffer) {
if (buffer == NULL) {
return;
}
wl_list_remove(&buffer->buffer_destroy.link);
wl_list_remove(&buffer->link);
wl_buffer_destroy(buffer->wl_buffer);
free(buffer);
}
static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) {
struct wlr_wl_buffer *buffer = data;
buffer->released = true;
wlr_buffer_unlock(buffer->buffer); // might free buffer
}
static const struct wl_buffer_listener buffer_listener = {
.release = buffer_handle_release,
};
static void buffer_handle_buffer_destroy(struct wl_listener *listener,
void *data) {
struct wlr_wl_buffer *buffer =
wl_container_of(listener, buffer, buffer_destroy);
destroy_wl_buffer(buffer);
}
static bool test_buffer(struct wlr_wl_backend *wl,
struct wlr_buffer *wlr_buffer) {
struct wlr_dmabuf_attributes dmabuf;
struct wlr_shm_attributes shm;
if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) {
return wlr_drm_format_set_has(&wl->linux_dmabuf_v1_formats,
dmabuf.format, dmabuf.modifier);
} else if (wlr_buffer_get_shm(wlr_buffer, &shm)) {
return wlr_drm_format_set_has(&wl->shm_formats, shm.format,
DRM_FORMAT_MOD_INVALID);
} else {
return false;
}
}
static struct wl_buffer *import_dmabuf(struct wlr_wl_backend *wl,
struct wlr_dmabuf_attributes *dmabuf) {
uint32_t modifier_hi = dmabuf->modifier >> 32;
uint32_t modifier_lo = (uint32_t)dmabuf->modifier;
struct zwp_linux_buffer_params_v1 *params =
zwp_linux_dmabuf_v1_create_params(wl->zwp_linux_dmabuf_v1);
for (int i = 0; i < dmabuf->n_planes; i++) {
zwp_linux_buffer_params_v1_add(params, dmabuf->fd[i], i,
dmabuf->offset[i], dmabuf->stride[i], modifier_hi, modifier_lo);
}
struct wl_buffer *wl_buffer = zwp_linux_buffer_params_v1_create_immed(
params, dmabuf->width, dmabuf->height, dmabuf->format, 0);
// TODO: handle create() errors
return wl_buffer;
}
static struct wl_buffer *import_shm(struct wlr_wl_backend *wl,
struct wlr_shm_attributes *shm) {
enum wl_shm_format wl_shm_format = convert_drm_format_to_wl_shm(shm->format);
uint32_t size = shm->stride * shm->height;
struct wl_shm_pool *pool = wl_shm_create_pool(wl->shm, shm->fd, size);
if (pool == NULL) {
return NULL;
}
struct wl_buffer *wl_buffer = wl_shm_pool_create_buffer(pool, shm->offset,
shm->width, shm->height, shm->stride, wl_shm_format);
wl_shm_pool_destroy(pool);
return wl_buffer;
}
static struct wlr_wl_buffer *create_wl_buffer(struct wlr_wl_backend *wl,
struct wlr_buffer *wlr_buffer) {
if (!test_buffer(wl, wlr_buffer)) {
return NULL;
}
struct wlr_dmabuf_attributes dmabuf;
struct wlr_shm_attributes shm;
struct wl_buffer *wl_buffer;
if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) {
wl_buffer = import_dmabuf(wl, &dmabuf);
} else if (wlr_buffer_get_shm(wlr_buffer, &shm)) {
wl_buffer = import_shm(wl, &shm);
} else {
return NULL;
}
if (wl_buffer == NULL) {
return NULL;
}
struct wlr_wl_buffer *buffer = calloc(1, sizeof(struct wlr_wl_buffer));
if (buffer == NULL) {
wl_buffer_destroy(wl_buffer);
return NULL;
}
buffer->wl_buffer = wl_buffer;
buffer->buffer = wlr_buffer_lock(wlr_buffer);
wl_list_insert(&wl->buffers, &buffer->link);
wl_buffer_add_listener(wl_buffer, &buffer_listener, buffer);
buffer->buffer_destroy.notify = buffer_handle_buffer_destroy;
wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy);
return buffer;
}
static struct wlr_wl_buffer *get_or_create_wl_buffer(struct wlr_wl_backend *wl,
struct wlr_buffer *wlr_buffer) {
struct wlr_wl_buffer *buffer;
wl_list_for_each(buffer, &wl->buffers, link) {
// We can only re-use a wlr_wl_buffer if the parent compositor has
// released it, because wl_buffer.release is per-wl_buffer, not per
// wl_surface.commit.
if (buffer->buffer == wlr_buffer && buffer->released) {
buffer->released = false;
wlr_buffer_lock(buffer->buffer);
return buffer;
}
}
return create_wl_buffer(wl, wlr_buffer);
}
static bool output_test(struct wlr_output *wlr_output) {
struct wlr_wl_output *output =
get_wl_output_from_output(wlr_output);
uint32_t unsupported =
wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE;
if (unsupported != 0) {
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
unsupported);
return false;
}
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM);
}
if ((wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) &&
!test_buffer(output->backend, wlr_output->pending.buffer)) {
return false;
}
return true;
}
static bool output_make_current(struct wlr_output *wlr_output,
int *buffer_age) {
struct wlr_wl_output *output =
get_wl_output_from_output(wlr_output);
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
buffer_age);
}
static bool output_swap_buffers(struct wlr_output *wlr_output,
pixman_region32_t *damage) {
static bool output_commit(struct wlr_output *wlr_output) {
struct wlr_wl_output *output =
get_wl_output_from_output(wlr_output);
if (output->frame_callback != NULL) {
wlr_log(WLR_ERROR, "Skipping buffer swap");
if (!output_test(wlr_output)) {
return false;
}
output->frame_callback = wl_surface_frame(output->surface);
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
if (!wlr_egl_swap_buffers(&output->backend->egl,
output->egl_surface, damage)) {
return false;
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
if (!output_set_custom_mode(wlr_output,
wlr_output->pending.custom_mode.width,
wlr_output->pending.custom_mode.height,
wlr_output->pending.custom_mode.refresh)) {
return false;
}
}
// TODO: if available, use the presentation-time protocol
wlr_output_send_present(wlr_output, NULL);
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
struct wp_presentation_feedback *wp_feedback = NULL;
if (output->backend->presentation != NULL) {
wp_feedback = wp_presentation_feedback(output->backend->presentation,
output->surface);
}
pixman_region32_t *damage = NULL;
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
damage = &wlr_output->pending.damage;
}
if (output->frame_callback != NULL) {
wlr_log(WLR_ERROR, "Skipping buffer swap");
return false;
}
output->frame_callback = wl_surface_frame(output->surface);
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
struct wlr_buffer *wlr_buffer = wlr_output->pending.buffer;
struct wlr_wl_buffer *buffer =
get_or_create_wl_buffer(output->backend, wlr_buffer);
if (buffer == NULL) {
return false;
}
wl_surface_attach(output->surface, buffer->wl_buffer, 0, 0);
if (damage == NULL) {
wl_surface_damage_buffer(output->surface,
0, 0, INT32_MAX, INT32_MAX);
} else {
int rects_len;
pixman_box32_t *rects =
pixman_region32_rectangles(damage, &rects_len);
for (int i = 0; i < rects_len; i++) {
pixman_box32_t *r = &rects[i];
wl_surface_damage_buffer(output->surface, r->x1, r->y1,
r->x2 - r->x1, r->y2 - r->y1);
}
}
wl_surface_commit(output->surface);
if (wp_feedback != NULL) {
struct wlr_wl_presentation_feedback *feedback =
calloc(1, sizeof(*feedback));
if (feedback == NULL) {
wp_presentation_feedback_destroy(wp_feedback);
return false;
}
feedback->output = output;
feedback->feedback = wp_feedback;
feedback->commit_seq = output->wlr_output.commit_seq + 1;
wl_list_insert(&output->presentation_feedbacks, &feedback->link);
wp_presentation_feedback_add_listener(wp_feedback,
&presentation_feedback_listener, feedback);
} else {
struct wlr_output_event_present present_event = {
.commit_seq = wlr_output->commit_seq + 1,
.presented = true,
};
wlr_output_send_present(wlr_output, &present_event);
}
}
wl_display_flush(output->backend->remote_display);
return true;
}
static void output_transform(struct wlr_output *wlr_output,
enum wl_output_transform transform) {
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
output->wlr_output.transform = transform;
}
static bool output_set_cursor(struct wlr_output *wlr_output,
struct wlr_texture *texture, int32_t scale,
enum wl_output_transform transform,
int32_t hotspot_x, int32_t hotspot_y, bool update_texture) {
struct wlr_buffer *wlr_buffer, int hotspot_x, int hotspot_y) {
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
struct wlr_wl_backend *backend = output->backend;
struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y };
wlr_box_transform(&hotspot, &hotspot,
wlr_output_transform_invert(wlr_output->transform),
output->cursor.width, output->cursor.height);
// TODO: use output->wlr_output.transform to transform pixels and hotpot
output->cursor.hotspot_x = hotspot.x;
output->cursor.hotspot_y = hotspot.y;
if (!update_texture) {
// Update hotspot without changing cursor image
update_wl_output_cursor(output);
return true;
}
output->cursor.hotspot_x = hotspot_x;
output->cursor.hotspot_y = hotspot_y;
if (output->cursor.surface == NULL) {
output->cursor.surface =
@ -115,53 +368,37 @@ static bool output_set_cursor(struct wlr_output *wlr_output,
}
struct wl_surface *surface = output->cursor.surface;
if (texture != NULL) {
int width, height;
wlr_texture_get_size(texture, &width, &height);
width = width * wlr_output->scale / scale;
height = height * wlr_output->scale / scale;
output->cursor.width = width;
output->cursor.height = height;
if (output->cursor.egl_window == NULL) {
output->cursor.egl_window =
wl_egl_window_create(surface, width, height);
if (wlr_buffer != NULL) {
struct wlr_wl_buffer *buffer =
get_or_create_wl_buffer(output->backend, wlr_buffer);
if (buffer == NULL) {
return false;
}
wl_egl_window_resize(output->cursor.egl_window, width, height, 0, 0);
EGLSurface egl_surface =
wlr_egl_create_surface(&backend->egl, output->cursor.egl_window);
wlr_egl_make_current(&backend->egl, egl_surface, NULL);
struct wlr_box cursor_box = {
.width = width,
.height = height,
};
float projection[9];
wlr_matrix_projection(projection, width, height, wlr_output->transform);
float matrix[9];
wlr_matrix_project_box(matrix, &cursor_box, transform, 0, projection);
wlr_renderer_begin(backend->renderer, width, height);
wlr_renderer_clear(backend->renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
wlr_render_texture_with_matrix(backend->renderer, texture, matrix, 1.0);
wlr_renderer_end(backend->renderer);
wlr_egl_swap_buffers(&backend->egl, egl_surface, NULL);
wlr_egl_destroy_surface(&backend->egl, egl_surface);
wl_surface_attach(surface, buffer->wl_buffer, 0, 0);
wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_commit(surface);
} else {
wl_surface_attach(surface, NULL, 0, 0);
wl_surface_commit(surface);
}
update_wl_output_cursor(output);
wl_display_flush(backend->remote_display);
return true;
}
static const struct wlr_drm_format_set *output_get_formats(
struct wlr_output *wlr_output, uint32_t buffer_caps) {
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
if (buffer_caps & WLR_BUFFER_CAP_DMABUF) {
return &output->backend->linux_dmabuf_v1_formats;
} else if (buffer_caps & WLR_BUFFER_CAP_SHM) {
return &output->backend->shm_formats;
}
return NULL;
}
static void output_destroy(struct wlr_output *wlr_output) {
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
if (output == NULL) {
@ -170,9 +407,6 @@ static void output_destroy(struct wlr_output *wlr_output) {
wl_list_remove(&output->link);
if (output->cursor.egl_window != NULL) {
wl_egl_window_destroy(output->cursor.egl_window);
}
if (output->cursor.surface) {
wl_surface_destroy(output->cursor.surface);
}
@ -181,17 +415,28 @@ static void output_destroy(struct wlr_output *wlr_output) {
wl_callback_destroy(output->frame_callback);
}
wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface);
wl_egl_window_destroy(output->egl_window);
struct wlr_wl_presentation_feedback *feedback, *feedback_tmp;
wl_list_for_each_safe(feedback, feedback_tmp,
&output->presentation_feedbacks, link) {
presentation_feedback_destroy(feedback);
}
if (output->zxdg_toplevel_decoration_v1) {
zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1);
}
xdg_toplevel_destroy(output->xdg_toplevel);
xdg_surface_destroy(output->xdg_surface);
wl_surface_destroy(output->surface);
wl_display_flush(output->backend->remote_display);
free(output);
}
void update_wl_output_cursor(struct wlr_wl_output *output) {
if (output->backend->pointer && output->enter_serial) {
wl_pointer_set_cursor(output->backend->pointer, output->enter_serial,
struct wlr_wl_pointer *pointer = output->cursor.pointer;
if (pointer) {
assert(pointer->output == output);
assert(output->enter_serial);
wl_pointer_set_cursor(pointer->wl_pointer, output->enter_serial,
output->cursor.surface, output->cursor.hotspot_x,
output->cursor.hotspot_y);
}
@ -202,29 +447,14 @@ static bool output_move_cursor(struct wlr_output *_output, int x, int y) {
return true;
}
static bool output_schedule_frame(struct wlr_output *wlr_output) {
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
if (output->frame_callback != NULL) {
wlr_log(WLR_ERROR, "Skipping frame scheduling");
return true;
}
output->frame_callback = wl_surface_frame(output->surface);
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
wl_surface_commit(output->surface);
return true;
}
static const struct wlr_output_impl output_impl = {
.set_custom_mode = output_set_custom_mode,
.transform = output_transform,
.destroy = output_destroy,
.make_current = output_make_current,
.swap_buffers = output_swap_buffers,
.test = output_test,
.commit = output_commit,
.set_cursor = output_set_cursor,
.move_cursor = output_move_cursor,
.schedule_frame = output_schedule_frame,
.get_cursor_formats = output_get_formats,
.get_primary_formats = output_get_formats,
};
bool wlr_output_is_wl(struct wlr_output *wlr_output) {
@ -241,7 +471,7 @@ static void xdg_surface_handle_configure(void *data,
// nothing else?
}
static struct xdg_surface_listener xdg_surface_listener = {
static const struct xdg_surface_listener xdg_surface_listener = {
.configure = xdg_surface_handle_configure,
};
@ -263,10 +493,10 @@ static void xdg_toplevel_handle_close(void *data,
struct wlr_wl_output *output = data;
assert(output && output->xdg_toplevel == xdg_toplevel);
wlr_output_destroy((struct wlr_output *)output);
wlr_output_destroy(&output->wlr_output);
}
static struct xdg_toplevel_listener xdg_toplevel_listener = {
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
.configure = xdg_toplevel_handle_configure,
.close = xdg_toplevel_handle_close,
};
@ -290,10 +520,18 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) {
wlr_output_update_custom_mode(wlr_output, 1280, 720, 0);
strncpy(wlr_output->make, "wayland", sizeof(wlr_output->make));
strncpy(wlr_output->model, "wayland", sizeof(wlr_output->model));
snprintf(wlr_output->name, sizeof(wlr_output->name), "WL-%d",
wl_list_length(&backend->outputs) + 1);
char name[64];
snprintf(name, sizeof(name), "WL-%zu", ++backend->last_output_num);
wlr_output_set_name(wlr_output, name);
char description[128];
snprintf(description, sizeof(description),
"Wayland output %zu", backend->last_output_num);
wlr_output_set_description(wlr_output, description);
output->backend = backend;
wl_list_init(&output->presentation_feedbacks);
output->surface = wl_compositor_create_surface(backend->compositor);
if (!output->surface) {
@ -314,6 +552,18 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) {
goto error;
}
if (backend->zxdg_decoration_manager_v1) {
output->zxdg_toplevel_decoration_v1 =
zxdg_decoration_manager_v1_get_toplevel_decoration(
backend->zxdg_decoration_manager_v1, output->xdg_toplevel);
if (!output->zxdg_toplevel_decoration_v1) {
wlr_log_errno(WLR_ERROR, "Could not get xdg toplevel decoration");
goto error;
}
zxdg_toplevel_decoration_v1_set_mode(output->zxdg_toplevel_decoration_v1,
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
}
wlr_wl_output_set_title(wlr_output, NULL);
xdg_toplevel_set_app_id(output->xdg_toplevel, "wlroots");
@ -323,40 +573,29 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) {
&xdg_toplevel_listener, output);
wl_surface_commit(output->surface);
output->egl_window = wl_egl_window_create(output->surface,
wlr_output->width, wlr_output->height);
output->egl_surface = wlr_egl_create_surface(&backend->egl,
output->egl_window);
wl_display_roundtrip(output->backend->remote_display);
// start rendering loop per callbacks by rendering first frame
if (!wlr_egl_make_current(&output->backend->egl, output->egl_surface,
NULL)) {
goto error;
}
wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 });
wlr_renderer_end(backend->renderer);
output->frame_callback = wl_surface_frame(output->surface);
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
if (!wlr_egl_swap_buffers(&output->backend->egl, output->egl_surface,
NULL)) {
goto error;
}
wl_list_insert(&backend->outputs, &output->link);
wlr_output_update_enabled(wlr_output, true);
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
if (backend->pointer != NULL) {
create_wl_pointer(backend->pointer, output);
struct wlr_wl_seat *seat;
wl_list_for_each(seat, &backend->seats, link) {
if (seat->pointer) {
create_wl_pointer(seat, output);
}
}
// TODO: let the compositor do this bit
if (backend->activation_v1 && backend->activation_token) {
xdg_activation_v1_activate(backend->activation_v1,
backend->activation_token, output->surface);
}
// Start the rendering loop by requesting the compositor to render a frame
wlr_output_schedule_frame(wlr_output);
return wlr_output;
error:
@ -376,4 +615,10 @@ void wlr_wl_output_set_title(struct wlr_output *output, const char *title) {
}
xdg_toplevel_set_title(wl_output->xdg_toplevel, title);
wl_display_flush(wl_output->backend->remote_display);
}
struct wl_surface *wlr_wl_output_get_surface(struct wlr_output *output) {
struct wlr_wl_output *wl_output = get_wl_output_from_output(output);
return wl_output->surface;
}

935
backend/wayland/seat.c Normal file
View File

@ -0,0 +1,935 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/interfaces/wlr_pointer.h>
#include <wlr/interfaces/wlr_touch.h>
#include <wlr/util/log.h>
#include "pointer-gestures-unstable-v1-client-protocol.h"
#include "relative-pointer-unstable-v1-client-protocol.h"
#include "backend/wayland.h"
#include "util/signal.h"
#include "util/time.h"
static struct wlr_wl_pointer *output_get_pointer(
struct wlr_wl_output *output,
const struct wl_pointer *wl_pointer) {
struct wlr_wl_input_device *dev;
wl_list_for_each(dev, &output->backend->devices, link) {
if (dev->wlr_input_device.type != WLR_INPUT_DEVICE_POINTER) {
continue;
}
struct wlr_wl_pointer *pointer =
pointer_get_wl(dev->wlr_input_device.pointer);
if (pointer->output == output && pointer->wl_pointer == wl_pointer) {
return pointer;
}
}
return NULL;
}
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface, wl_fixed_t sx,
wl_fixed_t sy) {
struct wlr_wl_seat *seat = data;
if (surface == NULL) {
return;
}
struct wlr_wl_output *output = wl_surface_get_user_data(surface);
assert(output);
struct wlr_wl_pointer *pointer = output_get_pointer(output, wl_pointer);
seat->active_pointer = pointer;
// Manage cursor icon/rendering on output
struct wlr_wl_pointer *current_pointer = output->cursor.pointer;
if (current_pointer && current_pointer != pointer) {
wlr_log(WLR_INFO, "Ignoring seat %s pointer cursor in favor of seat %s",
seat->name, current_pointer->input_device->seat->name);
return;
}
output->enter_serial = serial;
output->cursor.pointer = pointer;
update_wl_output_cursor(output);
}
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface) {
struct wlr_wl_seat *seat = data;
if (surface == NULL) {
return;
}
struct wlr_wl_output *output = wl_surface_get_user_data(surface);
assert(output);
if (seat->active_pointer != NULL &&
seat->active_pointer->output == output) {
seat->active_pointer = NULL;
}
if (output->cursor.pointer == seat->active_pointer) {
output->enter_serial = 0;
output->cursor.pointer = NULL;
}
}
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
uint32_t time, wl_fixed_t sx, wl_fixed_t sy) {
struct wlr_wl_seat *seat = data;
struct wlr_wl_pointer *pointer = seat->active_pointer;
if (pointer == NULL) {
return;
}
struct wlr_output *wlr_output = &pointer->output->wlr_output;
struct wlr_event_pointer_motion_absolute event = {
.device = &pointer->input_device->wlr_input_device,
.time_msec = time,
.x = wl_fixed_to_double(sx) / wlr_output->width,
.y = wl_fixed_to_double(sy) / wlr_output->height,
};
wlr_signal_emit_safe(&pointer->wlr_pointer.events.motion_absolute, &event);
}
static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
struct wlr_wl_seat *seat = data;
struct wlr_wl_pointer *pointer = seat->active_pointer;
if (pointer == NULL) {
return;
}
struct wlr_event_pointer_button event = {
.device = &pointer->input_device->wlr_input_device,
.button = button,
.state = state,
.time_msec = time,
};
wlr_signal_emit_safe(&pointer->wlr_pointer.events.button, &event);
}
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis, wl_fixed_t value) {
struct wlr_wl_seat *seat = data;
struct wlr_wl_pointer *pointer = seat->active_pointer;
if (pointer == NULL) {
return;
}
struct wlr_event_pointer_axis event = {
.device = &pointer->input_device->wlr_input_device,
.delta = wl_fixed_to_double(value),
.delta_discrete = pointer->axis_discrete,
.orientation = axis,
.time_msec = time,
.source = pointer->axis_source,
};
wlr_signal_emit_safe(&pointer->wlr_pointer.events.axis, &event);
pointer->axis_discrete = 0;
}
static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
struct wlr_wl_seat *seat = data;
struct wlr_wl_pointer *pointer = seat->active_pointer;
if (pointer == NULL) {
return;
}
wlr_signal_emit_safe(&pointer->wlr_pointer.events.frame,
&pointer->wlr_pointer);
}
static void pointer_handle_axis_source(void *data,
struct wl_pointer *wl_pointer, uint32_t axis_source) {
struct wlr_wl_seat *seat = data;
struct wlr_wl_pointer *pointer = seat->active_pointer;
if (pointer == NULL) {
return;
}
pointer->axis_source = axis_source;
}
static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis) {
struct wlr_wl_seat *seat = data;
struct wlr_wl_pointer *pointer = seat->active_pointer;
if (pointer == NULL) {
return;
}
struct wlr_event_pointer_axis event = {
.device = &pointer->input_device->wlr_input_device,
.delta = 0,
.delta_discrete = 0,
.orientation = axis,
.time_msec = time,
.source = pointer->axis_source,
};
wlr_signal_emit_safe(&pointer->wlr_pointer.events.axis, &event);
}
static void pointer_handle_axis_discrete(void *data,
struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
struct wlr_wl_seat *seat = data;
struct wlr_wl_pointer *pointer = seat->active_pointer;
if (pointer == NULL) {
return;
}
pointer->axis_discrete = discrete;
}
static const struct wl_pointer_listener pointer_listener = {
.enter = pointer_handle_enter,
.leave = pointer_handle_leave,
.motion = pointer_handle_motion,
.button = pointer_handle_button,
.axis = pointer_handle_axis,
.frame = pointer_handle_frame,
.axis_source = pointer_handle_axis_source,
.axis_stop = pointer_handle_axis_stop,
.axis_discrete = pointer_handle_axis_discrete,
};
static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
uint32_t format, int32_t fd, uint32_t size) {
close(fd);
}
static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
struct wlr_input_device *dev = data;
uint32_t time = get_current_time_msec();
uint32_t *keycode_ptr;
wl_array_for_each(keycode_ptr, keys) {
struct wlr_event_keyboard_key event = {
.keycode = *keycode_ptr,
.state = WL_KEYBOARD_KEY_STATE_PRESSED,
.time_msec = time,
.update_state = false,
};
wlr_keyboard_notify_key(dev->keyboard, &event);
}
}
static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface) {
struct wlr_input_device *dev = data;
uint32_t time = get_current_time_msec();
size_t num_keycodes = dev->keyboard->num_keycodes;
uint32_t pressed[num_keycodes + 1];
memcpy(pressed, dev->keyboard->keycodes,
num_keycodes * sizeof(uint32_t));
for (size_t i = 0; i < num_keycodes; ++i) {
uint32_t keycode = pressed[i];
struct wlr_event_keyboard_key event = {
.keycode = keycode,
.state = WL_KEYBOARD_KEY_STATE_RELEASED,
.time_msec = time,
.update_state = false,
};
wlr_keyboard_notify_key(dev->keyboard, &event);
}
}
static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
struct wlr_input_device *dev = data;
assert(dev && dev->keyboard);
struct wlr_event_keyboard_key wlr_event = {
.keycode = key,
.state = state,
.time_msec = time,
.update_state = false,
};
wlr_keyboard_notify_key(dev->keyboard, &wlr_event);
}
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group) {
struct wlr_input_device *dev = data;
assert(dev && dev->keyboard);
wlr_keyboard_notify_modifiers(dev->keyboard, mods_depressed, mods_latched,
mods_locked, group);
}
static void keyboard_handle_repeat_info(void *data,
struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) {
// This space is intentionally left blank
}
static const struct wl_keyboard_listener keyboard_listener = {
.keymap = keyboard_handle_keymap,
.enter = keyboard_handle_enter,
.leave = keyboard_handle_leave,
.key = keyboard_handle_key,
.modifiers = keyboard_handle_modifiers,
.repeat_info = keyboard_handle_repeat_info
};
static void touch_coordinates_to_absolute(struct wlr_wl_input_device *device,
wl_fixed_t x, wl_fixed_t y, double *sx, double *sy) {
// TODO: each output needs its own touch
struct wlr_wl_output *output, *tmp;
wl_list_for_each_safe(output, tmp, &device->backend->outputs, link) {
*sx = wl_fixed_to_double(x) / output->wlr_output.width;
*sy = wl_fixed_to_double(y) / output->wlr_output.height;
return; // Choose the first output in the list
}
*sx = *sy = 0;
}
static void touch_handle_down(void *data, struct wl_touch *wl_touch,
uint32_t serial, uint32_t time, struct wl_surface *surface,
int32_t id, wl_fixed_t x, wl_fixed_t y) {
struct wlr_wl_input_device *device = data;
assert(device && device->wlr_input_device.touch);
double sx, sy;
touch_coordinates_to_absolute(device, x, y, &sx, &sy);
struct wlr_event_touch_down event = {
.device = &device->wlr_input_device,
.time_msec = time,
.touch_id = id,
.x = sx,
.y = sy
};
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.down, &event);
}
static void touch_handle_up(void *data, struct wl_touch *wl_touch,
uint32_t serial, uint32_t time, int32_t id) {
struct wlr_wl_input_device *device = data;
assert(device && device->wlr_input_device.touch);
struct wlr_event_touch_up event = {
.device = &device->wlr_input_device,
.time_msec = time,
.touch_id = id,
};
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.up, &event);
}
static void touch_handle_motion(void *data, struct wl_touch *wl_touch,
uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) {
struct wlr_wl_input_device *device = data;
assert(device && device->wlr_input_device.touch);
double sx, sy;
touch_coordinates_to_absolute(device, x, y, &sx, &sy);
struct wlr_event_touch_motion event = {
.device = &device->wlr_input_device,
.time_msec = time,
.touch_id = id,
.x = sx,
.y = sy
};
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.motion, &event);
}
static void touch_handle_frame(void *data, struct wl_touch *wl_touch) {
struct wlr_wl_input_device *device = data;
assert(device && device->wlr_input_device.touch);
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.frame, NULL);
}
static void touch_handle_cancel(void *data, struct wl_touch *wl_touch) {
// no-op
}
static void touch_handle_shape(void *data, struct wl_touch *wl_touch,
int32_t id, wl_fixed_t major, wl_fixed_t minor) {
// no-op
}
static void touch_handle_orientation(void *data, struct wl_touch *wl_touch,
int32_t id, wl_fixed_t orientation) {
// no-op
}
static const struct wl_touch_listener touch_listener = {
.down = touch_handle_down,
.up = touch_handle_up,
.motion = touch_handle_motion,
.frame = touch_handle_frame,
.cancel = touch_handle_cancel,
.shape = touch_handle_shape,
.orientation = touch_handle_orientation,
};
static struct wlr_wl_input_device *get_wl_input_device_from_input_device(
struct wlr_input_device *wlr_dev) {
assert(wlr_input_device_is_wl(wlr_dev));
return (struct wlr_wl_input_device *)wlr_dev;
}
bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl) {
struct wlr_wl_seat *seat = calloc(1, sizeof(struct wlr_wl_seat));
if (!seat) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return false;
}
seat->wl_seat = wl_seat;
seat->backend = wl;
wl_list_insert(&wl->seats, &seat->link);
wl_seat_add_listener(wl_seat, &seat_listener, seat);
return true;
}
void destroy_wl_seats(struct wlr_wl_backend *wl) {
struct wlr_wl_seat *seat, *tmp_seat;
wl_list_for_each_safe(seat, tmp_seat, &wl->seats, link) {
if (seat->touch) {
wl_touch_destroy(seat->touch);
}
if (seat->pointer) {
wl_pointer_destroy(seat->pointer);
}
if (seat->keyboard && !wl->started) {
// early termination will not be handled by input_device_destroy
wl_keyboard_destroy(seat->keyboard);
}
free(seat->name);
assert(seat->wl_seat);
wl_seat_destroy(seat->wl_seat);
wl_list_remove(&seat->link);
free(seat);
}
}
static struct wlr_wl_seat *input_device_get_seat(struct wlr_input_device *wlr_dev) {
struct wlr_wl_input_device *dev =
get_wl_input_device_from_input_device(wlr_dev);
assert(dev->seat);
return dev->seat;
}
static void input_device_destroy(struct wlr_input_device *wlr_dev) {
struct wlr_wl_input_device *dev =
get_wl_input_device_from_input_device(wlr_dev);
if (dev->wlr_input_device.type == WLR_INPUT_DEVICE_KEYBOARD) {
struct wlr_wl_seat *seat = input_device_get_seat(wlr_dev);
wl_keyboard_release(seat->keyboard);
seat->keyboard = NULL;
}
// We can't destroy pointer here because we might have multiple devices
// exposing it to compositor.
wl_list_remove(&dev->link);
free(dev);
}
static const struct wlr_input_device_impl input_device_impl = {
.destroy = input_device_destroy,
};
bool wlr_input_device_is_wl(struct wlr_input_device *dev) {
return dev->impl == &input_device_impl;
}
struct wlr_wl_input_device *create_wl_input_device(
struct wlr_wl_seat *seat, enum wlr_input_device_type type) {
struct wlr_wl_input_device *dev =
calloc(1, sizeof(struct wlr_wl_input_device));
if (dev == NULL) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return NULL;
}
dev->backend = seat->backend;
dev->seat = seat;
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
unsigned int vendor = 0, product = 0;
const char *type_name = "unknown";
switch (type) {
case WLR_INPUT_DEVICE_KEYBOARD:
type_name = "keyboard";
break;
case WLR_INPUT_DEVICE_POINTER:
type_name = "pointer";
break;
case WLR_INPUT_DEVICE_TOUCH:
type_name = "touch";
break;
case WLR_INPUT_DEVICE_TABLET_TOOL:
type_name = "tablet-tool";
break;
case WLR_INPUT_DEVICE_TABLET_PAD:
type_name = "tablet-pad";
break;
case WLR_INPUT_DEVICE_SWITCH:
type_name = "switch";
break;
}
size_t name_size = 8 + strlen(type_name) + strlen(seat->name) + 1;
char name[name_size];
(void) snprintf(name, name_size, "wayland-%s-%s", type_name, seat->name);
wlr_input_device_init(wlr_dev, type, &input_device_impl, name, vendor,
product);
wl_list_insert(&seat->backend->devices, &dev->link);
return dev;
}
static const struct wlr_pointer_impl pointer_impl;
struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer) {
assert(wlr_pointer->impl == &pointer_impl);
return (struct wlr_wl_pointer *)wlr_pointer;
}
static void pointer_destroy(struct wlr_pointer *wlr_pointer) {
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_pointer);
if (pointer->output->cursor.pointer == pointer) {
pointer->output->cursor.pointer = NULL;
}
struct wlr_wl_seat *seat = pointer->input_device->seat;
if (seat->active_pointer == pointer) {
seat->active_pointer = NULL;
}
// pointer->wl_pointer belongs to the wlr_wl_seat
if (pointer->gesture_swipe != NULL) {
zwp_pointer_gesture_swipe_v1_destroy(pointer->gesture_swipe);
}
if (pointer->gesture_pinch != NULL) {
zwp_pointer_gesture_pinch_v1_destroy(pointer->gesture_pinch);
}
if (pointer->gesture_hold != NULL) {
zwp_pointer_gesture_hold_v1_destroy(pointer->gesture_hold);
}
if (pointer->relative_pointer != NULL) {
zwp_relative_pointer_v1_destroy(pointer->relative_pointer);
}
wl_list_remove(&pointer->output_destroy.link);
free(pointer);
}
static const struct wlr_pointer_impl pointer_impl = {
.destroy = pointer_destroy,
};
static void gesture_swipe_begin(void *data,
struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1,
uint32_t serial, uint32_t time,
struct wl_surface *surface, uint32_t fingers) {
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
struct wlr_event_pointer_swipe_begin wlr_event = {
.device = wlr_dev,
.time_msec = time,
.fingers = fingers,
};
input_device->fingers = fingers;
wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_begin, &wlr_event);
}
static void gesture_swipe_update(void *data,
struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1,
uint32_t time, wl_fixed_t dx, wl_fixed_t dy) {
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
struct wlr_event_pointer_swipe_update wlr_event = {
.device = wlr_dev,
.time_msec = time,
.fingers = input_device->fingers,
.dx = wl_fixed_to_double(dx),
.dy = wl_fixed_to_double(dy),
};
wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_update, &wlr_event);
}
static void gesture_swipe_end(void *data,
struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1,
uint32_t serial, uint32_t time, int32_t cancelled) {
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
struct wlr_event_pointer_swipe_end wlr_event = {
.device = wlr_dev,
.time_msec = time,
.cancelled = cancelled,
};
wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_end, &wlr_event);
}
static const struct zwp_pointer_gesture_swipe_v1_listener gesture_swipe_impl = {
.begin = gesture_swipe_begin,
.update = gesture_swipe_update,
.end = gesture_swipe_end,
};
static void gesture_pinch_begin(void *data,
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1,
uint32_t serial, uint32_t time,
struct wl_surface *surface, uint32_t fingers) {
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
struct wlr_event_pointer_pinch_begin wlr_event = {
.device = wlr_dev,
.time_msec = time,
.fingers = fingers,
};
input_device->fingers = fingers;
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_begin, &wlr_event);
}
static void gesture_pinch_update(void *data,
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1,
uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) {
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
struct wlr_event_pointer_pinch_update wlr_event = {
.device = wlr_dev,
.time_msec = time,
.fingers = input_device->fingers,
.dx = wl_fixed_to_double(dx),
.dy = wl_fixed_to_double(dy),
.scale = wl_fixed_to_double(scale),
.rotation = wl_fixed_to_double(rotation),
};
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_update, &wlr_event);
}
static void gesture_pinch_end(void *data,
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1,
uint32_t serial, uint32_t time, int32_t cancelled) {
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
struct wlr_event_pointer_pinch_end wlr_event = {
.device = wlr_dev,
.time_msec = time,
.cancelled = cancelled,
};
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_end, &wlr_event);
}
static const struct zwp_pointer_gesture_pinch_v1_listener gesture_pinch_impl = {
.begin = gesture_pinch_begin,
.update = gesture_pinch_update,
.end = gesture_pinch_end,
};
static void gesture_hold_begin(void *data,
struct zwp_pointer_gesture_hold_v1 *zwp_pointer_gesture_hold_v1,
uint32_t serial, uint32_t time,
struct wl_surface *surface, uint32_t fingers) {
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
struct wlr_event_pointer_hold_begin wlr_event = {
.device = wlr_dev,
.time_msec = time,
.fingers = fingers,
};
input_device->fingers = fingers;
wlr_signal_emit_safe(&wlr_dev->pointer->events.hold_begin, &wlr_event);
}
static void gesture_hold_end(void *data,
struct zwp_pointer_gesture_hold_v1 *zwp_pointer_gesture_hold_v1,
uint32_t serial, uint32_t time, int32_t cancelled) {
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
struct wlr_event_pointer_hold_end wlr_event = {
.device = wlr_dev,
.time_msec = time,
.cancelled = cancelled,
};
wlr_signal_emit_safe(&wlr_dev->pointer->events.hold_end, &wlr_event);
}
static const struct zwp_pointer_gesture_hold_v1_listener gesture_hold_impl = {
.begin = gesture_hold_begin,
.end = gesture_hold_end,
};
static void relative_pointer_handle_relative_motion(void *data,
struct zwp_relative_pointer_v1 *relative_pointer, uint32_t utime_hi,
uint32_t utime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel,
wl_fixed_t dy_unaccel) {
struct wlr_wl_input_device *input_device = data;
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
if (pointer_get_wl(wlr_dev->pointer) != input_device->seat->active_pointer) {
return;
}
uint64_t time_usec = (uint64_t)utime_hi << 32 | utime_lo;
struct wlr_event_pointer_motion wlr_event = {
.device = wlr_dev,
.time_msec = (uint32_t)(time_usec / 1000),
.delta_x = wl_fixed_to_double(dx),
.delta_y = wl_fixed_to_double(dy),
.unaccel_dx = wl_fixed_to_double(dx_unaccel),
.unaccel_dy = wl_fixed_to_double(dy_unaccel),
};
wlr_signal_emit_safe(&wlr_dev->pointer->events.motion, &wlr_event);
}
static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
.relative_motion = relative_pointer_handle_relative_motion,
};
static void pointer_handle_output_destroy(struct wl_listener *listener,
void *data) {
struct wlr_wl_pointer *pointer =
wl_container_of(listener, pointer, output_destroy);
wlr_input_device_destroy(&pointer->input_device->wlr_input_device);
}
void create_wl_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output) {
assert(seat->pointer);
struct wl_pointer *wl_pointer = seat->pointer;
struct wlr_wl_backend *backend = output->backend;
if (output_get_pointer(output, wl_pointer)) {
wlr_log(WLR_DEBUG,
"Pointer for seat %s and output %s already exists (ignoring)",
seat->name, output->wlr_output.name);
return;
}
struct wlr_wl_pointer *pointer = calloc(1, sizeof(struct wlr_wl_pointer));
if (pointer == NULL) {
wlr_log(WLR_ERROR, "Allocation failed");
return;
}
pointer->wl_pointer = wl_pointer;
pointer->output = output; // we need output to map absolute coordinates onto
struct wlr_wl_input_device *dev =
create_wl_input_device(seat, WLR_INPUT_DEVICE_POINTER);
if (dev == NULL) {
free(pointer);
wlr_log(WLR_ERROR, "Allocation failed");
return;
}
pointer->input_device = dev;
wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy);
pointer->output_destroy.notify = pointer_handle_output_destroy;
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
wlr_dev->pointer = &pointer->wlr_pointer;
wlr_dev->output_name = strdup(output->wlr_output.name);
wlr_pointer_init(wlr_dev->pointer, &pointer_impl);
if (backend->zwp_pointer_gestures_v1) {
uint32_t version = zwp_pointer_gestures_v1_get_version(
backend->zwp_pointer_gestures_v1);
pointer->gesture_swipe = zwp_pointer_gestures_v1_get_swipe_gesture(
backend->zwp_pointer_gestures_v1, wl_pointer);
zwp_pointer_gesture_swipe_v1_add_listener(pointer->gesture_swipe, &gesture_swipe_impl, dev);
pointer->gesture_pinch = zwp_pointer_gestures_v1_get_pinch_gesture(
backend->zwp_pointer_gestures_v1, wl_pointer);
zwp_pointer_gesture_pinch_v1_add_listener(pointer->gesture_pinch, &gesture_pinch_impl, dev);
if (version >= ZWP_POINTER_GESTURES_V1_GET_HOLD_GESTURE) {
pointer->gesture_hold = zwp_pointer_gestures_v1_get_hold_gesture(
backend->zwp_pointer_gestures_v1, wl_pointer);
zwp_pointer_gesture_hold_v1_add_listener(pointer->gesture_hold, &gesture_hold_impl, dev);
}
}
if (backend->zwp_relative_pointer_manager_v1) {
pointer->relative_pointer =
zwp_relative_pointer_manager_v1_get_relative_pointer(
backend->zwp_relative_pointer_manager_v1, wl_pointer);
zwp_relative_pointer_v1_add_listener(pointer->relative_pointer,
&relative_pointer_listener, dev);
}
wl_pointer_add_listener(wl_pointer, &pointer_listener, seat);
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev);
}
void create_wl_keyboard(struct wlr_wl_seat *seat) {
assert(seat->keyboard);
struct wl_keyboard *wl_keyboard = seat->keyboard;
struct wlr_wl_input_device *dev =
create_wl_input_device(seat, WLR_INPUT_DEVICE_KEYBOARD);
if (!dev) {
return;
}
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
wlr_dev->keyboard = calloc(1, sizeof(*wlr_dev->keyboard));
if (!wlr_dev->keyboard) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
wlr_input_device_destroy(wlr_dev);
return;
}
wlr_keyboard_init(wlr_dev->keyboard, NULL);
wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, wlr_dev);
wlr_signal_emit_safe(&seat->backend->backend.events.new_input, wlr_dev);
}
void create_wl_touch(struct wlr_wl_seat *seat) {
assert(seat->touch);
struct wl_touch *wl_touch = seat->touch;
struct wlr_wl_input_device *dev =
create_wl_input_device(seat, WLR_INPUT_DEVICE_TOUCH);
if (!dev) {
return;
}
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
wlr_dev->touch = calloc(1, sizeof(*wlr_dev->touch));
if (!wlr_dev->touch) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
wlr_input_device_destroy(wlr_dev);
return;
}
wlr_touch_init(wlr_dev->touch, NULL);
wl_touch_add_listener(wl_touch, &touch_listener, dev);
wlr_signal_emit_safe(&seat->backend->backend.events.new_input, wlr_dev);
}
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
enum wl_seat_capability caps) {
struct wlr_wl_seat *seat = data;
struct wlr_wl_backend *backend = seat->backend;
if ((caps & WL_SEAT_CAPABILITY_POINTER) && seat->pointer == NULL) {
wlr_log(WLR_DEBUG, "seat %p offered pointer", (void *)wl_seat);
struct wl_pointer *wl_pointer = wl_seat_get_pointer(wl_seat);
seat->pointer = wl_pointer;
struct wlr_wl_output *output;
wl_list_for_each(output, &backend->outputs, link) {
create_wl_pointer(seat, output);
}
}
if (!(caps & WL_SEAT_CAPABILITY_POINTER) && seat->pointer != NULL) {
wlr_log(WLR_DEBUG, "seat %p dropped pointer", (void *)wl_seat);
struct wl_pointer *wl_pointer = seat->pointer;
struct wlr_wl_input_device *device, *tmp;
wl_list_for_each_safe(device, tmp, &backend->devices, link) {
if (device->wlr_input_device.type != WLR_INPUT_DEVICE_POINTER) {
continue;
}
struct wlr_wl_pointer *pointer =
pointer_get_wl(device->wlr_input_device.pointer);
if (pointer->wl_pointer != wl_pointer) {
continue;
}
wlr_log(WLR_DEBUG, "dropping pointer %s",
pointer->input_device->wlr_input_device.name);
struct wlr_wl_output *output = pointer->output;
wlr_input_device_destroy(&device->wlr_input_device);
assert(seat->active_pointer != pointer);
assert(output->cursor.pointer != pointer);
}
wl_pointer_release(seat->pointer);
seat->pointer = NULL;
}
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard == NULL) {
wlr_log(WLR_DEBUG, "seat %p offered keyboard", (void *)wl_seat);
struct wl_keyboard *wl_keyboard = wl_seat_get_keyboard(wl_seat);
seat->keyboard = wl_keyboard;
if (backend->started) {
create_wl_keyboard(seat);
}
}
if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard != NULL) {
wlr_log(WLR_DEBUG, "seat %p dropped keyboard", (void *)wl_seat);
struct wlr_wl_input_device *device, *tmp;
wl_list_for_each_safe(device, tmp, &backend->devices, link) {
if (device->wlr_input_device.type != WLR_INPUT_DEVICE_KEYBOARD) {
continue;
}
if (device->seat != seat) {
continue;
}
wlr_input_device_destroy(&device->wlr_input_device);
}
assert(seat->keyboard == NULL); // free'ed by input_device_destroy
}
if ((caps & WL_SEAT_CAPABILITY_TOUCH) && seat->touch == NULL) {
wlr_log(WLR_DEBUG, "seat %p offered touch", (void *)wl_seat);
seat->touch = wl_seat_get_touch(wl_seat);
if (backend->started) {
create_wl_touch(seat);
}
}
if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->touch != NULL) {
wlr_log(WLR_DEBUG, "seat %p dropped touch", (void *)wl_seat);
struct wlr_wl_input_device *device, *tmp;
wl_list_for_each_safe(device, tmp, &backend->devices, link) {
if (device->wlr_input_device.type == WLR_INPUT_DEVICE_TOUCH) {
wlr_input_device_destroy(&device->wlr_input_device);
}
}
wl_touch_release(seat->touch);
seat->touch = NULL;
}
}
static void seat_handle_name(void *data, struct wl_seat *wl_seat,
const char *name) {
struct wlr_wl_seat *seat = data;
free(seat->name);
seat->name = strdup(name);
}
const struct wl_seat_listener seat_listener = {
.capabilities = seat_handle_capabilities,
.name = seat_handle_name,
};
struct wl_seat *wlr_wl_input_device_get_seat(struct wlr_input_device *wlr_dev) {
return input_device_get_seat(wlr_dev)->wl_seat;
}

938
backend/wayland/tablet_v2.c Normal file
View File

@ -0,0 +1,938 @@
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#endif
#include <wayland-util.h>
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <wlr/interfaces/wlr_tablet_pad.h>
#include <wlr/interfaces/wlr_tablet_tool.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/interfaces/wlr_input_device.h>
#include "util/signal.h"
#include "util/time.h"
#include "wlr/util/log.h"
#include "tablet-unstable-v2-client-protocol.h"
#include "backend/wayland.h"
struct wlr_wl_tablet_seat {
struct zwp_tablet_seat_v2 *tablet_seat;
};
struct wlr_wl_tablet_tool {
/* static */
struct zwp_tablet_tool_v2 *tool;
struct wlr_tablet_tool wlr_tool;
/* semi-static */
struct wlr_wl_output *output;
struct wlr_wl_input_device *tablet;
double pre_x, pre_y;
/* per frame */
double x, y;
double pressure;
double distance;
double tilt_x, tilt_y;
double rotation;
double slider;
double wheel_delta;
bool is_in;
bool is_out;
bool is_up;
bool is_down;
};
struct wlr_wl_tablet_pad_ring {
struct wl_list link; // wlr_wl_tablet_pad_group::rings
/* static */
struct zwp_tablet_pad_ring_v2 *ring;
struct wlr_wl_tablet_pad_group *group;
size_t index;
/* per frame */
enum wlr_tablet_pad_ring_source source;
double angle;
bool stopped;
};
struct wlr_wl_tablet_pad_strip {
struct wl_list link; // wlr_wl_tablet_pad_group::strips
struct zwp_tablet_pad_strip_v2 *strip;
struct wlr_wl_tablet_pad_group *group;
size_t index;
enum wlr_tablet_pad_strip_source source;
double position;
bool stopped;
};
struct wlr_wl_tablet_pad_group {
struct zwp_tablet_pad_group_v2 *pad_group;
struct wlr_tablet_pad *pad;
unsigned int mode;
struct wlr_tablet_pad_group group;
struct wl_list rings; // wlr_wl_tablet_pad_ring::link
struct wl_list strips; // wlr_wl_tablet_pad_strips::link
};
static void handle_tablet_pad_ring_source(void *data,
struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2,
uint32_t source) {
struct wlr_wl_tablet_pad_ring *ring = data;
ring->source = source;
}
static void handle_tablet_pad_ring_angle(void *data,
struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2,
wl_fixed_t degrees) {
struct wlr_wl_tablet_pad_ring *ring = data;
ring->angle = wl_fixed_to_double(degrees);
}
static void handle_tablet_pad_ring_stop(void *data,
struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2) {
struct wlr_wl_tablet_pad_ring *ring = data;
ring->stopped = true;
}
static void handle_tablet_pad_ring_frame(void *data,
struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2,
uint32_t time) {
struct wlr_wl_tablet_pad_ring *ring = data;
struct wlr_event_tablet_pad_ring evt = {
.time_msec = time,
.source = ring->source,
.ring = ring->index,
.position = ring->angle,
.mode = ring->group->mode,
};
if (ring->angle >= 0) {
wlr_signal_emit_safe(&ring->group->pad->events.ring, &evt);
}
if (ring->stopped) {
evt.position = -1;
wlr_signal_emit_safe(&ring->group->pad->events.ring, &evt);
}
ring->angle = -1;
ring->stopped = false;
ring->source = 0;
}
static const struct zwp_tablet_pad_ring_v2_listener tablet_pad_ring_listener = {
.source = handle_tablet_pad_ring_source,
.angle = handle_tablet_pad_ring_angle,
.stop = handle_tablet_pad_ring_stop,
.frame = handle_tablet_pad_ring_frame,
};
static void handle_tablet_pad_strip_source(void *data,
struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2,
uint32_t source) {
struct wlr_wl_tablet_pad_strip *strip = data;
strip->source = source;
}
static void handle_tablet_pad_strip_position(void *data,
struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2,
uint32_t position) {
struct wlr_wl_tablet_pad_strip *strip = data;
strip->position = (double) position / 65536.0;
}
static void handle_tablet_pad_strip_stop(void *data,
struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2) {
struct wlr_wl_tablet_pad_strip *strip = data;
strip->stopped = true;
}
static void handle_tablet_pad_strip_frame(void *data,
struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2,
uint32_t time) {
struct wlr_wl_tablet_pad_strip *strip = data;
struct wlr_event_tablet_pad_strip evt = {
.time_msec = time,
.source = strip->source,
.strip = strip->index,
.position = strip->position,
.mode = strip->group->mode,
};
if (strip->position >= 0) {
wlr_signal_emit_safe(&strip->group->pad->events.strip, &evt);
}
if (strip->stopped) {
evt.position = -1;
wlr_signal_emit_safe(&strip->group->pad->events.strip, &evt);
}
strip->position = -1;
strip->stopped = false;
strip->source = 0;
}
static const struct zwp_tablet_pad_strip_v2_listener tablet_pad_strip_listener = {
.source = handle_tablet_pad_strip_source,
.position = handle_tablet_pad_strip_position,
.stop = handle_tablet_pad_strip_stop,
.frame = handle_tablet_pad_strip_frame,
};
static void handle_tablet_pad_group_buttons(void *data,
struct zwp_tablet_pad_group_v2 *pad_group,
struct wl_array *buttons) {
struct wlr_wl_tablet_pad_group *group = data;
free(group->group.buttons);
group->group.buttons = calloc(1, buttons->size);
if (!group->group.buttons) {
// FIXME: Add actual error handling
return;
}
group->group.button_count = buttons->size / sizeof(int);
memcpy(group->group.buttons, buttons->data, buttons->size);
}
static void handle_tablet_pad_group_modes(void *data,
struct zwp_tablet_pad_group_v2 *pad_group, uint32_t modes) {
struct wlr_wl_tablet_pad_group *group = data;
group->group.mode_count = modes;
}
static void handle_tablet_pad_group_ring(void *data,
struct zwp_tablet_pad_group_v2 *pad_group,
struct zwp_tablet_pad_ring_v2 *ring) {
struct wlr_wl_tablet_pad_group *group = data;
struct wlr_wl_tablet_pad_ring *tablet_ring =
calloc(1, sizeof(struct wlr_wl_tablet_pad_ring));
if (!tablet_ring) {
zwp_tablet_pad_ring_v2_destroy(ring);
return;
}
tablet_ring->index = group->pad->ring_count++;
tablet_ring->group = group;
zwp_tablet_pad_ring_v2_add_listener(ring, &tablet_pad_ring_listener,
tablet_ring);
group->group.rings = realloc(group->group.rings,
++group->group.ring_count * sizeof(unsigned int));
group->group.rings[group->group.ring_count - 1] =
tablet_ring->index;
}
static void handle_tablet_pad_group_strip(void *data,
struct zwp_tablet_pad_group_v2 *pad_group,
struct zwp_tablet_pad_strip_v2 *strip) {
struct wlr_wl_tablet_pad_group *group = data;
struct wlr_wl_tablet_pad_strip *tablet_strip =
calloc(1, sizeof(struct wlr_wl_tablet_pad_strip));
if (!tablet_strip) {
zwp_tablet_pad_strip_v2_destroy(strip);
return;
}
tablet_strip->index = group->pad->strip_count++;
tablet_strip->group = group;
zwp_tablet_pad_strip_v2_add_listener(strip, &tablet_pad_strip_listener,
tablet_strip);
group->group.strips = realloc(group->group.strips,
++group->group.strip_count * sizeof(unsigned int));
group->group.strips[group->group.strip_count - 1] =
tablet_strip->index;
}
static void handle_tablet_pad_group_done(void *data,
struct zwp_tablet_pad_group_v2 *pad_group) {
/* Empty for now */
}
static void handle_tablet_pad_group_mode_switch(void *data,
struct zwp_tablet_pad_group_v2 *pad_group,
uint32_t time, uint32_t serial, uint32_t mode) {
struct wlr_wl_tablet_pad_group *group = data;
group->mode = mode;
}
/* This isn't in the listener, but keep the naming scheme around since the
* other removed functions work like this, and pad sub-resources are just a bit
* special */
static void handle_tablet_pad_group_removed(
struct wlr_wl_tablet_pad_group *group) {
/* No need to remove the ::link on strips rings as long as we do *not*
* wl_list_remove on the wl_groups ring/strip attributes here */
struct wlr_wl_tablet_pad_ring *ring, *tmp_ring;
wl_list_for_each_safe(ring, tmp_ring, &group->rings, link) {
zwp_tablet_pad_ring_v2_destroy(ring->ring);
free(ring);
}
struct wlr_wl_tablet_pad_strip *strip, *tmp_strip;
wl_list_for_each_safe(strip, tmp_strip, &group->strips, link) {
zwp_tablet_pad_strip_v2_destroy(strip->strip);
free(strip);
}
zwp_tablet_pad_group_v2_destroy(group->pad_group);
/* While I'm pretty sure we have control over this as well, it's
* outside the scope of a single function, so better be safe than
* sorry */
wl_list_remove(&group->group.link);
free(group);
}
static const struct zwp_tablet_pad_group_v2_listener tablet_pad_group_listener = {
.buttons = handle_tablet_pad_group_buttons,
.modes = handle_tablet_pad_group_modes,
.ring = handle_tablet_pad_group_ring,
.strip = handle_tablet_pad_group_strip,
.done = handle_tablet_pad_group_done,
.mode_switch = handle_tablet_pad_group_mode_switch,
};
static void handle_tablet_pad_group(void *data,
struct zwp_tablet_pad_v2 *zwp_tablet_pad,
struct zwp_tablet_pad_group_v2 *pad_group) {
struct wlr_wl_input_device *dev = data;
struct wlr_tablet_pad *pad = dev->wlr_input_device.tablet_pad;
struct wlr_wl_tablet_pad_group *group =
calloc(1, sizeof(struct wlr_wl_tablet_pad_group));
if (!group) {
zwp_tablet_pad_group_v2_destroy(pad_group);
return;
}
group->pad_group = pad_group;
group->pad = pad;
wl_list_init(&group->rings);
wl_list_init(&group->strips);
zwp_tablet_pad_group_v2_add_listener(pad_group,
&tablet_pad_group_listener, group);
wl_list_insert(&pad->groups, &group->group.link);
}
static void handle_tablet_pad_path(void *data,
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
const char *path) {
struct wlr_wl_input_device *dev = data;
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
char **dst = wl_array_add(&tablet_pad->paths, sizeof(char *));
*dst = strdup(path);
}
static void handle_tablet_pad_buttons(void *data,
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
uint32_t buttons) {
struct wlr_wl_input_device *dev = data;
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
tablet_pad->button_count = buttons;
}
static void handle_tablet_pad_button(void *data,
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
uint32_t time, uint32_t button, uint32_t state) {
struct wlr_wl_input_device *dev = data;
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
struct wlr_event_tablet_pad_button evt = {
.time_msec = time,
.button = button,
.state = state,
.mode = 0,
.group = 0,
};
wlr_signal_emit_safe(&tablet_pad->events.button, &evt);
}
static void handle_tablet_pad_done(void *data,
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2) {
struct wlr_wl_input_device *dev = data;
wlr_signal_emit_safe(&dev->backend->backend.events.new_input,
&dev->wlr_input_device);
}
static void handle_tablet_pad_enter(void *data,
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
uint32_t serial, struct zwp_tablet_v2 *tablet_p,
struct wl_surface *surface) {
struct wlr_wl_input_device *dev = data;
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
struct wlr_wl_input_device *tab_dev = zwp_tablet_v2_get_user_data(tablet_p);
struct wlr_input_device *tablet = &tab_dev->wlr_input_device;
wlr_log(WLR_DEBUG, "Tablet: %p\n", tablet);
wlr_signal_emit_safe(&tablet_pad->events.attach_tablet, tablet);
}
static void handle_tablet_pad_leave(void *data,
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
uint32_t serial, struct wl_surface *surface) {
/* Empty. Probably staying that way, unless we want to create/destroy
* tablet on enter/leave events (ehh) */
}
static void handle_tablet_pad_removed(void *data,
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2) {
struct wlr_wl_input_device *dev = data;
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
/* This doesn't free anything, but emits the destroy signal */
wlr_input_device_destroy(&dev->wlr_input_device);
/* This is a bit ugly, but we need to remove it from our list */
wl_list_remove(&dev->link);
struct wlr_wl_tablet_pad_group *group, *it;
wl_list_for_each_safe(group, it, &tablet_pad->groups, group.link) {
handle_tablet_pad_group_removed(group);
}
/* This frees */
wlr_tablet_pad_destroy(tablet_pad);
zwp_tablet_pad_v2_destroy(dev->resource);
free(dev);
}
static const struct zwp_tablet_pad_v2_listener tablet_pad_listener = {
.group = handle_tablet_pad_group,
.path = handle_tablet_pad_path,
.buttons = handle_tablet_pad_buttons,
.button = handle_tablet_pad_button,
.done = handle_tablet_pad_done,
.enter = handle_tablet_pad_enter,
.leave = handle_tablet_pad_leave,
.removed = handle_tablet_pad_removed,
};
static void handle_pad_added(void *data,
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
struct zwp_tablet_pad_v2 *id) {
wlr_log(WLR_DEBUG, "New tablet pad");
struct wlr_wl_seat *seat = data;
struct wlr_wl_input_device *dev = create_wl_input_device(
seat, WLR_INPUT_DEVICE_TABLET_PAD);
if (!dev) {
/* This leaks a couple of server-sent resource ids. iirc this
* shouldn't ever be a problem, but it isn't exactly nice
* either. */
zwp_tablet_pad_v2_destroy(id);
return;
}
dev->resource = id;
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
wlr_dev->tablet_pad = calloc(1, sizeof(*wlr_dev->tablet_pad));
if (!wlr_dev->tablet_pad) {
/* This leaks a couple of server-sent resource ids. iirc this
* shouldn't ever be a problem, but it isn't exactly nice
* either. */
free(dev);
zwp_tablet_pad_v2_destroy(id);
return;
}
wlr_tablet_pad_init(wlr_dev->tablet_pad, NULL);
zwp_tablet_pad_v2_add_listener(id, &tablet_pad_listener, dev);
}
static void handle_tablet_tool_done(void *data,
struct zwp_tablet_tool_v2 *id) {
/* empty */
}
static enum wlr_tablet_tool_type tablet_type_to_wlr_type(enum zwp_tablet_tool_v2_type type) {
switch (type) {
case ZWP_TABLET_TOOL_V2_TYPE_PEN:
return WLR_TABLET_TOOL_TYPE_PEN;
case ZWP_TABLET_TOOL_V2_TYPE_ERASER:
return WLR_TABLET_TOOL_TYPE_ERASER;
case ZWP_TABLET_TOOL_V2_TYPE_BRUSH:
return WLR_TABLET_TOOL_TYPE_BRUSH;
case ZWP_TABLET_TOOL_V2_TYPE_PENCIL:
return WLR_TABLET_TOOL_TYPE_PENCIL;
case ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH:
return WLR_TABLET_TOOL_TYPE_AIRBRUSH;
case ZWP_TABLET_TOOL_V2_TYPE_MOUSE:
return WLR_TABLET_TOOL_TYPE_MOUSE;
case ZWP_TABLET_TOOL_V2_TYPE_LENS:
return WLR_TABLET_TOOL_TYPE_LENS;
case ZWP_TABLET_TOOL_V2_TYPE_FINGER:
// unused, see:
// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/18
abort();
}
abort(); // unreachable
}
static void handle_tablet_tool_type(void *data,
struct zwp_tablet_tool_v2 *id,
uint32_t tool_type) {
struct wlr_wl_tablet_tool *tool = data;
tool->wlr_tool.type = tablet_type_to_wlr_type(tool_type);
}
static void handle_tablet_tool_serial(void *data,
struct zwp_tablet_tool_v2 *id,
uint32_t high, uint32_t low) {
struct wlr_wl_tablet_tool *tool = data;
tool->wlr_tool.hardware_serial =
((uint64_t) high) << 32 | (uint64_t) low;
}
static void handle_tablet_tool_id_wacom(void *data,
struct zwp_tablet_tool_v2 *id,
uint32_t high, uint32_t low) {
struct wlr_wl_tablet_tool *tool = data;
tool->wlr_tool.hardware_wacom =
((uint64_t) high) << 32 | (uint64_t) low;
}
static void handle_tablet_tool_capability(void *data,
struct zwp_tablet_tool_v2 *id,
uint32_t capability) {
struct wlr_wl_tablet_tool *tool = data;
enum zwp_tablet_tool_v2_capability cap = capability;
switch (cap) {
case ZWP_TABLET_TOOL_V2_CAPABILITY_TILT:
tool->wlr_tool.tilt = true;
break;
case ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE:
tool->wlr_tool.pressure = true;
break;
case ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE:
tool->wlr_tool.distance = true;
break;
case ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION:
tool->wlr_tool.rotation = true;
break;
case ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER:
tool->wlr_tool.slider = true;
break;
case ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL:
tool->wlr_tool.wheel = true;
break;
}
}
static void handle_tablet_tool_proximity_in(void *data,
struct zwp_tablet_tool_v2 *id, uint32_t serial,
struct zwp_tablet_v2 *tablet_id,
struct wl_surface *surface) {
struct wlr_wl_tablet_tool *tool = data;
tool->is_in = true;
tool->tablet = zwp_tablet_v2_get_user_data(tablet_id);
tool->output = wl_surface_get_user_data(surface);
}
static void handle_tablet_tool_proximity_out(void *data,
struct zwp_tablet_tool_v2 *id) {
struct wlr_wl_tablet_tool *tool = data;
tool->is_out = true;
tool->output = NULL;
}
static void handle_tablet_tool_down(void *data,
struct zwp_tablet_tool_v2 *id,
unsigned int serial) {
struct wlr_wl_tablet_tool *tool = data;
tool->is_down = true;
}
static void handle_tablet_tool_up(void *data,
struct zwp_tablet_tool_v2 *id) {
struct wlr_wl_tablet_tool *tool = data;
tool->is_up = true;
}
static void handle_tablet_tool_motion(void *data,
struct zwp_tablet_tool_v2 *id,
wl_fixed_t x, wl_fixed_t y) {
struct wlr_wl_tablet_tool *tool = data;
struct wlr_wl_output *output = tool->output;
assert(output);
tool->x = wl_fixed_to_double(x) / output->wlr_output.width;
tool->y = wl_fixed_to_double(y) / output->wlr_output.height;
}
static void handle_tablet_tool_pressure(void *data,
struct zwp_tablet_tool_v2 *id,
uint32_t pressure) {
struct wlr_wl_tablet_tool *tool = data;
tool->pressure = (double) pressure / 65535.0;
}
static void handle_tablet_tool_distance(void *data,
struct zwp_tablet_tool_v2 *id,
uint32_t distance) {
struct wlr_wl_tablet_tool *tool = data;
tool->distance = (double) distance / 65535.0;
}
static void handle_tablet_tool_tilt(void *data,
struct zwp_tablet_tool_v2 *id,
wl_fixed_t x, wl_fixed_t y) {
struct wlr_wl_tablet_tool *tool = data;
tool->tilt_x = wl_fixed_to_double(x);
tool->tilt_y = wl_fixed_to_double(y);
}
static void handle_tablet_tool_rotation(void *data,
struct zwp_tablet_tool_v2 *id,
wl_fixed_t rotation) {
struct wlr_wl_tablet_tool *tool = data;
tool->rotation = wl_fixed_to_double(rotation);
}
static void handle_tablet_tool_slider(void *data,
struct zwp_tablet_tool_v2 *id,
int slider) {
struct wlr_wl_tablet_tool *tool = data;
tool->slider = (double) slider / 65535.0;;
}
// TODO: This looks wrong :/
static void handle_tablet_tool_wheel(void *data,
struct zwp_tablet_tool_v2 *id,
wl_fixed_t degree, int clicks) {
struct wlr_wl_tablet_tool *tool = data;
tool->wheel_delta = wl_fixed_to_double(degree);
}
static void handle_tablet_tool_button(void *data,
struct zwp_tablet_tool_v2 *id,
uint32_t serial, uint32_t button, uint32_t state) {
struct wlr_wl_tablet_tool *tool = data;
struct wlr_tablet *tablet = tool->tablet->wlr_input_device.tablet;
struct wlr_event_tablet_tool_button evt = {
.device = &tool->tablet->wlr_input_device,
.tool = &tool->wlr_tool,
.time_msec = get_current_time_msec(),
.button = button,
.state = state == ZWP_TABLET_TOOL_V2_BUTTON_STATE_RELEASED ?
WLR_BUTTON_RELEASED : WLR_BUTTON_PRESSED,
};
wlr_signal_emit_safe(&tablet->events.button, &evt);
}
static void clear_tablet_tool_values(struct wlr_wl_tablet_tool *tool) {
tool->is_out = tool->is_in = false;
tool->is_up = tool->is_down = false;
tool->x = tool->y = NAN;
tool->pressure = NAN;
tool->distance = NAN;
tool->tilt_x = tool->tilt_y = NAN;
tool->rotation = NAN;
tool->slider = NAN;
tool->wheel_delta = NAN;
}
static void handle_tablet_tool_frame(void *data,
struct zwp_tablet_tool_v2 *id,
uint32_t time) {
struct wlr_wl_tablet_tool *tool = data;
if (tool->is_out && tool->is_in) {
/* we got a tablet tool coming in and out of proximity before
* we could process it. Just ignore anything it did */
goto clear_values;
}
struct wlr_tablet *tablet = tool->tablet->wlr_input_device.tablet;
if (tool->is_in) {
struct wlr_event_tablet_tool_proximity evt = {
.device = &tool->tablet->wlr_input_device,
.tool = &tool->wlr_tool,
.time_msec = time,
.x = tool->x,
.y = tool->y,
.state = WLR_TABLET_TOOL_PROXIMITY_IN,
};
wlr_signal_emit_safe(&tablet->events.proximity, &evt);
}
{
struct wlr_event_tablet_tool_axis evt = {
.device = &tool->tablet->wlr_input_device,
.tool = &tool->wlr_tool,
.time_msec = time,
.updated_axes = 0,
};
if (!isnan(tool->x) && !tool->is_in) {
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_X;
evt.x = tool->x;
}
if (!isnan(tool->y) && !tool->is_in) {
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_Y;
evt.y = tool->y;
}
if (!isnan(tool->pressure)) {
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_PRESSURE;
evt.pressure = tool->pressure;
}
if (!isnan(tool->distance)) {
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_DISTANCE;
evt.distance = tool->distance;
}
if (!isnan(tool->tilt_x)) {
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_TILT_X;
evt.tilt_x = tool->tilt_x;
}
if (!isnan(tool->tilt_y)) {
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_TILT_Y;
evt.tilt_y = tool->tilt_y;
}
if (!isnan(tool->rotation)) {
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_ROTATION;
evt.rotation = tool->rotation;
}
if (!isnan(tool->slider)) {
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_SLIDER;
evt.slider = tool->slider;
}
if (!isnan(tool->wheel_delta)) {
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_WHEEL;
evt.wheel_delta = tool->wheel_delta;
}
if (evt.updated_axes) {
wlr_signal_emit_safe(&tablet->events.axis, &evt);
}
}
/* This will always send down then up if we got both.
* Maybe we should send them right away, in case we get up then both in
* series?
* Downside: Here we have the frame time, if we sent right away, we
* need to generate the time */
if (tool->is_down) {
struct wlr_event_tablet_tool_tip evt = {
.device = &tool->tablet->wlr_input_device,
.tool = &tool->wlr_tool,
.time_msec = time,
.x = tool->x,
.y = tool->y,
.state = WLR_TABLET_TOOL_TIP_DOWN,
};
wlr_signal_emit_safe(&tablet->events.tip, &evt);
}
if (tool->is_up) {
struct wlr_event_tablet_tool_tip evt = {
.device = &tool->tablet->wlr_input_device,
.tool = &tool->wlr_tool,
.time_msec = time,
.x = tool->x,
.y = tool->y,
.state = WLR_TABLET_TOOL_TIP_UP,
};
wlr_signal_emit_safe(&tablet->events.tip, &evt);
}
if (tool->is_out) {
struct wlr_event_tablet_tool_proximity evt = {
.device = &tool->tablet->wlr_input_device,
.tool = &tool->wlr_tool,
.time_msec = time,
.x = tool->x,
.y = tool->y,
.state = WLR_TABLET_TOOL_PROXIMITY_OUT,
};
wlr_signal_emit_safe(&tablet->events.proximity, &evt);
}
clear_values:
clear_tablet_tool_values(tool);
}
static void handle_tablet_tool_removed(void *data,
struct zwp_tablet_tool_v2 *id) {
struct wlr_wl_tablet_tool *tool = data;
zwp_tablet_tool_v2_destroy(tool->tool);
wlr_signal_emit_safe(&tool->wlr_tool.events.destroy, &tool->wlr_tool);
free(tool);
}
static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = {
.removed = handle_tablet_tool_removed,
.done = handle_tablet_tool_done,
.type = handle_tablet_tool_type,
.hardware_serial = handle_tablet_tool_serial,
.hardware_id_wacom = handle_tablet_tool_id_wacom,
.capability = handle_tablet_tool_capability,
.proximity_in = handle_tablet_tool_proximity_in,
.proximity_out = handle_tablet_tool_proximity_out,
.down = handle_tablet_tool_down,
.up = handle_tablet_tool_up,
.motion = handle_tablet_tool_motion,
.pressure = handle_tablet_tool_pressure,
.distance = handle_tablet_tool_distance,
.tilt = handle_tablet_tool_tilt,
.rotation = handle_tablet_tool_rotation,
.slider = handle_tablet_tool_slider,
.wheel = handle_tablet_tool_wheel,
.button = handle_tablet_tool_button,
.frame = handle_tablet_tool_frame,
};
static void handle_tool_added(void *data,
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
struct zwp_tablet_tool_v2 *id) {
wlr_log(WLR_DEBUG, "New tablet tool");
struct wlr_wl_tablet_tool *tool = calloc(1, sizeof(*tool));
if (!tool) {
zwp_tablet_tool_v2_destroy(id);
return;
}
tool->tool = id;
clear_tablet_tool_values(tool);
wl_signal_init(&tool->wlr_tool.events.destroy);
zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listener, tool);
}
static void handle_tablet_name(void *data, struct zwp_tablet_v2 *zwp_tablet_v2,
const char *name) {
struct wlr_wl_input_device *dev = data;
struct wlr_tablet *tablet = dev->wlr_input_device.tablet;
free(tablet->name);
tablet->name = strdup(name);
}
static void handle_tablet_id(void *data, struct zwp_tablet_v2 *zwp_tablet_v2,
uint32_t vid, uint32_t pid) {
struct wlr_wl_input_device *dev = data;
dev->wlr_input_device.vendor = vid;
dev->wlr_input_device.product = pid;
}
static void handle_tablet_path(void *data, struct zwp_tablet_v2 *zwp_tablet_v2,
const char *path) {
struct wlr_wl_input_device *dev = data;
struct wlr_tablet *tablet = dev->wlr_input_device.tablet;
char **dst = wl_array_add(&tablet->paths, sizeof(char *));
*dst = strdup(path);
}
static void handle_tablet_done(void *data, struct zwp_tablet_v2 *zwp_tablet_v2) {
struct wlr_wl_input_device *dev = data;
wlr_signal_emit_safe(&dev->backend->backend.events.new_input,
&dev->wlr_input_device);
}
static void handle_tablet_removed(void *data,
struct zwp_tablet_v2 *zwp_tablet_v2) {
struct wlr_wl_input_device *dev = data;
/* This doesn't free anything, but emits the destroy signal */
wlr_input_device_destroy(&dev->wlr_input_device);
/* This is a bit ugly, but we need to remove it from our list */
wl_list_remove(&dev->link);
zwp_tablet_v2_destroy(dev->resource);
free(dev);
}
static const struct zwp_tablet_v2_listener tablet_listener = {
.name = handle_tablet_name,
.id = handle_tablet_id,
.path = handle_tablet_path,
.done = handle_tablet_done,
.removed = handle_tablet_removed,
};
static void handle_tab_added(void *data,
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
struct zwp_tablet_v2 *id) {
wlr_log(WLR_DEBUG, "New tablet");
struct wlr_wl_seat *seat = data;
struct wlr_wl_input_device *dev = create_wl_input_device(
seat, WLR_INPUT_DEVICE_TABLET_TOOL);
if (!dev) {
zwp_tablet_v2_destroy(id);
return;
}
dev->resource = id;
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
wlr_dev->tablet = calloc(1, sizeof(*wlr_dev->tablet));
if (!wlr_dev->tablet) {
zwp_tablet_v2_destroy(id);
return;
}
zwp_tablet_v2_set_user_data(id, wlr_dev->tablet);
wlr_tablet_init(wlr_dev->tablet, NULL);
zwp_tablet_v2_add_listener(id, &tablet_listener, dev);
}
static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
.tablet_added = handle_tab_added,
.tool_added = handle_tool_added,
.pad_added = handle_pad_added,
};
struct wlr_wl_tablet_seat *wl_add_tablet_seat(
struct zwp_tablet_manager_v2 *manager,
struct wlr_wl_seat *seat) {
struct wlr_wl_tablet_seat *ret =
calloc(1, sizeof(struct wlr_wl_tablet_seat));
if (!(ret->tablet_seat =
zwp_tablet_manager_v2_get_tablet_seat(manager, seat->wl_seat))) {
free(ret);
return NULL;
}
zwp_tablet_seat_v2_add_listener(ret->tablet_seat,
&tablet_seat_listener, seat);
return ret;
}

View File

@ -1,455 +0,0 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wayland-client.h>
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/interfaces/wlr_pointer.h>
#include <wlr/interfaces/wlr_touch.h>
#include <wlr/util/log.h>
#include "backend/wayland.h"
#include "util/signal.h"
static struct wlr_wl_pointer *output_get_pointer(struct wlr_wl_output *output) {
struct wlr_input_device *wlr_dev;
wl_list_for_each(wlr_dev, &output->backend->devices, link) {
if (wlr_dev->type != WLR_INPUT_DEVICE_POINTER) {
continue;
}
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer);
if (pointer->output == output) {
return pointer;
}
}
return NULL;
}
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface, wl_fixed_t sx,
wl_fixed_t sy) {
struct wlr_wl_backend *backend = data;
if (surface == NULL) {
return;
}
struct wlr_wl_output *output = wl_surface_get_user_data(surface);
assert(output);
struct wlr_wl_pointer *pointer = output_get_pointer(output);
output->enter_serial = serial;
backend->current_pointer = pointer;
update_wl_output_cursor(output);
}
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface) {
struct wlr_wl_backend *backend = data;
if (surface == NULL) {
return;
}
struct wlr_wl_output *output = wl_surface_get_user_data(surface);
assert(output);
output->enter_serial = 0;
if (backend->current_pointer == NULL ||
backend->current_pointer->output != output) {
return;
}
backend->current_pointer = NULL;
}
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
uint32_t time, wl_fixed_t sx, wl_fixed_t sy) {
struct wlr_wl_backend *backend = data;
struct wlr_wl_pointer *pointer = backend->current_pointer;
if (pointer == NULL) {
return;
}
struct wlr_output *wlr_output = &pointer->output->wlr_output;
struct wlr_event_pointer_motion_absolute event = {
.device = &pointer->input_device->wlr_input_device,
.time_msec = time,
.x = wl_fixed_to_double(sx) / wlr_output->width,
.y = wl_fixed_to_double(sy) / wlr_output->height,
};
wlr_signal_emit_safe(&pointer->wlr_pointer.events.motion_absolute, &event);
}
static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
struct wlr_wl_backend *backend = data;
struct wlr_wl_pointer *pointer = backend->current_pointer;
if (pointer == NULL) {
return;
}
struct wlr_event_pointer_button event = {
.device = &pointer->input_device->wlr_input_device,
.button = button,
.state = state,
.time_msec = time,
};
wlr_signal_emit_safe(&pointer->wlr_pointer.events.button, &event);
}
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis, wl_fixed_t value) {
struct wlr_wl_backend *backend = data;
struct wlr_wl_pointer *pointer = backend->current_pointer;
if (pointer == NULL) {
return;
}
struct wlr_event_pointer_axis event = {
.device = &pointer->input_device->wlr_input_device,
.delta = wl_fixed_to_double(value),
.delta_discrete = pointer->axis_discrete,
.orientation = axis,
.time_msec = time,
.source = pointer->axis_source,
};
wlr_signal_emit_safe(&pointer->wlr_pointer.events.axis, &event);
pointer->axis_discrete = 0;
}
static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
struct wlr_wl_backend *backend = data;
struct wlr_wl_pointer *pointer = backend->current_pointer;
if (pointer == NULL) {
return;
}
wlr_signal_emit_safe(&pointer->wlr_pointer.events.frame,
&pointer->wlr_pointer);
}
static void pointer_handle_axis_source(void *data,
struct wl_pointer *wl_pointer, uint32_t axis_source) {
struct wlr_wl_backend *backend = data;
struct wlr_wl_pointer *pointer = backend->current_pointer;
if (pointer == NULL) {
return;
}
pointer->axis_source = axis_source;
}
static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis) {
struct wlr_wl_backend *backend = data;
struct wlr_wl_pointer *pointer = backend->current_pointer;
if (pointer == NULL) {
return;
}
struct wlr_event_pointer_axis event = {
.device = &pointer->input_device->wlr_input_device,
.delta = 0,
.delta_discrete = 0,
.orientation = axis,
.time_msec = time,
.source = pointer->axis_source,
};
wlr_signal_emit_safe(&pointer->wlr_pointer.events.axis, &event);
}
static void pointer_handle_axis_discrete(void *data,
struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
struct wlr_wl_backend *backend = data;
struct wlr_wl_pointer *pointer = backend->current_pointer;
if (pointer == NULL) {
return;
}
pointer->axis_discrete = discrete;
}
static const struct wl_pointer_listener pointer_listener = {
.enter = pointer_handle_enter,
.leave = pointer_handle_leave,
.motion = pointer_handle_motion,
.button = pointer_handle_button,
.axis = pointer_handle_axis,
.frame = pointer_handle_frame,
.axis_source = pointer_handle_axis_source,
.axis_stop = pointer_handle_axis_stop,
.axis_discrete = pointer_handle_axis_discrete,
};
static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
uint32_t format, int32_t fd, uint32_t size) {
// TODO: set keymap
}
static uint32_t get_current_time_msec() {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return now.tv_nsec / 1000;
}
static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
struct wlr_input_device *dev = data;
uint32_t time = get_current_time_msec();
uint32_t *keycode_ptr;
wl_array_for_each(keycode_ptr, keys) {
struct wlr_event_keyboard_key event = {
.keycode = *keycode_ptr,
.state = WLR_KEY_PRESSED,
.time_msec = time,
.update_state = false,
};
wlr_keyboard_notify_key(dev->keyboard, &event);
}
}
static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface) {
struct wlr_input_device *dev = data;
uint32_t time = get_current_time_msec();
uint32_t pressed[dev->keyboard->num_keycodes + 1];
memcpy(pressed, dev->keyboard->keycodes,
dev->keyboard->num_keycodes * sizeof(uint32_t));
for (size_t i = 0; i < sizeof(pressed)/sizeof(pressed[0]); ++i) {
uint32_t keycode = pressed[i];
struct wlr_event_keyboard_key event = {
.keycode = keycode,
.state = WLR_KEY_RELEASED,
.time_msec = time,
.update_state = false,
};
wlr_keyboard_notify_key(dev->keyboard, &event);
}
}
static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
struct wlr_input_device *dev = data;
assert(dev && dev->keyboard);
struct wlr_event_keyboard_key wlr_event = {
.keycode = key,
.state = state,
.time_msec = time,
.update_state = false,
};
wlr_keyboard_notify_key(dev->keyboard, &wlr_event);
}
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group) {
struct wlr_input_device *dev = data;
assert(dev && dev->keyboard);
wlr_keyboard_notify_modifiers(dev->keyboard, mods_depressed, mods_latched,
mods_locked, group);
}
static void keyboard_handle_repeat_info(void *data,
struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) {
// This space is intentionally left blank
}
static struct wl_keyboard_listener keyboard_listener = {
.keymap = keyboard_handle_keymap,
.enter = keyboard_handle_enter,
.leave = keyboard_handle_leave,
.key = keyboard_handle_key,
.modifiers = keyboard_handle_modifiers,
.repeat_info = keyboard_handle_repeat_info
};
static struct wlr_wl_input_device *get_wl_input_device_from_input_device(
struct wlr_input_device *wlr_dev) {
assert(wlr_input_device_is_wl(wlr_dev));
return (struct wlr_wl_input_device *)wlr_dev;
}
static void input_device_destroy(struct wlr_input_device *wlr_dev) {
struct wlr_wl_input_device *dev =
get_wl_input_device_from_input_device(wlr_dev);
if (dev->resource) {
wl_proxy_destroy(dev->resource);
}
wl_list_remove(&dev->wlr_input_device.link);
free(dev);
}
static struct wlr_input_device_impl input_device_impl = {
.destroy = input_device_destroy,
};
bool wlr_input_device_is_wl(struct wlr_input_device *dev) {
return dev->impl == &input_device_impl;
}
static struct wlr_wl_input_device *create_wl_input_device(
struct wlr_wl_backend *backend, enum wlr_input_device_type type) {
struct wlr_wl_input_device *dev =
calloc(1, sizeof(struct wlr_wl_input_device));
if (dev == NULL) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return NULL;
}
dev->backend = backend;
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
unsigned int vendor = 0, product = 0;
const char *name = "wayland";
wlr_input_device_init(wlr_dev, type, &input_device_impl, name, vendor,
product);
wl_list_insert(&backend->devices, &wlr_dev->link);
return dev;
}
static struct wlr_pointer_impl pointer_impl;
struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer) {
assert(wlr_pointer->impl == &pointer_impl);
return (struct wlr_wl_pointer *)wlr_pointer;
}
static void pointer_destroy(struct wlr_pointer *wlr_pointer) {
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_pointer);
wl_list_remove(&pointer->output_destroy.link);
free(pointer);
}
static struct wlr_pointer_impl pointer_impl = {
.destroy = pointer_destroy,
};
static void pointer_handle_output_destroy(struct wl_listener *listener,
void *data) {
struct wlr_wl_pointer *pointer =
wl_container_of(listener, pointer, output_destroy);
wlr_input_device_destroy(&pointer->input_device->wlr_input_device);
}
void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *output) {
struct wlr_wl_backend *backend = output->backend;
struct wlr_input_device *wlr_dev;
wl_list_for_each(wlr_dev, &output->backend->devices, link) {
if (wlr_dev->type != WLR_INPUT_DEVICE_POINTER) {
continue;
}
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer);
if (pointer->output == output) {
return;
}
}
struct wlr_wl_pointer *pointer = calloc(1, sizeof(struct wlr_wl_pointer));
if (pointer == NULL) {
wlr_log(WLR_ERROR, "Allocation failed");
return;
}
pointer->wl_pointer = wl_pointer;
pointer->output = output;
wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy);
pointer->output_destroy.notify = pointer_handle_output_destroy;
struct wlr_wl_input_device *dev =
create_wl_input_device(backend, WLR_INPUT_DEVICE_POINTER);
if (dev == NULL) {
free(pointer);
wlr_log(WLR_ERROR, "Allocation failed");
return;
}
pointer->input_device = dev;
wlr_dev = &dev->wlr_input_device;
wlr_dev->pointer = &pointer->wlr_pointer;
wlr_dev->output_name = strdup(output->wlr_output.name);
wlr_pointer_init(wlr_dev->pointer, &pointer_impl);
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev);
}
void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *wl) {
struct wlr_wl_input_device *dev =
create_wl_input_device(wl, WLR_INPUT_DEVICE_KEYBOARD);
if (!dev) {
return;
}
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
wlr_dev->keyboard = calloc(1, sizeof(*wlr_dev->keyboard));
if (!wlr_dev->keyboard) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
free(dev);
return;
}
wlr_keyboard_init(wlr_dev->keyboard, NULL);
wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, wlr_dev);
dev->resource = wl_keyboard;
wlr_signal_emit_safe(&wl->backend.events.new_input, wlr_dev);
}
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
enum wl_seat_capability caps) {
struct wlr_wl_backend *backend = data;
assert(backend->seat == wl_seat);
if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
wlr_log(WLR_DEBUG, "seat %p offered pointer", (void*) wl_seat);
struct wl_pointer *wl_pointer = wl_seat_get_pointer(wl_seat);
backend->pointer = wl_pointer;
struct wlr_wl_output *output;
wl_list_for_each(output, &backend->outputs, link) {
create_wl_pointer(wl_pointer, output);
}
wl_pointer_add_listener(wl_pointer, &pointer_listener, backend);
}
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
wlr_log(WLR_DEBUG, "seat %p offered keyboard", (void*) wl_seat);
struct wl_keyboard *wl_keyboard = wl_seat_get_keyboard(wl_seat);
backend->keyboard = wl_keyboard;
if (backend->started) {
create_wl_keyboard(wl_keyboard, backend);
}
}
}
static void seat_handle_name(void *data, struct wl_seat *wl_seat,
const char *name) {
struct wlr_wl_backend *backend = data;
assert(backend->seat == wl_seat);
// Do we need to check if seatName was previously set for name change?
free(backend->seat_name);
backend->seat_name = strdup(name);
}
const struct wl_seat_listener seat_listener = {
.capabilities = seat_handle_capabilities,
.name = seat_handle_name,
};

View File

@ -1,17 +1,24 @@
#define _POSIX_C_SOURCE 200112L
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <xf86drm.h>
#include <wlr/config.h>
#include <X11/Xlib-xcb.h>
#include <wayland-server.h>
#include <drm_fourcc.h>
#include <wayland-server-core.h>
#include <xcb/xcb.h>
#include <xcb/dri3.h>
#include <xcb/present.h>
#include <xcb/render.h>
#include <xcb/shm.h>
#include <xcb/xcb_renderutil.h>
#include <xcb/xfixes.h>
#include <xcb/xinput.h>
@ -20,13 +27,27 @@
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/interfaces/wlr_pointer.h>
#include <wlr/render/egl.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/util/log.h>
#include "backend/x11.h"
#include "render/drm_format_set.h"
#include "util/signal.h"
// See dri2_format_for_depth in mesa
const struct wlr_x11_format formats[] = {
{ .drm = DRM_FORMAT_XRGB8888, .depth = 24, .bpp = 32 },
{ .drm = DRM_FORMAT_ARGB8888, .depth = 32, .bpp = 32 },
};
static const struct wlr_x11_format *x11_format_from_depth(uint8_t depth) {
for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
if (formats[i].depth == depth) {
return &formats[i];
}
}
return NULL;
}
struct wlr_x11_output *get_x11_output_from_window_id(
struct wlr_x11_backend *x11, xcb_window_t window) {
struct wlr_x11_output *output;
@ -38,6 +59,10 @@ struct wlr_x11_output *get_x11_output_from_window_id(
return NULL;
}
static void handle_x11_error(struct wlr_x11_backend *x11, xcb_value_error_t *ev);
static void handle_x11_unknown_event(struct wlr_x11_backend *x11,
xcb_generic_event_t *ev);
static void handle_x11_event(struct wlr_x11_backend *x11,
xcb_generic_event_t *event) {
switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) {
@ -46,7 +71,10 @@ static void handle_x11_event(struct wlr_x11_backend *x11,
struct wlr_x11_output *output =
get_x11_output_from_window_id(x11, ev->window);
if (output != NULL) {
wlr_output_update_needs_swap(&output->wlr_output);
pixman_region32_union_rect(
&output->exposed, &output->exposed,
ev->x, ev->y, ev->width, ev->height);
wlr_output_update_needs_frame(&output->wlr_output);
}
break;
}
@ -68,6 +96,9 @@ static void handle_x11_event(struct wlr_x11_backend *x11,
if (output != NULL) {
wlr_output_destroy(&output->wlr_output);
}
} else {
wlr_log(WLR_DEBUG, "Unhandled client message %"PRIu32,
ev->data.data32[0]);
}
break;
}
@ -75,8 +106,24 @@ static void handle_x11_event(struct wlr_x11_backend *x11,
xcb_ge_generic_event_t *ev = (xcb_ge_generic_event_t *)event;
if (ev->extension == x11->xinput_opcode) {
handle_x11_xinput_event(x11, ev);
} else if (ev->extension == x11->present_opcode) {
handle_x11_present_event(x11, ev);
} else {
handle_x11_unknown_event(x11, event);
}
break;
}
case 0: {
xcb_value_error_t *ev = (xcb_value_error_t *)event;
handle_x11_error(x11, ev);
break;
}
case XCB_UNMAP_NOTIFY:
case XCB_MAP_NOTIFY:
break;
default:
handle_x11_unknown_event(x11, event);
break;
}
}
@ -84,6 +131,9 @@ static int x11_event(int fd, uint32_t mask, void *data) {
struct wlr_x11_backend *x11 = data;
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
if (mask & WL_EVENT_ERROR) {
wlr_log(WLR_ERROR, "Failed to read from X11 server");
}
wl_display_terminate(x11->wl_display);
return 0;
}
@ -94,6 +144,12 @@ static int x11_event(int fd, uint32_t mask, void *data) {
free(e);
}
int ret = xcb_connection_has_error(x11->xcb);
if (ret != 0) {
wlr_log(WLR_ERROR, "X11 connection error (%d)", ret);
wl_display_terminate(x11->wl_display);
}
return 0;
}
@ -107,6 +163,8 @@ static bool backend_start(struct wlr_backend *backend) {
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
x11->started = true;
wlr_log(WLR_INFO, "Starting X11 backend");
wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->keyboard_dev);
for (size_t i = 0; i < x11->requested_outputs; ++i) {
@ -130,32 +188,43 @@ static void backend_destroy(struct wlr_backend *backend) {
wlr_input_device_destroy(&x11->keyboard_dev);
wlr_signal_emit_safe(&backend->events.destroy, backend);
wlr_backend_finish(backend);
if (x11->event_source) {
wl_event_source_remove(x11->event_source);
}
wl_list_remove(&x11->display_destroy.link);
wlr_renderer_destroy(x11->renderer);
wlr_egl_finish(&x11->egl);
wlr_drm_format_set_finish(&x11->primary_dri3_formats);
wlr_drm_format_set_finish(&x11->primary_shm_formats);
wlr_drm_format_set_finish(&x11->dri3_formats);
wlr_drm_format_set_finish(&x11->shm_formats);
if (x11->xlib_conn) {
XCloseDisplay(x11->xlib_conn);
}
#if HAS_XCB_ERRORS
xcb_errors_context_free(x11->errors_context);
#endif
close(x11->drm_fd);
xcb_disconnect(x11->xcb);
free(x11);
}
static struct wlr_renderer *backend_get_renderer(
struct wlr_backend *backend) {
static int backend_get_drm_fd(struct wlr_backend *backend) {
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
return x11->renderer;
return x11->drm_fd;
}
static uint32_t get_buffer_caps(struct wlr_backend *backend) {
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
return (x11->have_dri3 ? WLR_BUFFER_CAP_DMABUF : 0)
| (x11->have_shm ? WLR_BUFFER_CAP_SHM : 0);
}
static const struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_renderer = backend_get_renderer,
.get_drm_fd = backend_get_drm_fd,
.get_buffer_caps = get_buffer_caps,
};
bool wlr_backend_is_x11(struct wlr_backend *backend) {
@ -168,9 +237,165 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
backend_destroy(&x11->backend);
}
static xcb_depth_t *get_depth(xcb_screen_t *screen, uint8_t depth) {
xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(screen);
while (iter.rem > 0) {
if (iter.data->depth == depth) {
return iter.data;
}
xcb_depth_next(&iter);
}
return NULL;
}
static xcb_visualid_t pick_visualid(xcb_depth_t *depth) {
xcb_visualtype_t *visuals = xcb_depth_visuals(depth);
for (int i = 0; i < xcb_depth_visuals_length(depth); i++) {
if (visuals[i]._class == XCB_VISUAL_CLASS_TRUE_COLOR) {
return visuals[i].visual_id;
}
}
return 0;
}
static int query_dri3_drm_fd(struct wlr_x11_backend *x11) {
xcb_dri3_open_cookie_t open_cookie =
xcb_dri3_open(x11->xcb, x11->screen->root, 0);
xcb_dri3_open_reply_t *open_reply =
xcb_dri3_open_reply(x11->xcb, open_cookie, NULL);
if (open_reply == NULL) {
return -1;
}
int *open_fds = xcb_dri3_open_reply_fds(x11->xcb, open_reply);
if (open_fds == NULL) {
free(open_reply);
return -1;
}
assert(open_reply->nfd == 1);
int drm_fd = open_fds[0];
free(open_reply);
int flags = fcntl(drm_fd, F_GETFD);
if (flags < 0) {
close(drm_fd);
return -1;
}
if (fcntl(drm_fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
close(drm_fd);
return -1;
}
if (drmGetNodeTypeFromFd(drm_fd) != DRM_NODE_RENDER) {
char *render_name = drmGetRenderDeviceNameFromFd(drm_fd);
if (render_name == NULL) {
close(drm_fd);
return -1;
}
close(drm_fd);
drm_fd = open(render_name, O_RDWR | O_CLOEXEC);
if (drm_fd < 0) {
free(render_name);
return -1;
}
free(render_name);
}
return drm_fd;
}
static bool query_dri3_modifiers(struct wlr_x11_backend *x11,
const struct wlr_x11_format *format) {
if (x11->dri3_major_version == 1 && x11->dri3_minor_version < 2) {
return true; // GetSupportedModifiers requires DRI3 1.2
}
// Query the root window's supported modifiers, because we only care about
// screen_modifiers for now
xcb_dri3_get_supported_modifiers_cookie_t modifiers_cookie =
xcb_dri3_get_supported_modifiers(x11->xcb, x11->screen->root,
format->depth, format->bpp);
xcb_dri3_get_supported_modifiers_reply_t *modifiers_reply =
xcb_dri3_get_supported_modifiers_reply(x11->xcb, modifiers_cookie,
NULL);
if (!modifiers_reply) {
wlr_log(WLR_ERROR, "Failed to get DMA-BUF modifiers supported by "
"the X11 server for the format 0x%"PRIX32, format->drm);
return false;
}
// If modifiers aren't supported, DRI3 will return an empty list
const uint64_t *modifiers =
xcb_dri3_get_supported_modifiers_screen_modifiers(modifiers_reply);
int modifiers_len =
xcb_dri3_get_supported_modifiers_screen_modifiers_length(modifiers_reply);
for (int i = 0; i < modifiers_len; i++) {
wlr_drm_format_set_add(&x11->dri3_formats, format->drm, modifiers[i]);
}
free(modifiers_reply);
return true;
}
static bool query_formats(struct wlr_x11_backend *x11) {
xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(x11->screen);
while (iter.rem > 0) {
uint8_t depth = iter.data->depth;
const struct wlr_x11_format *format = x11_format_from_depth(depth);
if (format != NULL) {
if (x11->have_shm) {
wlr_drm_format_set_add(&x11->shm_formats, format->drm,
DRM_FORMAT_MOD_INVALID);
}
if (x11->have_dri3) {
// X11 always supports implicit modifiers
wlr_drm_format_set_add(&x11->dri3_formats, format->drm,
DRM_FORMAT_MOD_INVALID);
if (!query_dri3_modifiers(x11, format)) {
return false;
}
}
}
xcb_depth_next(&iter);
}
return true;
}
static void x11_get_argb32(struct wlr_x11_backend *x11) {
xcb_render_query_pict_formats_cookie_t cookie =
xcb_render_query_pict_formats(x11->xcb);
xcb_render_query_pict_formats_reply_t *reply =
xcb_render_query_pict_formats_reply(x11->xcb, cookie, NULL);
if (!reply) {
wlr_log(WLR_ERROR, "Did not get any reply from xcb_render_query_pict_formats");
return;
}
xcb_render_pictforminfo_t *format =
xcb_render_util_find_standard_format(reply, XCB_PICT_STANDARD_ARGB_32);
if (format == NULL) {
wlr_log(WLR_DEBUG, "No ARGB_32 render format");
free(reply);
return;
}
x11->argb32 = format->id;
free(reply);
}
struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
const char *x11_display,
wlr_renderer_create_func_t create_renderer_func) {
const char *x11_display) {
wlr_log(WLR_INFO, "Creating X11 backend");
struct wlr_x11_backend *x11 = calloc(1, sizeof(*x11));
if (!x11) {
return NULL;
@ -180,20 +405,12 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
x11->wl_display = display;
wl_list_init(&x11->outputs);
x11->xlib_conn = XOpenDisplay(x11_display);
if (!x11->xlib_conn) {
wlr_log(WLR_ERROR, "Failed to open X connection");
goto error_x11;
}
x11->xcb = XGetXCBConnection(x11->xlib_conn);
x11->xcb = xcb_connect(x11_display, NULL);
if (!x11->xcb || xcb_connection_has_error(x11->xcb)) {
wlr_log(WLR_ERROR, "Failed to open xcb connection");
goto error_display;
goto error_x11;
}
XSetEventQueueOwner(x11->xlib_conn, XCBOwnsEventQueue);
struct {
const char *name;
xcb_intern_atom_cookie_t cookie;
@ -203,6 +420,7 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
{ .name = "WM_DELETE_WINDOW", .atom = &x11->atoms.wm_delete_window },
{ .name = "_NET_WM_NAME", .atom = &x11->atoms.net_wm_name },
{ .name = "UTF8_STRING", .atom = &x11->atoms.utf8_string },
{ .name = "_VARIABLE_REFRESH", .atom = &x11->atoms.variable_refresh },
};
for (size_t i = 0; i < sizeof(atom) / sizeof(atom[0]); ++i) {
@ -224,6 +442,80 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
const xcb_query_extension_reply_t *ext;
// DRI3 extension
ext = xcb_get_extension_data(x11->xcb, &xcb_dri3_id);
if (ext && ext->present) {
xcb_dri3_query_version_cookie_t dri3_cookie =
xcb_dri3_query_version(x11->xcb, 1, 2);
xcb_dri3_query_version_reply_t *dri3_reply =
xcb_dri3_query_version_reply(x11->xcb, dri3_cookie, NULL);
if (dri3_reply && dri3_reply->major_version >= 1) {
x11->have_dri3 = true;
x11->dri3_major_version = dri3_reply->major_version;
x11->dri3_minor_version = dri3_reply->minor_version;
} else {
wlr_log(WLR_INFO, "X11 does not support required DRI3 version "
"(has %"PRIu32".%"PRIu32", want 1.0)",
dri3_reply->major_version, dri3_reply->minor_version);
}
free(dri3_reply);
} else {
wlr_log(WLR_INFO, "X11 does not support DRI3 extension");
}
// SHM extension
ext = xcb_get_extension_data(x11->xcb, &xcb_shm_id);
if (ext && ext->present) {
xcb_shm_query_version_cookie_t shm_cookie =
xcb_shm_query_version(x11->xcb);
xcb_shm_query_version_reply_t *shm_reply =
xcb_shm_query_version_reply(x11->xcb, shm_cookie, NULL);
if (shm_reply) {
if (shm_reply->major_version >= 1 || shm_reply->minor_version >= 2) {
if (shm_reply->shared_pixmaps) {
x11->have_shm = true;
} else {
wlr_log(WLR_INFO, "X11 does not support shared pixmaps");
}
} else {
wlr_log(WLR_INFO, "X11 does not support required SHM version "
"(has %"PRIu32".%"PRIu32", want 1.2)",
shm_reply->major_version, shm_reply->minor_version);
}
} else {
wlr_log(WLR_INFO, "X11 does not support required SHM version");
}
free(shm_reply);
} else {
wlr_log(WLR_INFO, "X11 does not support SHM extension");
}
// Present extension
ext = xcb_get_extension_data(x11->xcb, &xcb_present_id);
if (!ext || !ext->present) {
wlr_log(WLR_ERROR, "X11 does not support Present extension");
goto error_display;
}
x11->present_opcode = ext->major_opcode;
xcb_present_query_version_cookie_t present_cookie =
xcb_present_query_version(x11->xcb, 1, 2);
xcb_present_query_version_reply_t *present_reply =
xcb_present_query_version_reply(x11->xcb, present_cookie, NULL);
if (!present_reply || present_reply->major_version < 1) {
wlr_log(WLR_ERROR, "X11 does not support required Present version "
"(has %"PRIu32".%"PRIu32", want 1.0)",
present_reply->major_version, present_reply->minor_version);
free(present_reply);
goto error_display;
}
free(present_reply);
// Xfixes extension
ext = xcb_get_extension_data(x11->xcb, &xcb_xfixes_id);
if (!ext || !ext->present) {
wlr_log(WLR_ERROR, "X11 does not support Xfixes extension");
@ -234,14 +526,17 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
xcb_xfixes_query_version(x11->xcb, 4, 0);
xcb_xfixes_query_version_reply_t *fixes_reply =
xcb_xfixes_query_version_reply(x11->xcb, fixes_cookie, NULL);
if (!fixes_reply || fixes_reply->major_version < 4) {
wlr_log(WLR_ERROR, "X11 does not support required Xfixes version");
wlr_log(WLR_ERROR, "X11 does not support required Xfixes version "
"(has %"PRIu32".%"PRIu32", want 4.0)",
fixes_reply->major_version, fixes_reply->minor_version);
free(fixes_reply);
goto error_display;
}
free(fixes_reply);
// Xinput extension
ext = xcb_get_extension_data(x11->xcb, &xcb_input_id);
if (!ext || !ext->present) {
wlr_log(WLR_ERROR, "X11 does not support Xinput extension");
@ -253,9 +548,10 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
xcb_input_xi_query_version(x11->xcb, 2, 0);
xcb_input_xi_query_version_reply_t *xi_reply =
xcb_input_xi_query_version_reply(x11->xcb, xi_cookie, NULL);
if (!xi_reply || xi_reply->major_version < 2) {
wlr_log(WLR_ERROR, "X11 does not support required Xinput version");
wlr_log(WLR_ERROR, "X11 does not support required Xinput version "
"(has %"PRIu32".%"PRIu32", want 2.0)",
xi_reply->major_version, xi_reply->minor_version);
free(xi_reply);
goto error_display;
}
@ -272,28 +568,76 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
wl_event_source_check(x11->event_source);
x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb)).data;
if (!create_renderer_func) {
create_renderer_func = wlr_renderer_autocreate;
}
static EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 0,
EGL_NONE,
};
x11->renderer = create_renderer_func(&x11->egl, EGL_PLATFORM_X11_KHR,
x11->xlib_conn, config_attribs, x11->screen->root_visual);
if (x11->renderer == NULL) {
wlr_log(WLR_ERROR, "Failed to create renderer");
if (!x11->screen) {
wlr_log(WLR_ERROR, "Failed to get X11 screen");
goto error_event;
}
x11->depth = get_depth(x11->screen, 24);
if (!x11->depth) {
wlr_log(WLR_ERROR, "Failed to get 24-bit depth for X11 screen");
goto error_event;
}
x11->visualid = pick_visualid(x11->depth);
if (!x11->visualid) {
wlr_log(WLR_ERROR, "Failed to pick X11 visual");
goto error_event;
}
x11->x11_format = x11_format_from_depth(x11->depth->depth);
if (!x11->x11_format) {
wlr_log(WLR_ERROR, "Unsupported depth %"PRIu8, x11->depth->depth);
goto error_event;
}
x11->colormap = xcb_generate_id(x11->xcb);
xcb_create_colormap(x11->xcb, XCB_COLORMAP_ALLOC_NONE, x11->colormap,
x11->screen->root, x11->visualid);
if (!query_formats(x11)) {
wlr_log(WLR_ERROR, "Failed to query supported DRM formats");
return false;
}
x11->drm_fd = -1;
if (x11->have_dri3) {
// DRI3 may return a render node (Xwayland) or an authenticated primary
// node (plain Glamor).
x11->drm_fd = query_dri3_drm_fd(x11);
if (x11->drm_fd < 0) {
wlr_log(WLR_ERROR, "Failed to query DRI3 DRM FD");
goto error_event;
}
}
// Windows can only display buffers with the depth they were created with
// TODO: look into changing the window's depth at runtime
const struct wlr_drm_format *dri3_format =
wlr_drm_format_set_get(&x11->dri3_formats, x11->x11_format->drm);
if (x11->have_dri3 && dri3_format != NULL) {
wlr_drm_format_set_add(&x11->primary_dri3_formats,
dri3_format->format, DRM_FORMAT_MOD_INVALID);
for (size_t i = 0; i < dri3_format->len; i++) {
wlr_drm_format_set_add(&x11->primary_dri3_formats,
dri3_format->format, dri3_format->modifiers[i]);
}
}
const struct wlr_drm_format *shm_format =
wlr_drm_format_set_get(&x11->shm_formats, x11->x11_format->drm);
if (x11->have_shm && shm_format != NULL) {
wlr_drm_format_set_add(&x11->primary_shm_formats,
shm_format->format, DRM_FORMAT_MOD_INVALID);
}
#if HAS_XCB_ERRORS
if (xcb_errors_context_new(x11->xcb, &x11->errors_context) != 0) {
wlr_log(WLR_ERROR, "Failed to create error context");
return false;
}
#endif
wlr_input_device_init(&x11->keyboard_dev, WLR_INPUT_DEVICE_KEYBOARD,
&input_device_impl, "X11 keyboard", 0, 0);
wlr_keyboard_init(&x11->keyboard, &keyboard_impl);
@ -302,13 +646,87 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
x11->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &x11->display_destroy);
// Create an empty pixmap to be used as the cursor. The
// default GC foreground is 0, and that is what it will be
// filled with.
xcb_pixmap_t blank = xcb_generate_id(x11->xcb);
xcb_create_pixmap(x11->xcb, 1, blank, x11->screen->root, 1, 1);
xcb_gcontext_t gc = xcb_generate_id(x11->xcb);
xcb_create_gc(x11->xcb, gc, blank, 0, NULL);
xcb_rectangle_t rect = { .x = 0, .y = 0, .width = 1, .height = 1 };
xcb_poly_fill_rectangle(x11->xcb, blank, gc, 1, &rect);
x11->transparent_cursor = xcb_generate_id(x11->xcb);
xcb_create_cursor(x11->xcb, x11->transparent_cursor, blank, blank,
0, 0, 0, 0, 0, 0, 0, 0);
xcb_free_gc(x11->xcb, gc);
xcb_free_pixmap(x11->xcb, blank);
x11_get_argb32(x11);
return &x11->backend;
error_event:
wl_event_source_remove(x11->event_source);
error_display:
XCloseDisplay(x11->xlib_conn);
xcb_disconnect(x11->xcb);
error_x11:
free(x11);
return NULL;
}
static void handle_x11_error(struct wlr_x11_backend *x11, xcb_value_error_t *ev) {
#if HAS_XCB_ERRORS
const char *major_name = xcb_errors_get_name_for_major_code(
x11->errors_context, ev->major_opcode);
if (!major_name) {
wlr_log(WLR_DEBUG, "X11 error happened, but could not get major name");
goto log_raw;
}
const char *minor_name = xcb_errors_get_name_for_minor_code(
x11->errors_context, ev->major_opcode, ev->minor_opcode);
const char *extension;
const char *error_name = xcb_errors_get_name_for_error(x11->errors_context,
ev->error_code, &extension);
if (!error_name) {
wlr_log(WLR_DEBUG, "X11 error happened, but could not get error name");
goto log_raw;
}
wlr_log(WLR_ERROR, "X11 error: op %s (%s), code %s (%s), "
"sequence %"PRIu16", value %"PRIu32,
major_name, minor_name ? minor_name : "no minor",
error_name, extension ? extension : "no extension",
ev->sequence, ev->bad_value);
return;
log_raw:
#endif
wlr_log(WLR_ERROR, "X11 error: op %"PRIu8":%"PRIu16", code %"PRIu8", "
"sequence %"PRIu16", value %"PRIu32,
ev->major_opcode, ev->minor_opcode, ev->error_code,
ev->sequence, ev->bad_value);
}
static void handle_x11_unknown_event(struct wlr_x11_backend *x11,
xcb_generic_event_t *ev) {
#if HAS_XCB_ERRORS
const char *extension;
const char *event_name = xcb_errors_get_name_for_xcb_event(
x11->errors_context, ev, &extension);
if (!event_name) {
wlr_log(WLR_DEBUG, "No name for unhandled event: %u",
ev->response_type);
return;
}
wlr_log(WLR_DEBUG, "Unhandled X11 event: %s (%u)", event_name, ev->response_type);
#else
wlr_log(WLR_DEBUG, "Unhandled X11 event: %u", ev->response_type);
#endif
}

View File

@ -4,6 +4,8 @@
#include <linux/input-event-codes.h>
#include <wayland-server-protocol.h>
#include <xcb/xcb.h>
#include <xcb/xfixes.h>
#include <xcb/xinput.h>
@ -11,13 +13,14 @@
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/interfaces/wlr_pointer.h>
#include <wlr/interfaces/wlr_touch.h>
#include <wlr/util/log.h>
#include "backend/x11.h"
#include "util/signal.h"
static void send_key_event(struct wlr_x11_backend *x11, uint32_t key,
enum wlr_key_state st, xcb_timestamp_t time) {
enum wl_keyboard_key_state st, xcb_timestamp_t time) {
struct wlr_event_keyboard_key ev = {
.time_msec = time,
.keycode = key,
@ -36,6 +39,7 @@ static void send_button_event(struct wlr_x11_output *output, uint32_t key,
.state = st,
};
wlr_signal_emit_safe(&output->pointer.events.button, &ev);
wlr_signal_emit_safe(&output->pointer.events.frame, &output->pointer);
}
static void send_axis_event(struct wlr_x11_output *output, int32_t delta,
@ -65,6 +69,54 @@ static void send_pointer_position_event(struct wlr_x11_output *output,
wlr_signal_emit_safe(&output->pointer.events.frame, &output->pointer);
}
static void send_touch_down_event(struct wlr_x11_output *output,
int16_t x, int16_t y, int32_t touch_id, xcb_timestamp_t time) {
struct wlr_event_touch_down ev = {
.device = &output->touch_dev,
.time_msec = time,
.x = (double)x / output->wlr_output.width,
.y = (double)y / output->wlr_output.height,
.touch_id = touch_id,
};
wlr_signal_emit_safe(&output->touch.events.down, &ev);
wlr_signal_emit_safe(&output->touch.events.frame, NULL);
}
static void send_touch_motion_event(struct wlr_x11_output *output,
int16_t x, int16_t y, int32_t touch_id, xcb_timestamp_t time) {
struct wlr_event_touch_motion ev = {
.device = &output->touch_dev,
.time_msec = time,
.x = (double)x / output->wlr_output.width,
.y = (double)y / output->wlr_output.height,
.touch_id = touch_id,
};
wlr_signal_emit_safe(&output->touch.events.motion, &ev);
wlr_signal_emit_safe(&output->touch.events.frame, NULL);
}
static void send_touch_up_event(struct wlr_x11_output *output,
int32_t touch_id, xcb_timestamp_t time) {
struct wlr_event_touch_up ev = {
.device = &output->touch_dev,
.time_msec = time,
.touch_id = touch_id,
};
wlr_signal_emit_safe(&output->touch.events.up, &ev);
wlr_signal_emit_safe(&output->touch.events.frame, NULL);
}
static struct wlr_x11_touchpoint *get_touchpoint_from_x11_touch_id(
struct wlr_x11_output *output, uint32_t id) {
struct wlr_x11_touchpoint *touchpoint;
wl_list_for_each(touchpoint, &output->touchpoints, link) {
if (touchpoint->x11_id == id) {
return touchpoint;
}
}
return NULL;
}
void handle_x11_xinput_event(struct wlr_x11_backend *x11,
xcb_ge_generic_event_t *event) {
struct wlr_x11_output *output;
@ -74,9 +126,13 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
xcb_input_key_press_event_t *ev =
(xcb_input_key_press_event_t *)event;
if (ev->flags & XCB_INPUT_KEY_EVENT_FLAGS_KEY_REPEAT) {
return;
}
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
ev->mods.latched, ev->mods.locked, ev->mods.effective);
send_key_event(x11, ev->detail - 8, WLR_KEY_PRESSED, ev->time);
send_key_event(x11, ev->detail - 8, WL_KEYBOARD_KEY_STATE_PRESSED, ev->time);
x11->time = ev->time;
break;
}
@ -86,7 +142,7 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
ev->mods.latched, ev->mods.locked, ev->mods.effective);
send_key_event(x11, ev->detail - 8, WLR_KEY_RELEASED, ev->time);
send_key_event(x11, ev->detail - 8, WL_KEYBOARD_KEY_STATE_RELEASED, ev->time);
x11->time = ev->time;
break;
}
@ -163,34 +219,68 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
x11->time = ev->time;
break;
}
case XCB_INPUT_ENTER: {
xcb_input_enter_event_t *ev = (xcb_input_enter_event_t *)event;
case XCB_INPUT_TOUCH_BEGIN: {
xcb_input_touch_begin_event_t *ev = (xcb_input_touch_begin_event_t *)event;
output = get_x11_output_from_window_id(x11, ev->event);
if (!output) {
return;
}
if (!output->cursor_hidden) {
xcb_xfixes_hide_cursor(x11->xcb, output->win);
xcb_flush(x11->xcb);
output->cursor_hidden = true;
int32_t id = 0;
if (!wl_list_empty(&output->touchpoints)) {
struct wlr_x11_touchpoint *last_touchpoint = wl_container_of(
output->touchpoints.next, last_touchpoint, link);
id = last_touchpoint->wayland_id + 1;
}
struct wlr_x11_touchpoint *touchpoint = calloc(1, sizeof(struct wlr_x11_touchpoint));
touchpoint->x11_id = ev->detail;
touchpoint->wayland_id = id;
wl_list_init(&touchpoint->link);
wl_list_insert(&output->touchpoints, &touchpoint->link);
send_touch_down_event(output, ev->event_x >> 16,
ev->event_y >> 16, touchpoint->wayland_id, ev->time);
x11->time = ev->time;
break;
}
case XCB_INPUT_LEAVE: {
xcb_input_leave_event_t *ev = (xcb_input_leave_event_t *)event;
case XCB_INPUT_TOUCH_END: {
xcb_input_touch_end_event_t *ev = (xcb_input_touch_end_event_t *)event;
output = get_x11_output_from_window_id(x11, ev->event);
if (!output) {
return;
}
if (output->cursor_hidden) {
xcb_xfixes_show_cursor(x11->xcb, output->win);
xcb_flush(x11->xcb);
output->cursor_hidden = false;
struct wlr_x11_touchpoint *touchpoint = get_touchpoint_from_x11_touch_id(output, ev->detail);
if (!touchpoint) {
return;
}
send_touch_up_event(output, touchpoint->wayland_id, ev->time);
x11->time = ev->time;
wl_list_remove(&touchpoint->link);
free(touchpoint);
break;
}
case XCB_INPUT_TOUCH_UPDATE: {
xcb_input_touch_update_event_t *ev = (xcb_input_touch_update_event_t *)event;
output = get_x11_output_from_window_id(x11, ev->event);
if (!output) {
return;
}
struct wlr_x11_touchpoint *touchpoint = get_touchpoint_from_x11_touch_id(output, ev->detail);
if (!touchpoint) {
return;
}
send_touch_motion_event(output, ev->event_x >> 16,
ev->event_y >> 16, touchpoint->wayland_id, ev->time);
x11->time = ev->time;
break;
}
}
@ -220,6 +310,14 @@ const struct wlr_pointer_impl pointer_impl = {
.destroy = pointer_destroy,
};
static void touch_destroy(struct wlr_touch *wlr_touch) {
// Don't free the touch, it's on the stack
}
const struct wlr_touch_impl touch_impl = {
.destroy = touch_destroy,
};
void update_x11_pointer_position(struct wlr_x11_output *output,
xcb_timestamp_t time) {
struct wlr_x11_backend *x11 = output->x11;

View File

@ -1,13 +1,25 @@
x11_libs = []
x11_required = [
'x11-xcb',
'xcb',
'xcb-xinput',
'xcb-dri3',
'xcb-present',
'xcb-render',
'xcb-renderutil',
'xcb-shm',
'xcb-xfixes',
'xcb-xinput',
]
msg = ['Required for X11 backend support.']
if 'x11' in backends
msg += 'Install "@0@" or disable the X11 backend.'
endif
foreach lib : x11_required
dep = dependency(lib, required: get_option('x11-backend'))
dep = dependency(lib,
required: 'x11' in backends,
not_found_message: '\n'.join(msg).format(lib),
)
if not dep.found()
subdir_done()
endif
@ -15,21 +27,10 @@ foreach lib : x11_required
x11_libs += dep
endforeach
lib_wlr_backend_x11 = static_library(
'wlr_backend_x11',
files(
'backend.c',
'input_device.c',
'output.c',
),
include_directories: wlr_inc,
dependencies: [
wayland_server,
pixman,
xkbcommon,
x11_libs,
],
wlr_files += files(
'backend.c',
'input_device.c',
'output.c',
)
backend_parts += lib_wlr_backend_x11
conf_data.set10('WLR_HAS_X11_BACKEND', true)
wlr_deps += x11_libs
features += { 'x11-backend': true }

View File

@ -3,23 +3,31 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <drm_fourcc.h>
#include <xcb/dri3.h>
#include <xcb/present.h>
#include <xcb/render.h>
#include <xcb/shm.h>
#include <xcb/xcb.h>
#include <xcb/xinput.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/interfaces/wlr_pointer.h>
#include <wlr/interfaces/wlr_touch.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/util/log.h>
#include "backend/x11.h"
#include "util/signal.h"
#include "util/time.h"
static int signal_frame(void *data) {
struct wlr_x11_output *output = data;
wlr_output_send_frame(&output->wlr_output);
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
return 0;
}
static const uint32_t SUPPORTED_OUTPUT_STATE =
WLR_OUTPUT_STATE_BACKEND_OPTIONAL |
WLR_OUTPUT_STATE_BUFFER |
WLR_OUTPUT_STATE_MODE;
static void parse_xcb_setup(struct wlr_output *output,
xcb_connection_t *xcb) {
@ -39,26 +47,11 @@ static struct wlr_x11_output *get_x11_output_from_output(
return (struct wlr_x11_output *)wlr_output;
}
static void output_set_refresh(struct wlr_output *wlr_output, int32_t refresh) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
if (refresh <= 0) {
refresh = X11_DEFAULT_REFRESH;
}
wlr_output_update_custom_mode(&output->wlr_output, wlr_output->width,
wlr_output->height, refresh);
output->frame_delay = 1000000 / refresh;
}
static bool output_set_custom_mode(struct wlr_output *wlr_output,
int32_t width, int32_t height, int32_t refresh) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
struct wlr_x11_backend *x11 = output->x11;
output_set_refresh(&output->wlr_output, refresh);
const uint32_t values[] = { width, height };
xcb_void_cookie_t cookie = xcb_configure_window_checked(
x11->xcb, output->win,
@ -75,53 +68,427 @@ static bool output_set_custom_mode(struct wlr_output *wlr_output,
return true;
}
static void output_transform(struct wlr_output *wlr_output,
enum wl_output_transform transform) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
output->wlr_output.transform = transform;
}
static void destroy_x11_buffer(struct wlr_x11_buffer *buffer);
static void output_destroy(struct wlr_output *wlr_output) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
struct wlr_x11_backend *x11 = output->x11;
pixman_region32_fini(&output->exposed);
wlr_input_device_destroy(&output->pointer_dev);
wlr_input_device_destroy(&output->touch_dev);
struct wlr_x11_buffer *buffer, *buffer_tmp;
wl_list_for_each_safe(buffer, buffer_tmp, &output->buffers, link) {
destroy_x11_buffer(buffer);
}
wl_list_remove(&output->link);
wl_event_source_remove(output->frame_timer);
wlr_egl_destroy_surface(&x11->egl, output->surf);
if (output->cursor.pic != XCB_NONE) {
xcb_render_free_picture(x11->xcb, output->cursor.pic);
}
// A zero event mask deletes the event context
xcb_present_select_input(x11->xcb, output->present_event_id, output->win, 0);
xcb_destroy_window(x11->xcb, output->win);
xcb_flush(x11->xcb);
free(output);
}
static bool output_make_current(struct wlr_output *wlr_output,
int *buffer_age) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
struct wlr_x11_backend *x11 = output->x11;
return wlr_egl_make_current(&x11->egl, output->surf, buffer_age);
}
static bool output_swap_buffers(struct wlr_output *wlr_output,
pixman_region32_t *damage) {
struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output;
struct wlr_x11_backend *x11 = output->x11;
if (!wlr_egl_swap_buffers(&x11->egl, output->surf, damage)) {
static bool output_test(struct wlr_output *wlr_output) {
uint32_t unsupported =
wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE;
if (unsupported != 0) {
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
unsupported);
return false;
}
wlr_output_send_present(wlr_output, NULL);
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM);
}
return true;
}
static void destroy_x11_buffer(struct wlr_x11_buffer *buffer) {
if (!buffer) {
return;
}
wl_list_remove(&buffer->buffer_destroy.link);
wl_list_remove(&buffer->link);
xcb_free_pixmap(buffer->x11->xcb, buffer->pixmap);
free(buffer);
}
static void buffer_handle_buffer_destroy(struct wl_listener *listener,
void *data) {
struct wlr_x11_buffer *buffer =
wl_container_of(listener, buffer, buffer_destroy);
destroy_x11_buffer(buffer);
}
static xcb_pixmap_t import_dmabuf(struct wlr_x11_output *output,
struct wlr_dmabuf_attributes *dmabuf) {
struct wlr_x11_backend *x11 = output->x11;
if (dmabuf->format != x11->x11_format->drm) {
// The pixmap's depth must match the window's depth, otherwise Present
// will throw a Match error
return XCB_PIXMAP_NONE;
}
// xcb closes the FDs after sending them, so we need to dup them here
struct wlr_dmabuf_attributes dup_attrs = {0};
if (!wlr_dmabuf_attributes_copy(&dup_attrs, dmabuf)) {
return XCB_PIXMAP_NONE;
}
const struct wlr_x11_format *x11_fmt = x11->x11_format;
xcb_pixmap_t pixmap = xcb_generate_id(x11->xcb);
if (x11->dri3_major_version > 1 || x11->dri3_minor_version >= 2) {
if (dmabuf->n_planes > 4) {
wlr_dmabuf_attributes_finish(&dup_attrs);
return XCB_PIXMAP_NONE;
}
xcb_dri3_pixmap_from_buffers(x11->xcb, pixmap, output->win,
dmabuf->n_planes, dmabuf->width, dmabuf->height, dmabuf->stride[0],
dmabuf->offset[0], dmabuf->stride[1], dmabuf->offset[1],
dmabuf->stride[2], dmabuf->offset[2], dmabuf->stride[3],
dmabuf->offset[3], x11_fmt->depth, x11_fmt->bpp, dmabuf->modifier,
dup_attrs.fd);
} else {
// PixmapFromBuffers requires DRI3 1.2
if (dmabuf->n_planes != 1
|| dmabuf->modifier != DRM_FORMAT_MOD_INVALID) {
wlr_dmabuf_attributes_finish(&dup_attrs);
return XCB_PIXMAP_NONE;
}
xcb_dri3_pixmap_from_buffer(x11->xcb, pixmap, output->win,
dmabuf->height * dmabuf->stride[0], dmabuf->width, dmabuf->height,
dmabuf->stride[0], x11_fmt->depth, x11_fmt->bpp, dup_attrs.fd[0]);
}
return pixmap;
}
static xcb_pixmap_t import_shm(struct wlr_x11_output *output,
struct wlr_shm_attributes *shm) {
struct wlr_x11_backend *x11 = output->x11;
if (shm->format != x11->x11_format->drm) {
// The pixmap's depth must match the window's depth, otherwise Present
// will throw a Match error
return XCB_PIXMAP_NONE;
}
// xcb closes the FD after sending it
int fd = fcntl(shm->fd, F_DUPFD_CLOEXEC, 0);
if (fd < 0) {
wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed");
return XCB_PIXMAP_NONE;
}
xcb_shm_seg_t seg = xcb_generate_id(x11->xcb);
xcb_shm_attach_fd(x11->xcb, seg, fd, false);
xcb_pixmap_t pixmap = xcb_generate_id(x11->xcb);
xcb_shm_create_pixmap(x11->xcb, pixmap, output->win, shm->width,
shm->height, x11->x11_format->depth, seg, shm->offset);
xcb_shm_detach(x11->xcb, seg);
return pixmap;
}
static struct wlr_x11_buffer *create_x11_buffer(struct wlr_x11_output *output,
struct wlr_buffer *wlr_buffer) {
struct wlr_x11_backend *x11 = output->x11;
xcb_pixmap_t pixmap = XCB_PIXMAP_NONE;
struct wlr_dmabuf_attributes dmabuf_attrs;
struct wlr_shm_attributes shm_attrs;
if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf_attrs)) {
pixmap = import_dmabuf(output, &dmabuf_attrs);
} else if (wlr_buffer_get_shm(wlr_buffer, &shm_attrs)) {
pixmap = import_shm(output, &shm_attrs);
}
if (pixmap == XCB_PIXMAP_NONE) {
return NULL;
}
struct wlr_x11_buffer *buffer = calloc(1, sizeof(struct wlr_x11_buffer));
if (!buffer) {
xcb_free_pixmap(x11->xcb, pixmap);
return NULL;
}
buffer->buffer = wlr_buffer_lock(wlr_buffer);
buffer->pixmap = pixmap;
buffer->x11 = x11;
wl_list_insert(&output->buffers, &buffer->link);
buffer->buffer_destroy.notify = buffer_handle_buffer_destroy;
wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy);
return buffer;
}
static struct wlr_x11_buffer *get_or_create_x11_buffer(
struct wlr_x11_output *output, struct wlr_buffer *wlr_buffer) {
struct wlr_x11_buffer *buffer;
wl_list_for_each(buffer, &output->buffers, link) {
if (buffer->buffer == wlr_buffer) {
wlr_buffer_lock(buffer->buffer);
return buffer;
}
}
return create_x11_buffer(output, wlr_buffer);
}
static bool output_commit_buffer(struct wlr_x11_output *output) {
struct wlr_x11_backend *x11 = output->x11;
struct wlr_buffer *buffer = output->wlr_output.pending.buffer;
struct wlr_x11_buffer *x11_buffer =
get_or_create_x11_buffer(output, buffer);
if (!x11_buffer) {
goto error;
}
xcb_xfixes_region_t region = XCB_NONE;
if (output->wlr_output.pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
pixman_region32_union(&output->exposed, &output->exposed, &output->wlr_output.pending.damage);
int rects_len = 0;
pixman_box32_t *rects = pixman_region32_rectangles(&output->exposed, &rects_len);
xcb_rectangle_t *xcb_rects = calloc(rects_len, sizeof(xcb_rectangle_t));
if (!xcb_rects) {
goto error;
}
for (int i = 0; i < rects_len; i++) {
pixman_box32_t *box = &rects[i];
xcb_rects[i] = (struct xcb_rectangle_t){
.x = box->x1,
.y = box->y1,
.width = box->x2 - box->x1,
.height = box->y2 - box->y1,
};
}
region = xcb_generate_id(x11->xcb);
xcb_xfixes_create_region(x11->xcb, region, rects_len, xcb_rects);
free(xcb_rects);
}
pixman_region32_clear(&output->exposed);
uint32_t serial = output->wlr_output.commit_seq;
uint32_t options = 0;
uint64_t target_msc = output->last_msc ? output->last_msc + 1 : 0;
xcb_present_pixmap(x11->xcb, output->win, x11_buffer->pixmap, serial,
0, region, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, options, target_msc,
0, 0, 0, NULL);
if (region != XCB_NONE) {
xcb_xfixes_destroy_region(x11->xcb, region);
}
return true;
error:
destroy_x11_buffer(x11_buffer);
return false;
}
static bool output_commit(struct wlr_output *wlr_output) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
struct wlr_x11_backend *x11 = output->x11;
if (!output_test(wlr_output)) {
return false;
}
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
if (!output_set_custom_mode(wlr_output,
wlr_output->pending.custom_mode.width,
wlr_output->pending.custom_mode.height,
wlr_output->pending.custom_mode.refresh)) {
return false;
}
}
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED &&
x11->atoms.variable_refresh != XCB_ATOM_NONE) {
if (wlr_output->pending.adaptive_sync_enabled) {
uint32_t enabled = 1;
xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win,
x11->atoms.variable_refresh, XCB_ATOM_CARDINAL, 32, 1,
&enabled);
wlr_output->adaptive_sync_status = WLR_OUTPUT_ADAPTIVE_SYNC_UNKNOWN;
} else {
xcb_delete_property(x11->xcb, output->win,
x11->atoms.variable_refresh);
wlr_output->adaptive_sync_status = WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED;
}
}
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
if (!output_commit_buffer(output)) {
return false;
}
}
xcb_flush(x11->xcb);
return true;
}
static void update_x11_output_cursor(struct wlr_x11_output *output,
int32_t hotspot_x, int32_t hotspot_y) {
struct wlr_x11_backend *x11 = output->x11;
xcb_cursor_t cursor = x11->transparent_cursor;
if (output->cursor.pic != XCB_NONE) {
cursor = xcb_generate_id(x11->xcb);
xcb_render_create_cursor(x11->xcb, cursor, output->cursor.pic,
hotspot_x, hotspot_y);
}
uint32_t values[] = {cursor};
xcb_change_window_attributes(x11->xcb, output->win,
XCB_CW_CURSOR, values);
xcb_flush(x11->xcb);
if (cursor != x11->transparent_cursor) {
xcb_free_cursor(x11->xcb, cursor);
}
}
static bool output_cursor_to_picture(struct wlr_x11_output *output,
struct wlr_buffer *buffer) {
struct wlr_x11_backend *x11 = output->x11;
struct wlr_renderer *renderer = output->wlr_output.renderer;
if (output->cursor.pic != XCB_NONE) {
xcb_render_free_picture(x11->xcb, output->cursor.pic);
}
output->cursor.pic = XCB_NONE;
if (buffer == NULL) {
return true;
}
int depth = 32;
int stride = buffer->width * 4;
uint8_t *data = malloc(buffer->height * stride);
if (data == NULL) {
return false;
}
if (!wlr_renderer_begin_with_buffer(renderer, buffer)) {
free(data);
return false;
}
bool result = wlr_renderer_read_pixels(
renderer, DRM_FORMAT_ARGB8888, NULL,
stride, buffer->width, buffer->height, 0, 0, 0, 0,
data);
wlr_renderer_end(renderer);
if (!result) {
free(data);
return false;
}
xcb_pixmap_t pix = xcb_generate_id(x11->xcb);
xcb_create_pixmap(x11->xcb, depth, pix, output->win,
buffer->width, buffer->height);
output->cursor.pic = xcb_generate_id(x11->xcb);
xcb_render_create_picture(x11->xcb, output->cursor.pic,
pix, x11->argb32, 0, 0);
xcb_gcontext_t gc = xcb_generate_id(x11->xcb);
xcb_create_gc(x11->xcb, gc, pix, 0, NULL);
xcb_put_image(x11->xcb, XCB_IMAGE_FORMAT_Z_PIXMAP,
pix, gc, buffer->width, buffer->height, 0, 0, 0, depth,
stride * buffer->height * sizeof(uint8_t), data);
free(data);
xcb_free_gc(x11->xcb, gc);
xcb_free_pixmap(x11->xcb, pix);
return true;
}
static bool output_set_cursor(struct wlr_output *wlr_output,
struct wlr_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
struct wlr_x11_backend *x11 = output->x11;
if (x11->argb32 == XCB_NONE) {
return false;
}
if (buffer != NULL) {
if (hotspot_x < 0) {
hotspot_x = 0;
}
if (hotspot_x > buffer->width) {
hotspot_x = buffer->width;
}
if (hotspot_y < 0) {
hotspot_y = 0;
}
if (hotspot_y > buffer->height) {
hotspot_y = buffer->height;
}
}
bool success = output_cursor_to_picture(output, buffer);
update_x11_output_cursor(output, hotspot_x, hotspot_y);
return success;
}
static bool output_move_cursor(struct wlr_output *_output, int x, int y) {
// TODO: only return true if x == current x and y == current y
return true;
}
static const struct wlr_drm_format_set *output_get_primary_formats(
struct wlr_output *wlr_output, uint32_t buffer_caps) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
struct wlr_x11_backend *x11 = output->x11;
if (x11->have_dri3 && (buffer_caps & WLR_BUFFER_CAP_DMABUF)) {
return &output->x11->primary_dri3_formats;
} else if (x11->have_shm && (buffer_caps & WLR_BUFFER_CAP_SHM)) {
return &output->x11->primary_shm_formats;
}
return NULL;
}
static const struct wlr_output_impl output_impl = {
.set_custom_mode = output_set_custom_mode,
.transform = output_transform,
.destroy = output_destroy,
.make_current = output_make_current,
.swap_buffers = output_swap_buffers,
.test = output_test,
.commit = output_commit,
.set_cursor = output_set_cursor,
.move_cursor = output_move_cursor,
.get_primary_formats = output_get_primary_formats,
};
struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
@ -137,27 +504,39 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
return NULL;
}
output->x11 = x11;
wl_list_init(&output->buffers);
pixman_region32_init(&output->exposed);
struct wlr_output *wlr_output = &output->wlr_output;
wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display);
wlr_output->width = 1024;
wlr_output->height = 768;
wlr_output_update_custom_mode(wlr_output, 1024, 768, 0);
output_set_refresh(&output->wlr_output, 0);
char name[64];
snprintf(name, sizeof(name), "X11-%zu", ++x11->last_output_num);
wlr_output_set_name(wlr_output, name);
snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%d",
wl_list_length(&x11->outputs) + 1);
parse_xcb_setup(wlr_output, x11->xcb);
uint32_t mask = XCB_CW_EVENT_MASK;
char description[128];
snprintf(description, sizeof(description),
"X11 output %zu", x11->last_output_num);
wlr_output_set_description(wlr_output, description);
// The X11 protocol requires us to set a colormap and border pixel if the
// depth doesn't match the root window's
uint32_t mask = XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK |
XCB_CW_COLORMAP | XCB_CW_CURSOR;
uint32_t values[] = {
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY
0,
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY,
x11->colormap,
x11->transparent_cursor,
};
output->win = xcb_generate_id(x11->xcb);
xcb_create_window(x11->xcb, XCB_COPY_FROM_PARENT, output->win,
x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 1,
XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->screen->root_visual, mask, values);
xcb_create_window(x11->xcb, x11->depth->depth, output->win,
x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->visualid, mask, values);
struct {
xcb_input_event_mask_t head;
@ -169,17 +548,17 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS |
XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE |
XCB_INPUT_XI_EVENT_MASK_MOTION |
XCB_INPUT_XI_EVENT_MASK_ENTER |
XCB_INPUT_XI_EVENT_MASK_LEAVE,
XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN |
XCB_INPUT_XI_EVENT_MASK_TOUCH_END |
XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE,
};
xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head);
output->surf = wlr_egl_create_surface(&x11->egl, &output->win);
if (!output->surf) {
wlr_log(WLR_ERROR, "Failed to create EGL surface");
free(output);
return NULL;
}
uint32_t present_mask = XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY |
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY;
output->present_event_id = xcb_generate_id(x11->xcb);
xcb_present_select_input(x11->xcb, output->present_event_id, output->win,
present_mask);
xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win,
x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1,
@ -190,12 +569,8 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
xcb_map_window(x11->xcb, output->win);
xcb_flush(x11->xcb);
struct wl_event_loop *ev = wl_display_get_event_loop(x11->wl_display);
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);
wl_list_insert(&x11->outputs, &output->link);
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
wlr_output_update_enabled(wlr_output, true);
wlr_input_device_init(&output->pointer_dev, WLR_INPUT_DEVICE_POINTER,
@ -204,8 +579,19 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
output->pointer_dev.pointer = &output->pointer;
output->pointer_dev.output_name = strdup(wlr_output->name);
wlr_input_device_init(&output->touch_dev, WLR_INPUT_DEVICE_TOUCH,
&input_device_impl, "X11 touch", 0, 0);
wlr_touch_init(&output->touch, &touch_impl);
output->touch_dev.touch = &output->touch;
output->touch_dev.output_name = strdup(wlr_output->name);
wl_list_init(&output->touchpoints);
wlr_signal_emit_safe(&x11->backend.events.new_output, wlr_output);
wlr_signal_emit_safe(&x11->backend.events.new_input, &output->pointer_dev);
wlr_signal_emit_safe(&x11->backend.events.new_input, &output->touch_dev);
// Start the rendering loop by requesting the compositor to render a frame
wlr_output_schedule_frame(wlr_output);
return wlr_output;
}
@ -213,17 +599,18 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
void handle_x11_configure_notify(struct wlr_x11_output *output,
xcb_configure_notify_event_t *ev) {
// ignore events that set an invalid size:
if (ev->width > 0 && ev->height > 0) {
wlr_output_update_custom_mode(&output->wlr_output, ev->width,
ev->height, output->wlr_output.refresh);
// Move the pointer to its new location
update_x11_pointer_position(output, output->x11->time);
} else {
if (ev->width == 0 || ev->height == 0) {
wlr_log(WLR_DEBUG,
"Ignoring X11 configure event for height=%d, width=%d",
ev->width, ev->height);
return;
}
wlr_output_update_custom_mode(&output->wlr_output, ev->width,
ev->height, 0);
// Move the pointer to its new location
update_x11_pointer_position(output, output->x11->time);
}
bool wlr_output_is_x11(struct wlr_output *wlr_output) {
@ -245,3 +632,76 @@ void wlr_x11_output_set_title(struct wlr_output *output, const char *title) {
x11_output->x11->atoms.net_wm_name, x11_output->x11->atoms.utf8_string, 8,
strlen(title), title);
}
static struct wlr_x11_buffer *get_x11_buffer(struct wlr_x11_output *output,
xcb_pixmap_t pixmap) {
struct wlr_x11_buffer *buffer;
wl_list_for_each(buffer, &output->buffers, link) {
if (buffer->pixmap == pixmap) {
return buffer;
}
}
return NULL;
}
void handle_x11_present_event(struct wlr_x11_backend *x11,
xcb_ge_generic_event_t *event) {
struct wlr_x11_output *output;
switch (event->event_type) {
case XCB_PRESENT_EVENT_IDLE_NOTIFY:;
xcb_present_idle_notify_event_t *idle_notify =
(xcb_present_idle_notify_event_t *)event;
output = get_x11_output_from_window_id(x11, idle_notify->window);
if (!output) {
wlr_log(WLR_DEBUG, "Got PresentIdleNotify event for unknown window");
return;
}
struct wlr_x11_buffer *buffer =
get_x11_buffer(output, idle_notify->pixmap);
if (!buffer) {
wlr_log(WLR_DEBUG, "Got PresentIdleNotify event for unknown buffer");
return;
}
wlr_buffer_unlock(buffer->buffer); // may destroy buffer
break;
case XCB_PRESENT_COMPLETE_NOTIFY:;
xcb_present_complete_notify_event_t *complete_notify =
(xcb_present_complete_notify_event_t *)event;
output = get_x11_output_from_window_id(x11, complete_notify->window);
if (!output) {
wlr_log(WLR_DEBUG, "Got PresentCompleteNotify event for unknown window");
return;
}
output->last_msc = complete_notify->msc;
struct timespec t;
timespec_from_nsec(&t, complete_notify->ust * 1000);
uint32_t flags = 0;
if (complete_notify->mode == XCB_PRESENT_COMPLETE_MODE_FLIP) {
flags |= WLR_OUTPUT_PRESENT_ZERO_COPY;
}
bool presented = complete_notify->mode != XCB_PRESENT_COMPLETE_MODE_SKIP;
struct wlr_output_event_present present_event = {
.output = &output->wlr_output,
.commit_seq = complete_notify->serial,
.presented = presented,
.when = &t,
.seq = complete_notify->msc,
.flags = flags,
};
wlr_output_send_present(&output->wlr_output, &present_event);
wlr_output_send_frame(&output->wlr_output);
break;
default:
wlr_log(WLR_DEBUG, "Unhandled Present event %"PRIu16, event->event_type);
}
}

View File

@ -1,34 +1,54 @@
wlroots reads these environment variables
wlroots specific
----------------
# wlroots specific
* *WLR_BACKENDS*: comma-separated list of backends to use (available backends:
libinput, drm, wayland, x11, headless)
* *WLR_NO_HARDWARE_CURSORS*: set to 1 to use software cursors instead of
hardware cursors
* *WLR_XWAYLAND*: specifies the path to an Xwayland binary to be used (instead
of following shell search semantics for "Xwayland")
* *WLR_RENDERER*: forces the creation of a specified renderer (available
renderers: gles2, pixman, vulkan)
* *WLR_RENDER_DRM_DEVICE*: specifies the DRM node to use for
hardware-accelerated renderers.
## DRM backend
* *WLR_DRM_DEVICES*: specifies the DRM devices (as a colon separated list)
instead of auto probing them. The first existing device in this list is
considered the primary DRM device.
* *WLR_DRM_NO_ATOMIC*: set to 1 to use legacy DRM interface instead of atomic
mode setting
* *WLR_DRM_NO_ATOMIC_GAMMA*: set to 1 to use legacy DRM interface for gamma
control instead of the atomic interface
* *WLR_LIBINPUT_NO_DEVICES*: set to 1 to not fail without any input devices
* *WLR_BACKENDS*: comma-separated list of backends to use (available backends:
wayland, x11, headless, noop)
* *WLR_WL_OUTPUTS*: when using the wayland backend specifies the number of outputs
* *WLR_X11_OUTPUTS*: when using the X11 backend specifies the number of outputs
* *WLR_DRM_NO_MODIFIERS*: set to 1 to always allocate planes without modifiers,
this can fix certain modeset failures because of bandwidth restrictions.
## Headless backend
* *WLR_HEADLESS_OUTPUTS*: when using the headless backend specifies the number
of outputs
* *WLR_NO_HARDWARE_CURSORS*: set to 1 to use software cursors instead of
hardware cursors
* *WLR_SESSION*: specifies the wlr\_session to be used (available sessions:
logind/systemd, direct)
rootston specific
------------------
* *XKB_DEFAULT_RULES*, *XKB_DEFAULT_MODEL*, *XKB_DEFAULT_LAYOUT*,
*XKB_DEFAULT_VARIANT*, *XKB_DEFAULT_OPTIONS*: xkb setup
## libinput backend
generic
-------
* *DISPLAY*: if set probe X11 backend in *wlr_backend_autocreate*
* *WAYLAND_DISPLAY*, *_WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland
backend in *wlr_backend_autocreate*
* *WLR_LIBINPUT_NO_DEVICES*: set to 1 to not fail without any input devices
## Wayland backend
* *WLR_WL_OUTPUTS*: when using the wayland backend specifies the number of outputs
## X11 backend
* *WLR_X11_OUTPUTS*: when using the X11 backend specifies the number of outputs
## gles2 renderer
* *WLR_RENDERER_ALLOW_SOFTWARE*: allows the gles2 renderer to use software
rendering
# Generic
* *DISPLAY*: if set probe X11 backend in `wlr_backend_autocreate`
* *WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland backend in
`wlr_backend_autocreate`
* *XCURSOR_PATH*: directory where xcursors are located
* *XDG_SESSION_ID*: if set, session ID used by the logind session

View File

@ -205,10 +205,11 @@ static void remove_output(struct wayland_output *out) {
}
static struct wayland_output *find_output(struct capture_context *ctx,
struct wl_output *out, uint32_t id) {
struct wl_output *out, int id) {
struct wayland_output *output, *tmp;
wl_list_for_each_safe(output, tmp, &ctx->output_list, link) {
if ((output->output == out) || (output->id == id)) {
if (output->output == out || (id >= 0 && output->id == (uint32_t)id)
|| id == -1) {
return output;
}
}
@ -461,7 +462,7 @@ static void register_cb(struct capture_context *ctx) {
&frame_listener, ctx);
}
void *vid_encode_thread(void *arg) {
static void *vid_encode_thread(void *arg) {
int err = 0;
struct capture_context *ctx = arg;
@ -489,26 +490,27 @@ void *vid_encode_thread(void *arg) {
}
while (1) {
AVPacket pkt;
av_init_packet(&pkt);
int ret = avcodec_receive_packet(ctx->avctx, &pkt);
AVPacket *pkt = av_packet_alloc();
int ret = avcodec_receive_packet(ctx->avctx, pkt);
if (ret == AVERROR(EAGAIN)) {
av_packet_free(&pkt);
break;
} else if (ret == AVERROR_EOF) {
av_log(ctx, AV_LOG_INFO, "Encoder flushed!\n");
av_packet_free(&pkt);
goto end;
} else if (ret) {
av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n",
av_err2str(ret));
av_packet_free(&pkt);
err = ret;
goto end;
}
pkt.stream_index = 0;
err = av_interleaved_write_frame(ctx->avf, &pkt);
pkt->stream_index = 0;
err = av_interleaved_write_frame(ctx->avf, pkt);
av_packet_unref(&pkt);
av_packet_free(&pkt);
if (err) {
av_log(ctx, AV_LOG_ERROR, "Writing packet fail: %s!\n",
@ -686,7 +688,7 @@ static int init_encoding(struct capture_context *ctx) {
struct capture_context *q_ctx = NULL;
void on_quit_signal(int signo) {
static void on_quit_signal(int signo) {
printf("\r");
av_log(q_ctx, AV_LOG_WARNING, "Quitting!\n");
q_ctx->quit = true;
@ -752,8 +754,11 @@ static int init(struct capture_context *ctx) {
ctx->registry = wl_display_get_registry(ctx->display);
wl_registry_add_listener(ctx->registry, &registry_listener, ctx);
// First roundtrip to fetch globals
wl_display_roundtrip(ctx->display);
// Second roundtrip to fetch wl_output information
wl_display_roundtrip(ctx->display);
wl_display_dispatch(ctx->display);
if (!ctx->export_manager) {
av_log(ctx, AV_LOG_ERROR, "Compositor doesn't support %s!\n",
@ -766,16 +771,77 @@ static int init(struct capture_context *ctx) {
static void uninit(struct capture_context *ctx);
static const char usage[] = "usage: dmabuf-capture [options...] <destination file path>\n"
" -o <output ID>\n"
" -t <hardware device type>\n"
" -d <device path>\n"
" -e <encoder>\n"
" -f <pixel format>\n"
" -r <bitrate in Mbps>\n"
"\n"
"Example:\n"
" dmabuf-capture -o 32 -t vaapi -d /dev/dri/renderD129 \\\n"
" -e libx264 -f nv12 -r 12 recording.mkv\n";
int main(int argc, char *argv[]) {
int err;
struct capture_context ctx = { 0 };
ctx.class = &((AVClass) {
struct capture_context ctx = {
.hardware_device = "/dev/dri/renderD128",
.encoder_name = "libx264",
.out_bitrate = 12,
};
int output_id = -1;
const char *hw_device_type = "vaapi";
const char *software_format = "nv12";
int opt;
while ((opt = getopt(argc, argv, "ho:t:d:e:f:r:")) != -1) {
char *end;
switch (opt) {
case 'o':
output_id = strtol(optarg, &end, 10);
if (optarg[0] == '\0' || end[0] != '\0') {
fprintf(stderr, "Output ID is not an integer\n");
return 1;
}
break;
case 't':
hw_device_type = optarg;
break;
case 'd':
ctx.hardware_device = optarg;
break;
case 'e':
ctx.encoder_name = optarg;
break;
case 'f':
software_format = optarg;
break;
case 'r':
ctx.out_bitrate = strtof(optarg, &end);
if (optarg[0] == '\0' || end[0] != '\0') {
fprintf(stderr, "Bitrate is not a floating-pointer number\n");
return 1;
}
break;
default:
fprintf(stderr, "%s", usage);
return 1;
}
}
if (optind >= argc) {
fprintf(stderr, "Missing destination file argument\n");
fprintf(stderr, "%s", usage);
return 1;
}
ctx.out_filename = argv[optind];
ctx.class = &((AVClass){
.class_name = "dmabuf-capture",
.item_name = av_default_item_name,
.version = LIBAVUTIL_VERSION_INT,
});
err = init(&ctx);
int err = init(&ctx);
if (err) {
goto end;
}
@ -786,31 +852,16 @@ int main(int argc, char *argv[]) {
o->make, o->model, o->id);
}
if (argc != 8) {
printf("Invalid number of arguments! Usage and example:\n"
"./dmabuf-capture <source id> <hardware device type> <device> "
"<encoder name> <pixel format> <bitrate in Mbps> <file path>\n"
"./dmabuf-capture 0 vaapi /dev/dri/renderD129 libx264 nv12 12 "
"dmabuf_recording_01.mkv\n");
return 1;
}
const int o_id = strtol(argv[1], NULL, 10);
o = find_output(&ctx, NULL, o_id);
o = find_output(&ctx, NULL, output_id);
if (!o) {
printf("Unable to find output with ID %i!\n", o_id);
printf("Unable to find output with ID %d\n", output_id);
return 1;
}
ctx.target_output = o->output;
ctx.with_cursor = true;
ctx.hw_device_type = av_hwdevice_find_type_by_name(argv[2]);
ctx.hardware_device = argv[3];
ctx.encoder_name = argv[4];
ctx.software_format = av_get_pix_fmt(argv[5]);
ctx.out_bitrate = strtof(argv[6], NULL);
ctx.out_filename = argv[7];
ctx.hw_device_type = av_hwdevice_find_type_by_name(hw_device_type);
ctx.software_format = av_get_pix_fmt(software_format);
av_dict_set(&ctx.encoder_opts, "preset", "veryfast", 0);

122
examples/egl_common.c Normal file
View File

@ -0,0 +1,122 @@
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <wayland-client.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "egl_common.h"
EGLDisplay egl_display;
EGLConfig egl_config;
EGLContext egl_context;
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;
PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT;
const EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 1,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE,
};
const EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE,
};
bool egl_init(struct wl_display *display) {
const char *client_exts_str =
eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
if (client_exts_str == NULL) {
if (eglGetError() == EGL_BAD_DISPLAY) {
fprintf(stderr, "EGL_EXT_client_extensions not supported\n");
} else {
fprintf(stderr, "Failed to query EGL client extensions\n");
}
return false;
}
if (!strstr(client_exts_str, "EGL_EXT_platform_base")) {
fprintf(stderr, "EGL_EXT_platform_base not supported\n");
return false;
}
if (!strstr(client_exts_str, "EGL_EXT_platform_wayland")) {
fprintf(stderr, "EGL_EXT_platform_wayland not supported\n");
return false;
}
eglGetPlatformDisplayEXT =
(void *)eglGetProcAddress("eglGetPlatformDisplayEXT");
if (eglGetPlatformDisplayEXT == NULL) {
fprintf(stderr, "Failed to get eglGetPlatformDisplayEXT\n");
return false;
}
eglCreatePlatformWindowSurfaceEXT =
(void *)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
if (eglCreatePlatformWindowSurfaceEXT == NULL) {
fprintf(stderr, "Failed to get eglCreatePlatformWindowSurfaceEXT\n");
return false;
}
egl_display =
eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT,
display, NULL);
if (egl_display == EGL_NO_DISPLAY) {
fprintf(stderr, "Failed to create EGL display\n");
goto error;
}
if (eglInitialize(egl_display, NULL, NULL) == EGL_FALSE) {
fprintf(stderr, "Failed to initialize EGL\n");
goto error;
}
EGLint matched = 0;
if (!eglChooseConfig(egl_display, config_attribs,
&egl_config, 1, &matched)) {
fprintf(stderr, "eglChooseConfig failed\n");
goto error;
}
if (matched == 0) {
fprintf(stderr, "Failed to match an EGL config\n");
goto error;
}
egl_context =
eglCreateContext(egl_display, egl_config,
EGL_NO_CONTEXT, context_attribs);
if (egl_context == EGL_NO_CONTEXT) {
fprintf(stderr, "Failed to create EGL context\n");
goto error;
}
return true;
error:
eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE,
EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (egl_display) {
eglTerminate(egl_display);
}
eglReleaseThread();
return false;
}
void egl_finish(void) {
eglMakeCurrent(egl_display, EGL_NO_SURFACE,
EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(egl_display, egl_context);
eglTerminate(egl_display);
eglReleaseThread();
}

19
examples/egl_common.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef _EGL_COMMON_H
#define _EGL_COMMON_H
#endif
#include <stdbool.h>
#include <wayland-client.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
extern EGLDisplay egl_display;
extern EGLConfig egl_config;
extern EGLContext egl_context;
extern PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT;
bool egl_init(struct wl_display *display);
void egl_finish(void);

View File

@ -7,39 +7,48 @@
#include <wayland-client.h>
#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
/**
* Usage:
* 1. foreign-toplevel
* Prints a list of opened toplevels
* 2. foreign-toplevel -f <id>
* Focus the toplevel with the given id
* 3. foreign-toplevel -a <id>
* Maximize the toplevel with the given id
* 4. foreign-toplevel -u <id>
* Unmaximize the toplevel with the given id
* 5. foreign-toplevel -i <id>
* Minimize the toplevel with the given id
* 6. foreign-toplevel -r <id>
* Restore(unminimize) the toplevel with the given id
* 7. foreign-toplevel -c <id>
* Close the toplevel with the given id
* 8. foreign-toplevel -m
* Continuously print changes to the list of opened toplevels.
* Can be used together with some of the previous options.
*/
#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION 3
static void print_help(void) {
static const char usage[] =
"Usage: foreign-toplevel [OPTIONS] ...\n"
"Manage and view information about toplevel windows.\n"
"\n"
" -f <id> focus\n"
" -s <id> fullscreen\n"
" -o <output_id> select output for fullscreen toplevel to appear on. Use this\n"
" option with -s. View available outputs with wayland-info.\n"
" -S <id> unfullscreen\n"
" -a <id> maximize\n"
" -u <id> unmaximize\n"
" -i <id> minimize\n"
" -r <id> restore(unminimize)\n"
" -c <id> close\n"
" -m continuously print changes to the list of opened toplevels\n"
" Can be used together with some of the previous options.\n"
" -h print help message and quit\n";
fprintf(stderr, "%s", usage);
}
enum toplevel_state_field {
TOPLEVEL_STATE_MAXIMIZED = 1,
TOPLEVEL_STATE_MINIMIZED = 2,
TOPLEVEL_STATE_ACTIVATED = 4,
TOPLEVEL_STATE_INVALID = 8,
TOPLEVEL_STATE_MAXIMIZED = (1 << 0),
TOPLEVEL_STATE_MINIMIZED = (1 << 1),
TOPLEVEL_STATE_ACTIVATED = (1 << 2),
TOPLEVEL_STATE_FULLSCREEN = (1 << 3),
TOPLEVEL_STATE_INVALID = (1 << 4),
};
static const uint32_t no_parent = (uint32_t)-1;
static struct wl_output *pref_output = NULL;
static uint32_t pref_output_id = UINT32_MAX;
struct toplevel_state {
char *title;
char *app_id;
uint32_t state;
uint32_t parent_id;
};
static void copy_state(struct toplevel_state *current,
@ -64,6 +73,8 @@ static void copy_state(struct toplevel_state *current,
current->state = pending->state;
}
current->parent_id = pending->parent_id;
pending->state = TOPLEVEL_STATE_INVALID;
}
@ -81,6 +92,12 @@ static void print_toplevel(struct toplevel_v1 *toplevel, bool print_endl) {
toplevel->current.title ?: "(nil)",
toplevel->current.app_id ?: "(nil)");
if (toplevel->current.parent_id != no_parent) {
printf(" parent=%u", toplevel->current.parent_id);
} else {
printf(" no parent");
}
if (print_endl) {
printf("\n");
}
@ -102,12 +119,20 @@ static void print_toplevel_state(struct toplevel_v1 *toplevel, bool print_endl)
} else {
printf(" inactive");
}
if (toplevel->current.state & TOPLEVEL_STATE_FULLSCREEN) {
printf(" fullscreen");
}
if (print_endl) {
printf("\n");
}
}
static void finish_toplevel_state(struct toplevel_state *state) {
free(state->title);
free(state->app_id);
}
static void toplevel_handle_title(void *data,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
const char *title) {
@ -152,6 +177,8 @@ static uint32_t array_to_state(struct wl_array *array) {
state |= TOPLEVEL_STATE_MINIMIZED;
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)
state |= TOPLEVEL_STATE_ACTIVATED;
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN)
state |= TOPLEVEL_STATE_FULLSCREEN;
}
return state;
@ -164,6 +191,28 @@ static void toplevel_handle_state(void *data,
toplevel->pending.state = array_to_state(state);
}
static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager = NULL;
static struct wl_list toplevel_list;
static void toplevel_handle_parent(void *data,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_parent) {
struct toplevel_v1 *toplevel = data;
toplevel->pending.parent_id = no_parent;
if (zwlr_parent) {
struct toplevel_v1 *toplevel_tmp;
wl_list_for_each(toplevel_tmp, &toplevel_list, link) {
if (toplevel_tmp->zwlr_toplevel == zwlr_parent) {
toplevel->pending.parent_id = toplevel_tmp->id;
break;
}
}
if (toplevel->pending.parent_id == no_parent) {
fprintf(stderr, "Cannot find parent toplevel!\n");
}
}
}
static void toplevel_handle_done(void *data,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
struct toplevel_v1 *toplevel = data;
@ -184,6 +233,9 @@ static void toplevel_handle_closed(void *data,
printf(" closed\n");
zwlr_foreign_toplevel_handle_v1_destroy(zwlr_toplevel);
finish_toplevel_state(&toplevel->current);
finish_toplevel_state(&toplevel->pending);
free(toplevel);
}
static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = {
@ -194,11 +246,9 @@ static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = {
.state = toplevel_handle_state,
.done = toplevel_handle_done,
.closed = toplevel_handle_closed,
.parent = toplevel_handle_parent
};
static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager = NULL;
static struct wl_list toplevel_list;
static void toplevel_manager_handle_toplevel(void *data,
struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
@ -210,6 +260,8 @@ static void toplevel_manager_handle_toplevel(void *data,
toplevel->id = global_id++;
toplevel->zwlr_toplevel = zwlr_toplevel;
toplevel->current.parent_id = no_parent;
toplevel->pending.parent_id = no_parent;
wl_list_insert(&toplevel_list, &toplevel->link);
zwlr_foreign_toplevel_handle_v1_add_listener(zwlr_toplevel, &toplevel_impl,
@ -230,13 +282,16 @@ struct wl_seat *seat = NULL;
static void handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version) {
if (strcmp(interface, wl_output_interface.name) == 0) {
struct wl_output *output = wl_registry_bind(registry, name,
&wl_output_interface, version);
wl_output_set_user_data(output, (void*)(size_t)name); // assign some ID to the output
if (name == pref_output_id) {
pref_output = wl_registry_bind(registry, name,
&wl_output_interface, version);
}
} else if (strcmp(interface,
zwlr_foreign_toplevel_manager_v1_interface.name) == 0) {
toplevel_manager = wl_registry_bind(registry, name,
&zwlr_foreign_toplevel_manager_v1_interface, 1);
&zwlr_foreign_toplevel_manager_v1_interface,
WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION);
wl_list_init(&toplevel_list);
zwlr_foreign_toplevel_manager_v1_add_listener(toplevel_manager,
@ -278,11 +333,11 @@ int main(int argc, char **argv) {
int focus_id = -1, close_id = -1;
int maximize_id = -1, unmaximize_id = -1;
int minimize_id = -1, restore_id = -1;
int fullscreen_id = -1, unfullscreen_id = -1;
int one_shot = 1;
int c;
// TODO maybe print usage with -h?
while ((c = getopt(argc, argv, "f:a:u:i:r:c:m")) != -1) {
while ((c = getopt(argc, argv, "f:a:u:i:r:c:s:S:mo:h")) != -1) {
switch (c) {
case 'f':
focus_id = atoi(optarg);
@ -302,9 +357,26 @@ int main(int argc, char **argv) {
case 'c':
close_id = atoi(optarg);
break;
case 's':
fullscreen_id = atoi(optarg);
break;
case 'S':
unfullscreen_id = atoi(optarg);
break;
case 'm':
one_shot = 0;
break;
case 'o':
pref_output_id = atoi(optarg);
break;
case '?':
print_help();
return EXIT_FAILURE;
break;
case 'h':
print_help();
return EXIT_SUCCESS;
break;
}
}
@ -316,7 +388,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (toplevel_manager == NULL) {
@ -342,6 +413,16 @@ int main(int argc, char **argv) {
if ((toplevel = toplevel_by_id_or_bail(restore_id))) {
zwlr_foreign_toplevel_handle_v1_unset_minimized(toplevel->zwlr_toplevel);
}
if ((toplevel = toplevel_by_id_or_bail(fullscreen_id))) {
if (pref_output_id != UINT32_MAX && pref_output == NULL) {
fprintf(stderr, "Could not find output %i\n", pref_output_id);
}
zwlr_foreign_toplevel_handle_v1_set_fullscreen(toplevel->zwlr_toplevel, pref_output);
}
if ((toplevel = toplevel_by_id_or_bail(unfullscreen_id))) {
zwlr_foreign_toplevel_handle_v1_unset_fullscreen(toplevel->zwlr_toplevel);
}
if ((toplevel = toplevel_by_id_or_bail(close_id))) {
zwlr_foreign_toplevel_handle_v1_close(toplevel->zwlr_toplevel);
}

View File

@ -5,16 +5,17 @@
#include <stdlib.h>
#include <time.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_fullscreen_shell_v1.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
/**
@ -25,6 +26,7 @@ struct fullscreen_server {
struct wl_display *wl_display;
struct wlr_backend *backend;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_fullscreen_shell_v1 *fullscreen_shell;
struct wl_listener present_surface;
@ -47,7 +49,6 @@ struct fullscreen_output {
struct render_data {
struct wlr_output *output;
struct wlr_renderer *renderer;
struct tinywl_view *view;
struct timespec *when;
};
@ -89,7 +90,7 @@ static void output_handle_frame(struct wl_listener *listener, void *data) {
int width, height;
wlr_output_effective_resolution(output->wlr_output, &width, &height);
if (!wlr_output_make_current(output->wlr_output, NULL)) {
if (!wlr_output_attach_render(output->wlr_output, NULL)) {
return;
}
@ -108,7 +109,7 @@ static void output_handle_frame(struct wl_listener *listener, void *data) {
}
wlr_renderer_end(renderer);
wlr_output_swap_buffers(output->wlr_output, NULL, NULL);
wlr_output_commit(output->wlr_output);
}
static void output_set_surface(struct fullscreen_output *output,
@ -147,11 +148,7 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) {
wl_container_of(listener, server, new_output);
struct wlr_output *wlr_output = data;
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);
}
wlr_output_init_render(wlr_output, server->allocator, server->renderer);
struct fullscreen_output *output =
calloc(1, sizeof(struct fullscreen_output));
@ -163,6 +160,13 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) {
wlr_output_layout_add_auto(server->output_layout, wlr_output);
wlr_output_create_global(wlr_output);
struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
if (mode != NULL) {
wlr_output_set_mode(wlr_output, mode);
}
wlr_output_commit(wlr_output);
}
static void server_handle_present_surface(struct wl_listener *listener,
@ -202,12 +206,13 @@ int main(int argc, char *argv[]) {
struct fullscreen_server server = {0};
server.wl_display = wl_display_create();
server.backend = wlr_backend_autocreate(server.wl_display, NULL);
server.renderer = wlr_backend_get_renderer(server.backend);
server.backend = wlr_backend_autocreate(server.wl_display);
server.renderer = wlr_renderer_autocreate(server.backend);
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
server.allocator = wlr_allocator_autocreate(server.backend,
server.renderer);
wlr_compositor_create(server.wl_display, server.renderer);
wlr_linux_dmabuf_v1_create(server.wl_display, server.renderer);
server.output_layout = wlr_output_layout_create();

View File

@ -162,7 +162,6 @@ int main(int argc, char *argv[]) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (gamma_control_manager == NULL) {

View File

@ -4,7 +4,7 @@
#include <string.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <wlr/render/egl.h>
#include "egl_common.h"
#include "idle-inhibit-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
@ -27,12 +27,11 @@ static struct xdg_wm_base *wm_base = NULL;
static struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = NULL;
static struct zwp_idle_inhibitor_v1 *idle_inhibitor = NULL;
struct wlr_egl egl;
struct wl_egl_window *egl_window;
struct wlr_egl_surface *egl_surface;
static void draw(void) {
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
float color[] = {1.0, 1.0, 0.0, 1.0};
if (idle_inhibitor) {
@ -43,7 +42,7 @@ static void draw(void) {
glClearColor(color[0], color[1], color[2], 1.0);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(egl.display, egl_surface);
eglSwapBuffers(egl_display, egl_surface);
}
static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
@ -177,7 +176,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (compositor == NULL) {
@ -193,8 +191,7 @@ int main(int argc, char **argv) {
return EXIT_FAILURE;
}
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
WL_SHM_FORMAT_ARGB8888);
egl_init(display);
struct wl_surface *surface = wl_compositor_create_surface(compositor);
struct xdg_surface *xdg_surface =
@ -215,7 +212,8 @@ int main(int argc, char **argv) {
wl_surface_commit(surface);
egl_window = wl_egl_window_create(surface, width, height);
egl_surface = wlr_egl_create_surface(&egl, egl_window);
egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, egl_window, NULL);
wl_display_roundtrip(display);

View File

@ -52,7 +52,7 @@ static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = {
.resumed = handle_resume,
};
int parse_args(int argc, char *argv[]) {
static int parse_args(int argc, char *argv[]) {
int c;
while ((c = getopt(argc, argv, "c:hs:t:")) != -1) {
switch(c)
@ -81,7 +81,7 @@ int parse_args(int argc, char *argv[]) {
return 0;
}
void *simulate_activity(void *data) {
static void *simulate_activity(void *data) {
sleep(simulate_activity_timeout);
fprintf(stdout, "simulate user activity\n");
struct thread_args *arg = data;
@ -90,7 +90,7 @@ void *simulate_activity(void *data) {
return NULL;
}
void *close_program(void *data) {
static void *close_program(void *data) {
sleep(close_timeout);
struct thread_args *arg = data;
org_kde_kwin_idle_timeout_release(arg->timer);
@ -100,7 +100,7 @@ void *close_program(void *data) {
return NULL;
}
void *main_loop(void *data) {
static void *main_loop(void *data) {
struct wl_display *display = data;
while (wl_display_dispatch(display) != -1) {
;
@ -125,9 +125,8 @@ int main(int argc, char *argv[]) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
free(registry);
wl_registry_destroy(registry);
if (idle_manager == NULL) {
fprintf(stderr, "display doesn't support idle protocol\n");

View File

@ -5,7 +5,7 @@
#include <string.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <wlr/render/egl.h>
#include "egl_common.h"
#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
@ -18,12 +18,11 @@ static struct xdg_wm_base *wm_base = NULL;
static struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager = NULL;
static struct zwlr_input_inhibitor_v1 *input_inhibitor = NULL;
struct wlr_egl egl;
struct wl_egl_window *egl_window;
struct wlr_egl_surface *egl_surface;
static void render_frame(void) {
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
glViewport(0, 0, width, height);
if (keys) {
@ -33,7 +32,7 @@ static void render_frame(void) {
}
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(egl.display, egl_surface);
eglSwapBuffers(egl_display, egl_surface);
}
static void xdg_surface_handle_configure(void *data,
@ -150,7 +149,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display);
assert(registry);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
assert(compositor && seat && wm_base && input_inhibit_manager);
@ -158,8 +156,7 @@ int main(int argc, char **argv) {
input_inhibit_manager);
assert(input_inhibitor);
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
WL_SHM_FORMAT_ARGB8888);
egl_init(display);
struct wl_surface *surface = wl_compositor_create_surface(compositor);
assert(surface);
@ -175,7 +172,8 @@ int main(int argc, char **argv) {
wl_surface_commit(surface);
egl_window = wl_egl_window_create(surface, width, height);
egl_surface = wlr_egl_create_surface(&egl, egl_window);
egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, egl_window, NULL);
wl_display_roundtrip(display);

View File

@ -0,0 +1,216 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-client.h>
#include <xkbcommon/xkbcommon.h>
#include "input-method-unstable-v2-client-protocol.h"
static struct wl_display *display = NULL;
static struct wl_seat *seat = NULL;
static struct zwp_input_method_manager_v2 *input_method_manager = NULL;
static struct zwp_input_method_v2 *input_method = NULL;
static struct zwp_input_method_keyboard_grab_v2 *kb_grab = NULL;
static bool active = false;
static bool pending_active = false;
static struct xkb_context *xkb_context = NULL;
static struct xkb_keymap *keymap = NULL;
static struct xkb_state *xkb_state = NULL;
static void handle_key(void *data,
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
printf("handle_key %u %u %u %u\n", serial, time, key, state);
xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, key + 8);
char keysym_name[64];
xkb_keysym_get_name(keysym, keysym_name, sizeof(keysym_name));
printf("xkb translated to %s\n", keysym_name);
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
if (keysym == XKB_KEY_KP_Enter || keysym == XKB_KEY_Return) {
printf("Stopping grab\n");
zwp_input_method_keyboard_grab_v2_release(kb_grab);
kb_grab = NULL;
}
}
}
static void handle_modifiers(void *data,
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group) {
printf("handle_modifiers %u %u %u %u %u\n", serial, mods_depressed,
mods_latched, mods_locked, group);
xkb_state_update_mask(xkb_state, mods_depressed, mods_latched,
mods_locked, 0, 0, group);
}
static void handle_keymap(void *data,
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
uint32_t format, int32_t fd, uint32_t size) {
printf("handle_keymap\n");
char *keymap_string = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
xkb_keymap_unref(keymap);
keymap = xkb_keymap_new_from_string(xkb_context, keymap_string,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
munmap(keymap_string, size);
close(fd);
xkb_state_unref(xkb_state);
xkb_state = xkb_state_new(keymap);
}
static void handle_repeat_info(void *data,
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
int32_t rate, int32_t delay) {
printf("handle_repeat_info %d %d", rate, delay);
}
static const struct zwp_input_method_keyboard_grab_v2_listener grab_listener = {
.key = handle_key,
.modifiers = handle_modifiers,
.keymap = handle_keymap,
.repeat_info = handle_repeat_info,
};
static void handle_activate(void *data,
struct zwp_input_method_v2 *zwp_input_method_v2) {
pending_active = true;
}
static void handle_deactivate(void *data,
struct zwp_input_method_v2 *zwp_input_method_v2) {
pending_active = false;
}
static void handle_unavailable(void *data,
struct zwp_input_method_v2 *zwp_input_method_v2) {
printf("IM unavailable\n");
zwp_input_method_v2_destroy(zwp_input_method_v2);
input_method = NULL;
}
static void im_activate(void *data,
struct zwp_input_method_v2 *id) {
kb_grab = zwp_input_method_v2_grab_keyboard(input_method);
if (kb_grab == NULL) {
fprintf(stderr, "Failed to grab\n");
exit(EXIT_FAILURE);
}
zwp_input_method_keyboard_grab_v2_add_listener(kb_grab, &grab_listener,
NULL);
printf("Started grab, press enter to stop grab\n");
}
static void im_deactivate(void *data,
struct zwp_input_method_v2 *context) {
if (kb_grab != NULL) {
zwp_input_method_keyboard_grab_v2_release(kb_grab);
kb_grab = NULL;
}
}
static void handle_done(void *data,
struct zwp_input_method_v2 *zwp_input_method_v2) {
bool prev_active = active;
if (active != pending_active) {
printf("Now %s\n", pending_active ? "active" : "inactive");
}
active = pending_active;
if (active && !prev_active) {
im_activate(data, zwp_input_method_v2);
} else if (!active && prev_active) {
im_deactivate(data, zwp_input_method_v2);
}
}
static void handle_surrounding_text(void *data,
struct zwp_input_method_v2 *zwp_input_method_v2,
const char *text, uint32_t cursor, uint32_t anchor) {
// not for this test
}
static void handle_text_change_cause(void *data,
struct zwp_input_method_v2 *zwp_input_method_v2,
uint32_t cause) {
// not for this test
}
static void handle_content_type(void *data,
struct zwp_input_method_v2 *zwp_input_method_v2,
uint32_t hint, uint32_t purpose) {
// not for this test
}
static const struct zwp_input_method_v2_listener im_listener = {
.activate = handle_activate,
.deactivate = handle_deactivate,
.surrounding_text = handle_surrounding_text,
.text_change_cause = handle_text_change_cause,
.content_type = handle_content_type,
.done = handle_done,
.unavailable = handle_unavailable,
};
static void handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version) {
if (strcmp(interface, zwp_input_method_manager_v2_interface.name) == 0) {
input_method_manager = wl_registry_bind(registry, name,
&zwp_input_method_manager_v2_interface, 1);
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
}
}
static void handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name) {
// who cares
}
static const struct wl_registry_listener registry_listener = {
.global = handle_global,
.global_remove = handle_global_remove,
};
int main(int argc, char **argv) {
xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (xkb_context == NULL) {
fprintf(stderr, "Failed to create xkb context\n");
return EXIT_FAILURE;
}
display = wl_display_connect(NULL);
if (display == NULL) {
fprintf(stderr, "Failed to create display\n");
return EXIT_FAILURE;
}
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_roundtrip(display);
if (input_method_manager == NULL) {
fprintf(stderr, "input-method not available\n");
return EXIT_FAILURE;
}
if (seat == NULL) {
fprintf(stderr, "seat not available\n");
return EXIT_FAILURE;
}
input_method = zwp_input_method_manager_v2_get_input_method(
input_method_manager, seat);
zwp_input_method_v2_add_listener(input_method, &im_listener, NULL);
wl_display_roundtrip(display);
while (wl_display_dispatch(display) != -1) {
// This space is intentionally left blank
};
return EXIT_SUCCESS;
}

View File

@ -176,7 +176,7 @@ static void timer_arm(unsigned seconds) {
}
}
static void do_updates() {
static void do_updates(void) {
printf("Update %d\n", update_stage);
switch (update_stage) {
case 0:
@ -240,7 +240,7 @@ static void do_updates() {
};
}
static void handle_timer() {
static void handle_timer(void) {
printf("Timer dispatched at %d\n", update_stage);
do_updates();
}
@ -324,7 +324,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (compositor == NULL) {

View File

@ -0,0 +1,261 @@
#include <GLES2/gl2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include "egl_common.h"
#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
#include <linux/input-event-codes.h>
/**
* Usage: keyboard-shortcuts-inhibit
* Creates a xdg-toplevel using the keyboard-shortcuts-inhibit protocol.
* It will be solid green, when it has an keyboard shortcuts inhibitor, and
* solid yellow if it does not.
* Left click with a pointer will toggle this state. (Touch is not supported
* for now).
* The compositor (de-)activating the inhibitor will also toggle state.
* With a compositor supporting the protocol, compositor shortcuts will be
* suspended while the inhibitor is active and the window has focus.
*/
static int width = 500, height = 300;
static struct wl_compositor *compositor = NULL;
static struct wl_seat *seat = NULL;
static struct xdg_wm_base *wm_base = NULL;
static struct zwp_keyboard_shortcuts_inhibit_manager_v1 *
keyboard_shortcuts_inhibit_manager = NULL;
static struct zwp_keyboard_shortcuts_inhibitor_v1 *
keyboard_shortcuts_inhibitor = NULL;
static bool active = false;
struct wl_egl_window *egl_window;
struct wlr_egl_surface *egl_surface;
static void draw(void) {
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
float color[] = {1.0, 1.0, 0.0, 1.0};
if (keyboard_shortcuts_inhibitor) {
color[0] = 0.0;
}
glViewport(0, 0, width, height);
glClearColor(color[0], color[1], color[2], 1.0);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(egl_display, egl_surface);
}
static void keyboard_shortcuts_inhibit_handle_active(void *data,
struct zwp_keyboard_shortcuts_inhibitor_v1 *
zwp_keyboard_shortcuts_inhibitor_v1) {
active = 1;
draw();
}
static void keyboard_shortcuts_inhibit_handle_inactive(void *data,
struct zwp_keyboard_shortcuts_inhibitor_v1 *
zwp_keyboard_shortcuts_inhibitor_v1) {
active = 0;
draw();
}
static const struct zwp_keyboard_shortcuts_inhibitor_v1_listener
keyboard_shortcuts_inhibitor_listener = {
.active = keyboard_shortcuts_inhibit_handle_active,
.inactive = keyboard_shortcuts_inhibit_handle_inactive,
};
static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
uint32_t time, uint32_t button, uint32_t state_w) {
struct wl_surface *surface = data;
if (button == BTN_LEFT && state_w == WL_POINTER_BUTTON_STATE_PRESSED) {
if (keyboard_shortcuts_inhibitor) {
zwp_keyboard_shortcuts_inhibitor_v1_destroy(
keyboard_shortcuts_inhibitor);
keyboard_shortcuts_inhibitor = NULL;
active = false;
} else {
keyboard_shortcuts_inhibitor =
zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
keyboard_shortcuts_inhibit_manager, surface, seat);
zwp_keyboard_shortcuts_inhibitor_v1_add_listener(
keyboard_shortcuts_inhibitor,
&keyboard_shortcuts_inhibitor_listener, NULL);
}
}
draw();
}
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface,
wl_fixed_t surface_x, wl_fixed_t surface_y) {
// This space intentionally left blank
}
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface) {
// This space intentionally left blank
}
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
// This space intentionally left blank
}
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis, wl_fixed_t value) {
// This space intentionally left blank
}
static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
// This space intentionally left blank
}
static void pointer_handle_axis_source(void *data,
struct wl_pointer *wl_pointer, uint32_t axis_source) {
// This space intentionally left blank
}
static void pointer_handle_axis_stop(void *data,
struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) {
// This space intentionally left blank
}
static void pointer_handle_axis_discrete(void *data,
struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
// This space intentionally left blank
}
static const struct wl_pointer_listener pointer_listener = {
.enter = pointer_handle_enter,
.leave = pointer_handle_leave,
.motion = pointer_handle_motion,
.button = pointer_handle_button,
.axis = pointer_handle_axis,
.frame = pointer_handle_frame,
.axis_source = pointer_handle_axis_source,
.axis_stop = pointer_handle_axis_stop,
.axis_discrete = pointer_handle_axis_discrete,
};
static void xdg_surface_handle_configure(void *data,
struct xdg_surface *xdg_surface, uint32_t serial) {
xdg_surface_ack_configure(xdg_surface, serial);
wl_egl_window_resize(egl_window, width, height, 0, 0);
draw();
}
static const struct xdg_surface_listener xdg_surface_listener = {
.configure = xdg_surface_handle_configure,
};
static void xdg_toplevel_handle_configure(void *data,
struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h,
struct wl_array *states) {
width = w;
height = h;
}
static void xdg_toplevel_handle_close(void *data,
struct xdg_toplevel *xdg_toplevel) {
exit(EXIT_SUCCESS);
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
.configure = xdg_toplevel_handle_configure,
.close = xdg_toplevel_handle_close,
};
static void handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version) {
if (strcmp(interface, "wl_compositor") == 0) {
compositor = wl_registry_bind(registry, name,
&wl_compositor_interface, 1);
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
} else if (strcmp(interface, zwp_keyboard_shortcuts_inhibit_manager_v1_interface.name) == 0) {
keyboard_shortcuts_inhibit_manager = wl_registry_bind(registry, name,
&zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
}
}
static void handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name) {
// who cares
}
static const struct wl_registry_listener registry_listener = {
.global = handle_global,
.global_remove = handle_global_remove,
};
int main(int argc, char **argv) {
struct wl_display *display = wl_display_connect(NULL);
if (display == NULL) {
fprintf(stderr, "Failed to create display\n");
return EXIT_FAILURE;
}
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_roundtrip(display);
if (compositor == NULL) {
fprintf(stderr, "wl-compositor not available\n");
return EXIT_FAILURE;
}
if (wm_base == NULL) {
fprintf(stderr, "xdg-shell not available\n");
return EXIT_FAILURE;
}
if (keyboard_shortcuts_inhibit_manager == NULL) {
fprintf(stderr, "keyboard-shortcuts-inhibit not available\n");
return EXIT_FAILURE;
}
egl_init(display);
struct wl_surface *surface = wl_compositor_create_surface(compositor);
struct xdg_surface *xdg_surface =
xdg_wm_base_get_xdg_surface(wm_base, surface);
struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
struct wl_pointer *pointer = wl_seat_get_pointer(seat);
wl_pointer_add_listener(pointer, &pointer_listener, surface);
xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL);
xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
wl_surface_commit(surface);
egl_window = wl_egl_window_create(surface, width, height);
egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, egl_window, NULL);
wl_display_roundtrip(display);
keyboard_shortcuts_inhibitor =
zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
keyboard_shortcuts_inhibit_manager, surface, seat);
zwp_keyboard_shortcuts_inhibitor_v1_add_listener(
keyboard_shortcuts_inhibitor,
&keyboard_shortcuts_inhibitor_listener, NULL);
draw();
while (wl_display_dispatch(display) != -1) {
// This space intentionally left blank
}
return EXIT_SUCCESS;
}

View File

@ -11,8 +11,7 @@
#include <wayland-client.h>
#include <wayland-cursor.h>
#include <wayland-egl.h>
#include <wlr/render/egl.h>
#include <wlr/util/log.h>
#include "egl_common.h"
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
@ -29,7 +28,6 @@ struct zwlr_layer_surface_v1 *layer_surface;
static struct wl_output *wl_output;
struct wl_surface *wl_surface;
struct wlr_egl egl;
struct wl_egl_window *egl_window;
struct wlr_egl_surface *egl_surface;
struct wl_callback *frame_callback;
@ -50,7 +48,8 @@ static int32_t margin_top = 0;
static double alpha = 1.0;
static bool run_display = true;
static bool animate = false;
static bool keyboard_interactive = false;
static enum zwlr_layer_surface_v1_keyboard_interactivity keyboard_interactive =
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
static double frame = 0;
static int cur_x = -1, cur_y = -1;
static int buttons = 0;
@ -93,7 +92,7 @@ static struct wl_callback_listener popup_frame_listener = {
};
static void draw(void) {
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
@ -142,7 +141,7 @@ static void draw(void) {
frame_callback = wl_surface_frame(wl_surface);
wl_callback_add_listener(frame_callback, &frame_listener, NULL);
eglSwapBuffers(egl.display, egl_surface);
eglSwapBuffers(egl_display, egl_surface);
demo.last_frame = ts;
}
@ -150,7 +149,7 @@ static void draw(void) {
static void draw_popup(void) {
static float alpha_mod = -0.01;
eglMakeCurrent(egl.display, popup_egl_surface, popup_egl_surface, egl.context);
eglMakeCurrent(egl_display, popup_egl_surface, popup_egl_surface, egl_context);
glViewport(0, 0, popup_width, popup_height);
glClearColor(popup_red, 0.5f, 0.5f, popup_alpha);
popup_alpha += alpha_mod;
@ -162,7 +161,7 @@ static void draw_popup(void) {
popup_frame_callback = wl_surface_frame(popup_wl_surface);
assert(popup_frame_callback);
wl_callback_add_listener(popup_frame_callback, &popup_frame_listener, NULL);
eglSwapBuffers(egl.display, popup_egl_surface);
eglSwapBuffers(egl_display, popup_egl_surface);
wl_surface_commit(popup_wl_surface);
}
@ -177,8 +176,7 @@ static const struct xdg_surface_listener xdg_surface_listener = {
static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup,
int32_t x, int32_t y, int32_t width, int32_t height) {
wlr_log(WLR_DEBUG, "Popup configured %dx%d@%d,%d",
width, height, x, y);
fprintf(stderr, "Popup configured %dx%d@%d,%d\n", width, height, x, y);
popup_width = width;
popup_height = height;
if (popup_egl_window) {
@ -187,7 +185,7 @@ static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup,
}
static void popup_destroy(void) {
wlr_egl_destroy_surface(&egl, popup_egl_surface);
eglDestroySurface(egl_display, popup_egl_surface);
wl_egl_window_destroy(popup_egl_window);
xdg_popup_destroy(popup);
wl_surface_destroy(popup_wl_surface);
@ -197,7 +195,7 @@ static void popup_destroy(void) {
}
static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) {
wlr_log(WLR_DEBUG, "Popup done");
fprintf(stderr, "Popup done\n");
popup_destroy();
}
@ -241,8 +239,9 @@ static void create_popup(uint32_t serial) {
popup_wl_surface = surface;
popup_egl_window = wl_egl_window_create(surface, popup_width, popup_height);
assert(popup_egl_window);
popup_egl_surface = wlr_egl_create_surface(&egl, popup_egl_window);
assert(popup_egl_surface);
popup_egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, popup_egl_window, NULL);
assert(popup_egl_surface != EGL_NO_SURFACE);
draw_popup();
}
@ -259,7 +258,7 @@ static void layer_surface_configure(void *data,
static void layer_surface_closed(void *data,
struct zwlr_layer_surface_v1 *surface) {
wlr_egl_destroy_surface(&egl, egl_surface);
eglDestroySurface(egl_display, egl_surface);
wl_egl_window_destroy(egl_window);
zwlr_layer_surface_v1_destroy(surface);
wl_surface_destroy(wl_surface);
@ -376,17 +375,17 @@ static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
wlr_log(WLR_DEBUG, "Keyboard enter");
fprintf(stderr, "Keyboard enter\n");
}
static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface) {
wlr_log(WLR_DEBUG, "Keyboard leave");
fprintf(stderr, "Keyboard leave\n");
}
static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
wlr_log(WLR_DEBUG, "Key event: %d %d", key, state);
fprintf(stderr, "Key event: %d %d\n", key, state);
}
static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
@ -453,8 +452,8 @@ static void handle_global(void *data, struct wl_registry *registry,
&wl_seat_interface, 1);
wl_seat_add_listener(seat, &seat_listener, NULL);
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
layer_shell = wl_registry_bind(
registry, name, &zwlr_layer_shell_v1_interface, 1);
layer_shell = wl_registry_bind(registry, name,
&zwlr_layer_shell_v1_interface, version < 4 ? version : 4);
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
xdg_wm_base = wl_registry_bind(
registry, name, &xdg_wm_base_interface, 1);
@ -472,13 +471,12 @@ static const struct wl_registry_listener registry_listener = {
};
int main(int argc, char **argv) {
wlr_log_init(WLR_DEBUG, NULL);
char *namespace = "wlroots";
int exclusive_zone = 0;
int32_t margin_right = 0, margin_bottom = 0, margin_left = 0;
bool found;
int c;
while ((c = getopt(argc, argv, "knw:h:o:l:a:x:m:t:")) != -1) {
while ((c = getopt(argc, argv, "k:nw:h:o:l:a:x:m:t:")) != -1) {
switch (c) {
case 'o':
output = atoi(optarg);
@ -558,9 +556,29 @@ int main(int argc, char **argv) {
case 'n':
animate = true;
break;
case 'k':
keyboard_interactive = true;
case 'k': {
const struct {
const char *name;
enum zwlr_layer_surface_v1_keyboard_interactivity value;
} kb_int[] = {
{ "none", ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE },
{ "exclusive", ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE },
{ "on_demand", ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND }
};
found = false;
for (size_t i = 0; i < sizeof(kb_int) / sizeof(kb_int[0]); ++i) {
if (strcmp(optarg, kb_int[i].name) == 0) {
keyboard_interactive = kb_int[i].value;
found = true;
break;
}
}
if (!found) {
fprintf(stderr, "invalid keyboard interactivity setting %s\n", optarg);
return 1;
}
break;
}
default:
break;
}
@ -610,9 +628,7 @@ int main(int argc, char **argv) {
cursor_surface = wl_compositor_create_surface(compositor);
assert(cursor_surface);
EGLint attribs[] = { EGL_ALPHA_SIZE, 8, EGL_NONE };
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display,
attribs, WL_SHM_FORMAT_ARGB8888);
egl_init(display);
wl_surface = wl_compositor_create_surface(compositor);
assert(wl_surface);
@ -634,8 +650,9 @@ int main(int argc, char **argv) {
egl_window = wl_egl_window_create(wl_surface, width, height);
assert(egl_window);
egl_surface = wlr_egl_create_surface(&egl, egl_window);
assert(egl_surface);
egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, egl_window, NULL);
assert(egl_surface != EGL_NO_SURFACE);
wl_display_roundtrip(display);
draw();

View File

@ -1,89 +1,127 @@
threads = dependency('threads')
wayland_egl = dependency('wayland-egl')
wayland_cursor = dependency('wayland-cursor')
libpng = dependency('libpng', required: false)
wayland_client = dependency('wayland-client')
libpng = dependency('libpng', required: false, disabler: true)
egl = dependency('egl', required: false, disabler: true)
glesv2 = dependency('glesv2', required: false, disabler: true)
# These versions correspond to ffmpeg 4.0
libavutil = dependency('libavutil', version: '>=56.14.100', required: false)
libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false)
libavformat = dependency('libavformat', version: '>=58.12.100', required: false)
libavutil = dependency('libavutil', version: '>=56.14.100', required: false, disabler: true)
libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false, disabler: true)
libavformat = dependency('libavformat', version: '>=58.12.100', required: false, disabler: true)
# Only needed for drm_fourcc.h
libdrm = dependency('libdrm').partial_dependency(compile_args: true, includes: true)
# epoll is a separate library in FreeBSD
if host_machine.system() == 'freebsd'
libepoll = [dependency('epoll-shim')]
libepoll = dependency('epoll-shim')
else
libepoll = []
libepoll = dependency('', required: false)
endif
# Small hack until https://github.com/mesonbuild/meson/pull/3386/ is merged
foreach dep : ['libpng', 'libavutil', 'libavcodec', 'libavformat']
if not get_variable(dep).found()
set_variable(dep, disabler())
endif
endforeach
if not cc.has_header('libavutil/hwcontext_drm.h', dependencies: libavutil)
libavutil = disabler()
endif
examples = {
compositors = {
'simple': {
'src': 'simple.c',
'dep': [wlroots],
},
'pointer': {
'src': 'pointer.c',
'dep': [wlroots],
},
'touch': {
'src': ['touch.c', 'cat.c'],
'dep': [wlroots],
},
'tablet': {
'src': 'tablet.c',
'dep': [wlroots],
},
'rotation': {
'src': ['rotation.c', 'cat.c'],
'dep': [wlroots],
},
'multi-pointer': {
'src': 'multi-pointer.c',
'dep': [wlroots],
},
'output-layout': {
'src': ['output-layout.c', 'cat.c'],
'dep': [wlroots],
},
'screenshot': {
'src': 'screenshot.c',
'dep': [wayland_client, wlr_protos, rt],
'fullscreen-shell': {
'src': 'fullscreen-shell.c',
'proto': ['fullscreen-shell-unstable-v1'],
},
'quads': {
'src': 'quads.c',
},
'scene-graph': {
'src': 'scene-graph.c',
'proto': ['xdg-shell'],
},
}
clients = {
'idle': {
'src': 'idle.c',
'dep': [wayland_client, wlr_protos, threads],
'dep': threads,
'proto': ['kde-idle'],
},
'idle-inhibit': {
'src': 'idle-inhibit.c',
'dep': [wayland_client, wlr_protos, wlroots],
'src': ['idle-inhibit.c', 'egl_common.c'],
'dep': [wayland_egl, egl, glesv2],
'proto': [
'idle-inhibit-unstable-v1',
'xdg-shell',
],
},
'keyboard-shortcuts-inhibit': {
'src': ['keyboard-shortcuts-inhibit.c', 'egl_common.c'],
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
'proto': [
'keyboard-shortcuts-inhibit-unstable-v1',
'xdg-shell',
],
},
'layer-shell': {
'src': 'layer-shell.c',
'dep': [wayland_client, wayland_cursor, wlr_protos, wlroots],
'src': ['layer-shell.c', 'egl_common.c'],
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
'proto': [
'wlr-layer-shell-unstable-v1',
'xdg-shell',
],
},
'input-inhibitor': {
'src': 'input-inhibitor.c',
'dep': [wayland_client, wayland_cursor, wlr_protos, wlroots],
'src': ['input-inhibitor.c', 'egl_common.c'],
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
'proto': [
'wlr-input-inhibitor-unstable-v1',
'xdg-shell',
],
},
'gamma-control': {
'src': 'gamma-control.c',
'dep': [wayland_client, wayland_cursor, wlr_protos, math],
'dep': [wayland_cursor, math],
'proto': ['wlr-gamma-control-unstable-v1'],
},
'output-power-management': {
'src': 'output-power-management.c',
'dep': [wayland_client],
'proto': ['wlr-output-power-management-unstable-v1'],
},
'pointer-constraints': {
'src': 'pointer-constraints.c',
'dep': [wayland_client, wlr_protos, wlroots],
'src': ['pointer-constraints.c', 'egl_common.c'],
'dep': [wayland_egl, egl, glesv2],
'proto': [
'pointer-constraints-unstable-v1',
'xdg-shell',
],
},
'relative-pointer': {
'src': 'relative-pointer-unstable-v1.c',
'dep': [wayland_client, wlr_protos, wlroots],
'src': ['relative-pointer-unstable-v1.c', 'egl_common.c'],
'dep': [wayland_egl, egl, glesv2],
'proto': [
'pointer-constraints-unstable-v1',
'relative-pointer-unstable-v1',
'xdg-shell',
],
},
'dmabuf-capture': {
'src': 'dmabuf-capture.c',
@ -91,51 +129,92 @@ examples = {
libavcodec,
libavformat,
libavutil,
drm.partial_dependency(compile_args: true), # <drm_fourcc.h>
drm,
threads,
wayland_client,
wlr_protos,
],
'proto': ['wlr-export-dmabuf-unstable-v1'],
},
'screencopy': {
'src': 'screencopy.c',
'dep': [libpng, wayland_client, wlr_protos, rt],
'dep': [libpng, rt],
'proto': ['wlr-screencopy-unstable-v1'],
},
'screencopy-dmabuf': {
'src': 'screencopy-dmabuf.c',
'dep': [libpng, rt, gbm, drm],
'proto': [
'wlr-screencopy-unstable-v1',
'linux-dmabuf-unstable-v1',
],
},
'toplevel-decoration': {
'src': 'toplevel-decoration.c',
'dep': [wayland_client, wlr_protos, wlroots],
'src': ['toplevel-decoration.c', 'egl_common.c'],
'dep': [wayland_egl, egl, glesv2],
'proto': [
'xdg-decoration-unstable-v1',
'xdg-shell',
],
},
'input-method': {
'src': 'input-method.c',
'dep': [wayland_client, wlr_protos] + libepoll,
'dep': [wayland_egl, libepoll],
'proto': [
'input-method-unstable-v2',
'text-input-unstable-v3',
'xdg-shell',
],
},
'text-input': {
'src': 'text-input.c',
'dep': [wayland_cursor, wayland_client, wlr_protos, wlroots],
'src': ['text-input.c', 'egl_common.c'],
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
'proto': [
'text-input-unstable-v3',
'xdg-shell',
],
},
'foreign-toplevel': {
'src': 'foreign-toplevel.c',
'dep': [wayland_client, wlr_protos, wlroots],
'proto': ['wlr-foreign-toplevel-management-unstable-v1'],
},
'fullscreen-shell': {
'src': 'fullscreen-shell.c',
'dep': [wlr_protos, wlroots],
'virtual-pointer': {
'src': 'virtual-pointer.c',
'proto': ['wlr-virtual-pointer-unstable-v1'],
},
'input-method-keyboard-grab': {
'src': 'input-method-keyboard-grab.c',
'dep': xkbcommon,
'proto': [
'input-method-unstable-v2',
],
},
}
foreach name, info : examples
all_dep_found = true
foreach d : info.get('dep')
all_dep_found = all_dep_found and d.found()
foreach name, info : compositors
extra_src = []
foreach p : info.get('proto', [])
extra_src += protocols_server_header[p]
endforeach
if all_dep_found
executable(
name,
info.get('src'),
dependencies: info.get('dep'),
build_by_default: get_option('examples'),
)
else
warning('Dependencies not satisfied for ' + name)
endif
executable(
name,
[info.get('src'), extra_src],
dependencies: [wlroots, libdrm],
include_directories: [wlr_inc, proto_inc],
build_by_default: get_option('examples'),
)
endforeach
foreach name, info : clients
extra_src = []
foreach p : info.get('proto')
extra_src += protocols_code[p]
extra_src += protocols_client_header[p]
endforeach
executable(
name,
[info.get('src'), extra_src],
dependencies: [wayland_client, info.get('dep', [])],
build_by_default: get_option('examples'),
)
endforeach

View File

@ -1,23 +1,21 @@
#define _POSIX_C_SOURCE 200112L
#include <assert.h>
#include <GLES2/gl2.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <wayland-server-protocol.h>
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/backend/session.h>
#include <wlr/render/gles2.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_list.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/util/log.h>
#include <wlr/xcursor.h>
#include <xkbcommon/xkbcommon.h>
@ -25,6 +23,8 @@
struct sample_state {
struct wl_display *display;
struct wlr_xcursor *xcursor;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
float default_color[4];
float clear_color[4];
struct wlr_output_layout *layout;
@ -69,10 +69,10 @@ struct sample_keyboard {
struct wl_listener destroy;
};
void configure_cursor(struct wlr_cursor *cursor, struct wlr_input_device *device,
static void configure_cursor(struct wlr_cursor *cursor, struct wlr_input_device *device,
struct sample_state *sample) {
struct sample_output *output;
wlr_log(WLR_ERROR, "Configuring cursor %p for device %p", cursor, device);
wlr_log(WLR_INFO, "Configuring cursor %p for device %p", cursor, device);
// reset mappings
wlr_cursor_map_to_output(cursor, NULL);
@ -89,19 +89,21 @@ void configure_cursor(struct wlr_cursor *cursor, struct wlr_input_device *device
}
}
void output_frame_notify(struct wl_listener *listener, void *data) {
static void output_frame_notify(struct wl_listener *listener, void *data) {
struct sample_output *output = wl_container_of(listener, output, frame);
struct sample_state *sample = output->sample;
struct wlr_output *wlr_output = output->output;
struct wlr_renderer *renderer = sample->renderer;
wlr_output_make_current(wlr_output, NULL);
wlr_output_attach_render(wlr_output, NULL);
glClearColor(sample->clear_color[0], sample->clear_color[1],
sample->clear_color[2], sample->clear_color[3]);
glClear(GL_COLOR_BUFFER_BIT);
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(renderer, sample->clear_color);
wlr_output_render_software_cursors(wlr_output, NULL);
wlr_output_swap_buffers(wlr_output, NULL, NULL);
wlr_renderer_end(renderer);
wlr_output_commit(wlr_output);
}
static void handle_cursor_motion(struct wl_listener *listener, void *data) {
@ -128,27 +130,7 @@ static void cursor_destroy(struct sample_cursor *cursor) {
free(cursor);
}
void input_remove_notify(struct wl_listener *listener, void *data) {
struct wlr_input_device *device = data;
struct sample_cursor *sample_cursor = wl_container_of(listener, sample_cursor, destroy);
struct sample_state *sample = sample_cursor->sample;
struct sample_cursor *cursor;
wl_list_for_each(cursor, &sample->cursors, link) {
if (cursor->device == device) {
cursor_destroy(cursor);
break;
}
}
struct sample_pointer *pointer;
wl_list_for_each(pointer, &sample->pointers, link) {
if (pointer->device == device) {
free(pointer);
break;
}
}
}
void output_remove_notify(struct wl_listener *listener, void *data) {
static void output_remove_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
struct sample_state *sample = sample_output->sample;
wl_list_remove(&sample_output->frame.link);
@ -162,14 +144,13 @@ void output_remove_notify(struct wl_listener *listener, void *data) {
}
}
void new_output_notify(struct wl_listener *listener, void *data) {
static void new_output_notify(struct wl_listener *listener, void *data) {
struct wlr_output *output = data;
struct sample_state *sample = wl_container_of(listener, sample, new_output);
wlr_output_init_render(output, sample->allocator, sample->renderer);
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
if (!wl_list_empty(&output->modes)) {
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
wlr_output_set_mode(output, mode);
}
sample_output->output = output;
sample_output->sample = sample;
wl_signal_add(&output->events.frame, &sample_output->frame);
@ -179,7 +160,6 @@ void new_output_notify(struct wl_listener *listener, void *data) {
wlr_output_layout_add_auto(sample->layout, output);
struct sample_cursor *cursor;
wl_list_for_each(cursor, &sample->cursors, link) {
configure_cursor(cursor->cursor, cursor->device, sample);
@ -192,9 +172,16 @@ void new_output_notify(struct wl_listener *listener, void *data) {
cursor->cursor->y);
}
wl_list_insert(&sample->outputs, &sample_output->link);
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
if (mode != NULL) {
wlr_output_set_mode(output, mode);
}
wlr_output_commit(output);
}
void keyboard_key_notify(struct wl_listener *listener, void *data) {
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
struct sample_state *sample = keyboard->sample;
struct wlr_event_keyboard_key *event = data;
@ -210,14 +197,14 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
}
}
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
wl_list_remove(&keyboard->destroy.link);
wl_list_remove(&keyboard->key.link);
free(keyboard);
}
void new_input_notify(struct wl_listener *listener, void *data) {
static void new_input_notify(struct wl_listener *listener, void *data) {
struct wlr_input_device *device = data;
struct sample_state *sample = wl_container_of(listener, sample, new_input);
switch (device->type) {
@ -229,18 +216,12 @@ void new_input_notify(struct wl_listener *listener, void *data) {
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
keyboard->key.notify = keyboard_key_notify;
struct xkb_rule_names rules = { 0 };
rules.rules = getenv("XKB_DEFAULT_RULES");
rules.model = getenv("XKB_DEFAULT_MODEL");
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
rules.variant = getenv("XKB_DEFAULT_VARIANT");
rules.options = getenv("XKB_DEFAULT_OPTIONS");
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
exit(1);
}
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!keymap) {
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
@ -290,10 +271,14 @@ int main(int argc, char *argv[]) {
.clear_color = { 0.25f, 0.25f, 0.25f, 1 },
.display = display,
};
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
struct wlr_backend *wlr = wlr_backend_autocreate(display);
if (!wlr) {
exit(1);
}
state.renderer = wlr_renderer_autocreate(wlr);
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
wl_list_init(&state.cursors);
wl_list_init(&state.pointers);
wl_list_init(&state.outputs);

View File

@ -1,5 +1,5 @@
#define _POSIX_C_SOURCE 200112L
#include <GLES2/gl2.h>
#include <drm_fourcc.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
@ -8,16 +8,17 @@
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include <wayland-server-protocol.h>
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/backend/session.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
#include "cat.h"
@ -27,6 +28,7 @@ struct sample_state {
struct wl_listener new_output;
struct wl_listener new_input;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_texture *cat_texture;
struct wlr_output_layout *layout;
float x_offs, y_offs;
@ -107,7 +109,7 @@ static void animate_cat(struct sample_state *sample,
sample->ts_last = ts;
}
void output_frame_notify(struct wl_listener *listener, void *data) {
static void output_frame_notify(struct wl_listener *listener, void *data) {
struct sample_output *output = wl_container_of(listener, output, frame);
struct sample_state *sample = output->sample;
struct timespec ts;
@ -115,7 +117,7 @@ void output_frame_notify(struct wl_listener *listener, void *data) {
struct wlr_output *wlr_output = output->output;
wlr_output_make_current(wlr_output, NULL);
wlr_output_attach_render(wlr_output, NULL);
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
@ -137,7 +139,7 @@ void output_frame_notify(struct wl_listener *listener, void *data) {
}
wlr_renderer_end(sample->renderer);
wlr_output_swap_buffers(wlr_output, NULL, NULL);
wlr_output_commit(wlr_output);
}
static void update_velocities(struct sample_state *sample,
@ -146,7 +148,7 @@ static void update_velocities(struct sample_state *sample,
sample->y_vel += y_diff;
}
void output_remove_notify(struct wl_listener *listener, void *data) {
static void output_remove_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
struct sample_state *sample = sample_output->sample;
wlr_output_layout_remove(sample->layout, sample_output->output);
@ -155,14 +157,13 @@ void output_remove_notify(struct wl_listener *listener, void *data) {
free(sample_output);
}
void new_output_notify(struct wl_listener *listener, void *data) {
static void new_output_notify(struct wl_listener *listener, void *data) {
struct wlr_output *output = data;
struct sample_state *sample = wl_container_of(listener, sample, new_output);
wlr_output_init_render(output, sample->allocator, sample->renderer);
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
if (!wl_list_empty(&output->modes)) {
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
wlr_output_set_mode(output, mode);
}
wlr_output_layout_add_auto(sample->layout, output);
sample_output->output = output;
sample_output->sample = sample;
@ -170,9 +171,16 @@ void new_output_notify(struct wl_listener *listener, void *data) {
sample_output->frame.notify = output_frame_notify;
wl_signal_add(&output->events.destroy, &sample_output->destroy);
sample_output->destroy.notify = output_remove_notify;
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
if (mode != NULL) {
wlr_output_set_mode(output, mode);
}
wlr_output_commit(output);
}
void keyboard_key_notify(struct wl_listener *listener, void *data) {
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
struct sample_state *sample = keyboard->sample;
struct wlr_event_keyboard_key *event = data;
@ -189,7 +197,7 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
// and make this change in pixels/sec^2
// Also, key repeat
int delta = 75;
if (event->state == WLR_KEY_PRESSED) {
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
switch (sym) {
case XKB_KEY_Left:
update_velocities(sample, -delta, 0);
@ -208,14 +216,14 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
}
}
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
wl_list_remove(&keyboard->destroy.link);
wl_list_remove(&keyboard->key.link);
free(keyboard);
}
void new_input_notify(struct wl_listener *listener, void *data) {
static void new_input_notify(struct wl_listener *listener, void *data) {
struct wlr_input_device *device = data;
struct sample_state *sample = wl_container_of(listener, sample, new_input);
switch (device->type) {
@ -227,18 +235,12 @@ void new_input_notify(struct wl_listener *listener, void *data) {
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
keyboard->key.notify = keyboard_key_notify;
struct xkb_rule_names rules = { 0 };
rules.rules = getenv("XKB_DEFAULT_RULES");
rules.model = getenv("XKB_DEFAULT_MODEL");
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
rules.variant = getenv("XKB_DEFAULT_VARIANT");
rules.options = getenv("XKB_DEFAULT_OPTIONS");
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
exit(1);
}
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!keymap) {
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
@ -266,7 +268,7 @@ int main(int argc, char *argv[]) {
state.layout = wlr_output_layout_create();
clock_gettime(CLOCK_MONOTONIC, &state.ts_last);
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
struct wlr_backend *wlr = wlr_backend_autocreate(display);
if (!wlr) {
exit(1);
}
@ -276,11 +278,13 @@ int main(int argc, char *argv[]) {
wl_signal_add(&wlr->events.new_input, &state.new_input);
state.new_input.notify = new_input_notify;
state.renderer = wlr_backend_get_renderer(wlr);
state.renderer = wlr_renderer_autocreate(wlr);
state.cat_texture = wlr_texture_from_pixels(state.renderer,
WL_SHM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
DRM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
cat_tex.pixel_data);
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
if (!wlr_backend_start(wlr)) {
wlr_log(WLR_ERROR, "Failed to start backend");
wlr_backend_destroy(wlr);

View File

@ -0,0 +1,144 @@
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <wayland-client-protocol.h>
#include <wayland-client.h>
#include "wlr-output-power-management-unstable-v1-client-protocol.h"
struct output {
struct wl_output *wl_output;
struct zwlr_output_power_v1 *output_power;
struct wl_list link;
};
static struct wl_list outputs;
static struct zwlr_output_power_manager_v1 *output_power_manager = NULL;
static void output_power_handle_mode(void *data,
struct zwlr_output_power_v1 *output_power,
enum zwlr_output_power_v1_mode mode) {
struct output *output = data;
switch (mode) {
case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
printf("Output %p disabled\n", output);
break;
case ZWLR_OUTPUT_POWER_V1_MODE_ON:
printf("Output %p enabled\n", output);
break;
}
}
static void output_power_handle_failed(void *data,
struct zwlr_output_power_v1 *output_power) {
fprintf(stderr, "failed to set output power mode\n");
exit(EXIT_FAILURE);
}
static const struct zwlr_output_power_v1_listener output_power_listener = {
.mode = output_power_handle_mode,
.failed = output_power_handle_failed,
};
static void registry_handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version) {
if (strcmp(interface, wl_output_interface.name) == 0) {
struct output *output = calloc(1, sizeof(struct output));
output->wl_output = wl_registry_bind(registry, name,
&wl_output_interface, 1);
wl_list_insert(&outputs, &output->link);
} else if (strcmp(interface,
zwlr_output_power_manager_v1_interface.name) == 0) {
output_power_manager = wl_registry_bind(registry, name,
&zwlr_output_power_manager_v1_interface, 1);
}
}
static void registry_handle_global_remove(void *data,
struct wl_registry *registry, uint32_t name) {
// Who cares?
}
static const struct wl_registry_listener registry_listener = {
.global = registry_handle_global,
.global_remove = registry_handle_global_remove,
};
static const char usage[] = "usage: output-power-management [options...]\n"
" -h: show this help message\n"
" -e: turn outputs on\n"
" -d: turn outputs off\n"
" -w: continuously watch for power mode changes\n";
int main(int argc, char *argv[]) {
wl_list_init(&outputs);
int opt;
enum zwlr_output_power_v1_mode mode =
ZWLR_OUTPUT_POWER_V1_MODE_ON;
bool watch_mode = false;
while ((opt = getopt(argc, argv, "edhw")) != -1) {
switch (opt) {
case 'e':
mode = ZWLR_OUTPUT_POWER_V1_MODE_ON;
break;
case 'd':
mode = ZWLR_OUTPUT_POWER_V1_MODE_OFF;
break;
case 'w':
watch_mode = true;
break;
case 'h':
default:
fprintf(stderr, usage);
return opt == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
}
}
struct wl_display *display = wl_display_connect(NULL);
if (display == NULL) {
fprintf(stderr, "failed to create display\n");
return -1;
}
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_roundtrip(display);
if (output_power_manager == NULL) {
fprintf(stderr,
"compositor doesn't support wlr-output-power-management-unstable-v1\n");
return EXIT_FAILURE;
}
struct output *output;
wl_list_for_each(output, &outputs, link) {
output->output_power = zwlr_output_power_manager_v1_get_output_power(
output_power_manager, output->wl_output);
zwlr_output_power_v1_add_listener(output->output_power,
&output_power_listener, output);
}
wl_display_roundtrip(display);
wl_list_for_each(output, &outputs, link) {
zwlr_output_power_v1_set_mode(output->output_power,
mode);
}
if (!watch_mode) {
wl_display_roundtrip(display);
return EXIT_SUCCESS;
}
while (wl_display_dispatch(display) != -1) {
// nothing to see here, please move along
}
return EXIT_SUCCESS;
}

View File

@ -5,7 +5,7 @@
#include <string.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <wlr/render/egl.h>
#include "egl_common.h"
#include "xdg-shell-client-protocol.h"
#include "pointer-constraints-unstable-v1-client-protocol.h"
@ -16,7 +16,6 @@ static struct wl_seat *seat = NULL;
static struct xdg_wm_base *wm_base = NULL;
static struct zwp_pointer_constraints_v1 *pointer_constraints = NULL;
struct wlr_egl egl;
struct wl_egl_window *egl_window;
struct wlr_egl_surface *egl_surface;
struct zwp_locked_pointer_v1* locked_pointer;
@ -32,7 +31,7 @@ enum {
struct wl_region *regions[3];
static void draw(void) {
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
float color[] = {1.0, 1.0, 0.0, 1.0};
@ -40,7 +39,7 @@ static void draw(void) {
glClearColor(color[0], color[1], color[2], 1.0);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(egl.display, egl_surface);
eglSwapBuffers(egl_display, egl_surface);
}
static void pointer_handle_button(void *data, struct wl_pointer *pointer,
@ -198,7 +197,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
struct wl_region *disjoint_region = wl_compositor_create_region(compositor);
@ -212,8 +210,7 @@ int main(int argc, char **argv) {
wl_region_add(joint_region, 256, 256, 256, 256);
regions[REGION_TYPE_JOINT] = joint_region;
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
WL_SHM_FORMAT_ARGB8888);
egl_init(display);
struct wl_surface *surface = wl_compositor_create_surface(compositor);
struct xdg_surface *xdg_surface =
@ -242,7 +239,8 @@ int main(int argc, char **argv) {
wl_surface_commit(surface);
egl_window = wl_egl_window_create(surface, width, height);
egl_surface = wlr_egl_create_surface(&egl, egl_window);
egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, egl_window, NULL);
wl_display_roundtrip(display);

View File

@ -6,17 +6,18 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <wayland-server-protocol.h>
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/backend/session.h>
#include <wlr/render/gles2.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_list.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_tablet_tool.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
@ -24,6 +25,8 @@
struct sample_state {
struct wl_display *display;
struct compositor_state *compositor;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_xcursor_manager *xcursor_manager;
struct wlr_cursor *cursor;
double cur_x, cur_y;
@ -91,19 +94,19 @@ static void warp_to_touch(struct sample_state *state,
wlr_cursor_warp_absolute(state->cursor, dev, x, y);
}
void output_frame_notify(struct wl_listener *listener, void *data) {
static void output_frame_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = wl_container_of(listener, sample_output, frame);
struct sample_state *state = sample_output->state;
struct wlr_output *wlr_output = sample_output->output;
struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
struct wlr_renderer *renderer = state->renderer;
assert(renderer);
wlr_output_make_current(wlr_output, NULL);
wlr_output_attach_render(wlr_output, NULL);
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(renderer, state->clear_color);
wlr_output_render_software_cursors(wlr_output, NULL);
wlr_output_swap_buffers(wlr_output, NULL, NULL);
wlr_renderer_end(renderer);
wlr_output_commit(wlr_output);
}
static void handle_cursor_motion(struct wl_listener *listener, void *data) {
@ -222,7 +225,7 @@ static void handle_tablet_tool_axis(struct wl_listener *listener, void *data) {
}
}
void keyboard_key_notify(struct wl_listener *listener, void *data) {
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
struct sample_state *sample = keyboard->state;
struct wlr_event_keyboard_key *event = data;
@ -238,7 +241,7 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
}
}
void output_remove_notify(struct wl_listener *listener, void *data) {
static void output_remove_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
struct sample_state *sample = sample_output->state;
wlr_output_layout_remove(sample->layout, sample_output->output);
@ -247,14 +250,13 @@ void output_remove_notify(struct wl_listener *listener, void *data) {
free(sample_output);
}
void new_output_notify(struct wl_listener *listener, void *data) {
static void new_output_notify(struct wl_listener *listener, void *data) {
struct wlr_output *output = data;
struct sample_state *sample = wl_container_of(listener, sample, new_output);
wlr_output_init_render(output, sample->allocator, sample->renderer);
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
if (!wl_list_empty(&output->modes)) {
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
wlr_output_set_mode(output, mode);
}
sample_output->output = output;
sample_output->state = sample;
wl_signal_add(&output->events.frame, &sample_output->frame);
@ -266,17 +268,24 @@ void new_output_notify(struct wl_listener *listener, void *data) {
wlr_xcursor_manager_load(sample->xcursor_manager, output->scale);
wlr_xcursor_manager_set_cursor_image(sample->xcursor_manager, "left_ptr",
sample->cursor);
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
if (mode != NULL) {
wlr_output_set_mode(output, mode);
}
wlr_output_commit(output);
}
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
wl_list_remove(&keyboard->destroy.link);
wl_list_remove(&keyboard->key.link);
free(keyboard);
}
void new_input_notify(struct wl_listener *listener, void *data) {
static void new_input_notify(struct wl_listener *listener, void *data) {
struct wlr_input_device *device = data;
struct sample_state *state = wl_container_of(listener, state, new_input);
switch (device->type) {
@ -294,18 +303,12 @@ void new_input_notify(struct wl_listener *listener, void *data) {
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
keyboard->key.notify = keyboard_key_notify;
struct xkb_rule_names rules = { 0 };
rules.rules = getenv("XKB_DEFAULT_RULES");
rules.model = getenv("XKB_DEFAULT_MODEL");
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
rules.variant = getenv("XKB_DEFAULT_VARIANT");
rules.options = getenv("XKB_DEFAULT_OPTIONS");
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
exit(1);
}
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!keymap) {
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
@ -330,10 +333,14 @@ int main(int argc, char *argv[]) {
.display = display
};
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
struct wlr_backend *wlr = wlr_backend_autocreate(display);
if (!wlr) {
exit(1);
}
state.renderer = wlr_renderer_autocreate(wlr);
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
state.cursor = wlr_cursor_create();
state.layout = wlr_output_layout_create();
wlr_cursor_attach_output_layout(state.cursor, state.layout);

220
examples/quads.c Normal file
View File

@ -0,0 +1,220 @@
#define _XOPEN_SOURCE 600
#include <getopt.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.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_keyboard.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
struct sample_state {
struct wl_display *display;
struct wl_listener new_output;
struct wl_listener new_input;
struct timespec last_frame;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wl_list outputs;
};
struct sample_output {
struct sample_state *sample;
struct wlr_output *output;
struct wl_listener frame;
struct wl_listener destroy;
struct wl_list link;
};
struct sample_keyboard {
struct sample_state *sample;
struct wlr_input_device *device;
struct wl_listener key;
struct wl_listener destroy;
};
static void output_frame_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = wl_container_of(listener, sample_output, frame);
struct sample_state *sample = sample_output->sample;
struct wlr_output *wlr_output = sample_output->output;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int32_t width, height;
wlr_output_effective_resolution(wlr_output, &width, &height);
wlr_output_attach_render(wlr_output, NULL);
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
static float rotation = 0.f;
for (int y = -128; y < height; y += 128) {
for (int x = -128; x < width; x += 128) {
struct wlr_box box = {
.x = x,
.y = y,
.width = 128,
.height = 128,
};
float color[] = {
255.f / x,
255.f / y,
255.f / (x + y),
1.f
};
float matrix[9];
wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL,
rotation, wlr_output->transform_matrix);
wlr_render_quad_with_matrix(sample->renderer, color, matrix);
}
}
wlr_renderer_end(sample->renderer);
wlr_output_commit(wlr_output);
//TODO rotate with a delta time
rotation += 0.05;
if (rotation > 2 * M_PI) {
rotation = 0.f;
}
sample->last_frame = now;
}
static void output_remove_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
wl_list_remove(&sample_output->frame.link);
wl_list_remove(&sample_output->destroy.link);
free(sample_output);
}
static void new_output_notify(struct wl_listener *listener, void *data) {
struct wlr_output *output = data;
struct sample_state *sample = wl_container_of(listener, sample, new_output);
wlr_output_init_render(output, sample->allocator, sample->renderer);
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
if (mode != NULL) {
wlr_output_set_mode(output, mode);
}
sample_output->output = output;
sample_output->sample = sample;
wl_signal_add(&output->events.frame, &sample_output->frame);
sample_output->frame.notify = output_frame_notify;
wl_signal_add(&output->events.destroy, &sample_output->destroy);
sample_output->destroy.notify = output_remove_notify;
wl_list_insert(&sample->outputs, &sample_output->link);
wlr_output_commit(output);
}
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
struct sample_state *sample = keyboard->sample;
struct wlr_event_keyboard_key *event = data;
uint32_t keycode = event->keycode + 8;
const xkb_keysym_t *syms;
int nsyms = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state,
keycode, &syms);
for (int i = 0; i < nsyms; i++) {
xkb_keysym_t sym = syms[i];
if (sym == XKB_KEY_Escape) {
wl_display_terminate(sample->display);
}
}
}
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
wl_list_remove(&keyboard->destroy.link);
wl_list_remove(&keyboard->key.link);
free(keyboard);
}
static void new_input_notify(struct wl_listener *listener, void *data) {
struct wlr_input_device *device = data;
struct sample_state *sample = wl_container_of(listener, sample, new_input);
switch (device->type) {
case WLR_INPUT_DEVICE_KEYBOARD:;
struct sample_keyboard *keyboard = calloc(1, sizeof(struct sample_keyboard));
keyboard->device = device;
keyboard->sample = sample;
wl_signal_add(&device->events.destroy, &keyboard->destroy);
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
keyboard->key.notify = keyboard_key_notify;
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
exit(1);
}
struct xkb_keymap *keymap = xkb_map_new_from_names(context, NULL,
XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!keymap) {
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
exit(1);
}
wlr_keyboard_set_keymap(device->keyboard, keymap);
xkb_keymap_unref(keymap);
xkb_context_unref(context);
break;
default:
break;
}
}
int main(int argc, char *argv[]) {
wlr_log_init(WLR_DEBUG, NULL);
struct wl_display *display = wl_display_create();
struct sample_state state = {
.display = display,
};
wl_list_init(&state.outputs);
struct wlr_backend *wlr = wlr_backend_autocreate(display);
if (!wlr) {
exit(1);
}
wl_signal_add(&wlr->events.new_output, &state.new_output);
state.new_output.notify = new_output_notify;
wl_signal_add(&wlr->events.new_input, &state.new_input);
state.new_input.notify = new_input_notify;
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
state.renderer = wlr_renderer_autocreate(wlr);
if (!state.renderer) {
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
wlr_backend_destroy(wlr);
exit(EXIT_FAILURE);
}
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
if (!wlr_backend_start(wlr)) {
wlr_log(WLR_ERROR, "Failed to start backend");
wlr_backend_destroy(wlr);
exit(1);
}
wl_display_run(display);
wl_display_destroy(display);
}

View File

@ -4,7 +4,7 @@
#include <GLES2/gl2.h>
#include <linux/input-event-codes.h>
#include <wayland-egl.h>
#include <wlr/render/egl.h>
#include "egl_common.h"
#include "pointer-constraints-unstable-v1-client-protocol.h"
#include "relative-pointer-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
@ -17,7 +17,6 @@
*/
struct egl_info {
struct wlr_egl *egl;
struct wl_egl_window *egl_window;
struct wlr_egl_surface *egl_surface;
uint32_t width;
@ -58,8 +57,8 @@ static struct wl_callback_listener surface_callback_listener = {
};
static void draw_init(struct egl_info *e) {
eglMakeCurrent(e->egl->display, e->egl_surface,
e->egl_surface, e->egl->context);
eglMakeCurrent(egl_display, e->egl_surface,
e->egl_surface, egl_context);
glViewport(0, 0, e->width, e->height);
}
@ -88,7 +87,7 @@ static void draw_end(struct egl_info *e) {
e->frame_callback = wl_surface_frame(e->surface);
wl_callback_add_listener(e->frame_callback, &surface_callback_listener, e);
eglSwapBuffers(e->egl->display, e->egl_surface);
eglSwapBuffers(egl_display, e->egl_surface);
}
@ -170,8 +169,7 @@ static void xdg_toplevel_handle_configure(void *data,
static void xdg_toplevel_handle_close(void *data,
struct xdg_toplevel *xdg_toplevel) {
struct egl_info *e = data;
wlr_egl_finish(e->egl);
egl_finish();
exit(EXIT_SUCCESS);
}
@ -412,7 +410,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
/* Check that all the global interfaces were captured */
@ -441,11 +438,9 @@ int main(int argc, char **argv) {
/* Initialize EGL context */
struct egl_info *e = calloc(1, sizeof(struct egl_info));
e->egl = calloc(1, sizeof(struct wlr_egl));
e->width = e->height = 512;
wlr_egl_init(e->egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
WL_SHM_FORMAT_ARGB8888);
egl_init(display);
/* Create the surface and xdg_toplevels, and set listeners */
@ -462,7 +457,8 @@ int main(int argc, char **argv) {
wl_surface_commit(surface);
e->egl_window = wl_egl_window_create(surface, e->width, e->height);
e->egl_surface = wlr_egl_create_surface(e->egl, e->egl_window);
e->egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, e->egl_window, NULL);
e->surface = surface;
wl_display_roundtrip(display);

View File

@ -1,5 +1,5 @@
#define _POSIX_C_SOURCE 200112L
#include <GLES2/gl2.h>
#include <drm_fourcc.h>
#include <getopt.h>
#include <math.h>
#include <stdio.h>
@ -8,14 +8,12 @@
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include <wayland-server-protocol.h>
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/backend/session.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/util/log.h>
@ -28,6 +26,7 @@ struct sample_state {
struct wl_listener new_input;
struct timespec last_frame;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_texture *cat_texture;
struct wl_list outputs;
enum wl_output_transform transform;
@ -60,7 +59,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
int32_t width, height;
wlr_output_effective_resolution(wlr_output, &width, &height);
wlr_output_make_current(wlr_output, NULL);
wlr_output_attach_render(wlr_output, NULL);
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
@ -72,7 +71,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
}
wlr_renderer_end(sample->renderer);
wlr_output_swap_buffers(wlr_output, NULL, NULL);
wlr_output_commit(wlr_output);
long ms = (now.tv_sec - sample->last_frame.tv_sec) * 1000 +
(now.tv_nsec - sample->last_frame.tv_nsec) / 1000000;
@ -98,21 +97,20 @@ static void update_velocities(struct sample_state *sample,
}
}
void output_remove_notify(struct wl_listener *listener, void *data) {
static void output_remove_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
wl_list_remove(&sample_output->frame.link);
wl_list_remove(&sample_output->destroy.link);
free(sample_output);
}
void new_output_notify(struct wl_listener *listener, void *data) {
static void new_output_notify(struct wl_listener *listener, void *data) {
struct wlr_output *output = data;
struct sample_state *sample = wl_container_of(listener, sample, new_output);
wlr_output_init_render(output, sample->allocator, sample->renderer);
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
if (!wl_list_empty(&output->modes)) {
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
wlr_output_set_mode(output, mode);
}
sample_output->x_offs = sample_output->y_offs = 0;
sample_output->x_vel = sample_output->y_vel = 128;
@ -124,9 +122,16 @@ void new_output_notify(struct wl_listener *listener, void *data) {
wl_signal_add(&output->events.destroy, &sample_output->destroy);
sample_output->destroy.notify = output_remove_notify;
wl_list_insert(&sample->outputs, &sample_output->link);
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
if (mode != NULL) {
wlr_output_set_mode(output, mode);
}
wlr_output_commit(output);
}
void keyboard_key_notify(struct wl_listener *listener, void *data) {
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
struct sample_state *sample = keyboard->sample;
struct wlr_event_keyboard_key *event = data;
@ -139,7 +144,7 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
if (sym == XKB_KEY_Escape) {
wl_display_terminate(sample->display);
}
if (event->state == WLR_KEY_PRESSED) {
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
switch (sym) {
case XKB_KEY_Left:
update_velocities(sample, -16, 0);
@ -158,14 +163,14 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
}
}
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
wl_list_remove(&keyboard->destroy.link);
wl_list_remove(&keyboard->key.link);
free(keyboard);
}
void new_input_notify(struct wl_listener *listener, void *data) {
static void new_input_notify(struct wl_listener *listener, void *data) {
struct wlr_input_device *device = data;
struct sample_state *sample = wl_container_of(listener, sample, new_input);
switch (device->type) {
@ -177,18 +182,12 @@ void new_input_notify(struct wl_listener *listener, void *data) {
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
keyboard->key.notify = keyboard_key_notify;
struct xkb_rule_names rules = { 0 };
rules.rules = getenv("XKB_DEFAULT_RULES");
rules.model = getenv("XKB_DEFAULT_MODEL");
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
rules.variant = getenv("XKB_DEFAULT_VARIANT");
rules.options = getenv("XKB_DEFAULT_OPTIONS");
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
exit(1);
}
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!keymap) {
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
@ -229,7 +228,7 @@ int main(int argc, char *argv[]) {
}
break;
default:
break;
return 1;
}
}
wlr_log_init(WLR_DEBUG, NULL);
@ -240,7 +239,7 @@ int main(int argc, char *argv[]) {
};
wl_list_init(&state.outputs);
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
struct wlr_backend *wlr = wlr_backend_autocreate(display);
if (!wlr) {
exit(1);
}
@ -251,20 +250,22 @@ int main(int argc, char *argv[]) {
state.new_input.notify = new_input_notify;
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
state.renderer = wlr_backend_get_renderer(wlr);
state.renderer = wlr_renderer_autocreate(wlr);
if (!state.renderer) {
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
wlr_backend_destroy(wlr);
exit(EXIT_FAILURE);
}
state.cat_texture = wlr_texture_from_pixels(state.renderer,
WL_SHM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
DRM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
cat_tex.pixel_data);
if (!state.cat_texture) {
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
exit(EXIT_FAILURE);
}
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
if (!wlr_backend_start(wlr)) {
wlr_log(WLR_ERROR, "Failed to start backend");
wlr_backend_destroy(wlr);

214
examples/scene-graph.c Normal file
View File

@ -0,0 +1,214 @@
#define _POSIX_C_SOURCE 200112L
#include <assert.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.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_output.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
/* Simple compositor making use of the scene-graph API. Input is unimplemented.
*
* New surfaces are stacked on top of the existing ones as they appear. */
static const int border_width = 3;
struct server {
struct wl_display *display;
struct wlr_backend *backend;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_scene *scene;
struct wl_list outputs;
struct wl_list surfaces;
struct wl_listener new_output;
struct wl_listener new_surface;
};
struct surface {
struct wlr_surface *wlr;
struct wlr_scene_surface *scene_surface;
struct wlr_scene_rect *border;
struct wl_list link;
struct wl_listener commit;
struct wl_listener destroy;
};
struct output {
struct wl_list link;
struct server *server;
struct wlr_output *wlr;
struct wlr_scene_output *scene_output;
struct wl_listener frame;
};
static void output_handle_frame(struct wl_listener *listener, void *data) {
struct output *output = wl_container_of(listener, output, frame);
if (!wlr_scene_output_commit(output->scene_output)) {
return;
}
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
struct surface *surface;
wl_list_for_each(surface, &output->server->surfaces, link) {
wlr_surface_send_frame_done(surface->wlr, &now);
}
}
static void server_handle_new_output(struct wl_listener *listener, void *data) {
struct server *server = wl_container_of(listener, server, new_output);
struct wlr_output *wlr_output = data;
wlr_output_init_render(wlr_output, server->allocator, server->renderer);
struct output *output =
calloc(1, sizeof(struct output));
output->wlr = wlr_output;
output->server = server;
output->frame.notify = output_handle_frame;
wl_signal_add(&wlr_output->events.frame, &output->frame);
wl_list_insert(&server->outputs, &output->link);
output->scene_output = wlr_scene_output_create(server->scene, wlr_output);
if (!wl_list_empty(&wlr_output->modes)) {
struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
wlr_output_set_mode(wlr_output, mode);
wlr_output_commit(wlr_output);
}
wlr_output_create_global(wlr_output);
}
static void surface_handle_commit(struct wl_listener *listener, void *data) {
struct surface *surface = wl_container_of(listener, surface, commit);
wlr_scene_rect_set_size(surface->border,
surface->wlr->current.width + 2 * border_width,
surface->wlr->current.height + 2 * border_width);
}
static void surface_handle_destroy(struct wl_listener *listener, void *data) {
struct surface *surface = wl_container_of(listener, surface, destroy);
wlr_scene_node_destroy(&surface->scene_surface->node);
wlr_scene_node_destroy(&surface->border->node);
wl_list_remove(&surface->destroy.link);
wl_list_remove(&surface->link);
free(surface);
}
static void server_handle_new_surface(struct wl_listener *listener,
void *data) {
struct server *server = wl_container_of(listener, server, new_surface);
struct wlr_surface *wlr_surface = data;
int pos = 50 * wl_list_length(&server->surfaces);
struct surface *surface = calloc(1, sizeof(struct surface));
surface->wlr = wlr_surface;
surface->commit.notify = surface_handle_commit;
wl_signal_add(&wlr_surface->events.commit, &surface->commit);
surface->destroy.notify = surface_handle_destroy;
wl_signal_add(&wlr_surface->events.destroy, &surface->destroy);
/* Border dimensions will be set in surface.commit handler */
surface->border = wlr_scene_rect_create(&server->scene->node,
0, 0, (float[4]){ 0.5f, 0.5f, 0.5f, 1 });
wlr_scene_node_set_position(&surface->border->node, pos, pos);
surface->scene_surface =
wlr_scene_surface_create(&server->scene->node, wlr_surface);
wl_list_insert(server->surfaces.prev, &surface->link);
wlr_scene_node_set_position(&surface->scene_surface->node,
pos + border_width, pos + border_width);
}
int main(int argc, char *argv[]) {
wlr_log_init(WLR_DEBUG, NULL);
char *startup_cmd = NULL;
int c;
while ((c = getopt(argc, argv, "s:")) != -1) {
switch (c) {
case 's':
startup_cmd = optarg;
break;
default:
printf("usage: %s [-s startup-command]\n", argv[0]);
return EXIT_FAILURE;
}
}
if (optind < argc) {
printf("usage: %s [-s startup-command]\n", argv[0]);
return EXIT_FAILURE;
}
struct server server = {0};
server.display = wl_display_create();
server.backend = wlr_backend_autocreate(server.display);
server.scene = wlr_scene_create();
server.renderer = wlr_renderer_autocreate(server.backend);
wlr_renderer_init_wl_display(server.renderer, server.display);
server.allocator = wlr_allocator_autocreate(server.backend,
server.renderer);
struct wlr_compositor *compositor =
wlr_compositor_create(server.display, server.renderer);
wlr_xdg_shell_create(server.display);
wl_list_init(&server.outputs);
wl_list_init(&server.surfaces);
server.new_output.notify = server_handle_new_output;
wl_signal_add(&server.backend->events.new_output, &server.new_output);
server.new_surface.notify = server_handle_new_surface;
wl_signal_add(&compositor->events.new_surface, &server.new_surface);
const char *socket = wl_display_add_socket_auto(server.display);
if (!socket) {
wl_display_destroy(server.display);
return EXIT_FAILURE;
}
if (!wlr_backend_start(server.backend)) {
wl_display_destroy(server.display);
return EXIT_FAILURE;
}
setenv("WAYLAND_DISPLAY", socket, true);
if (startup_cmd != NULL) {
if (fork() == 0) {
execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL);
}
}
wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s",
socket);
wl_display_run(server.display);
wl_display_destroy_clients(server.display);
wl_display_destroy(server.display);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,361 @@
/*
* Copyright © 2008 Kristian Høgsberg
* Copyright © 2020 Andri Yngvason
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#define _POSIX_C_SOURCE 200112L
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <png.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <assert.h>
#include <gbm.h>
#include <xf86drm.h>
#include <drm_fourcc.h>
#include <wayland-client-protocol.h>
#include "wlr-screencopy-unstable-v1-client-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
struct format {
uint32_t format;
bool is_bgr;
};
static int drm_fd = -1;
static struct gbm_device *gbm_device = NULL;
static struct zwp_linux_dmabuf_v1 *dmabuf = NULL;
static struct zwlr_screencopy_manager_v1 *screencopy_manager = NULL;
static struct wl_output *output = NULL;
static struct {
struct gbm_bo *bo;
struct wl_buffer *wl_buffer;
int width, height;
uint32_t format;
bool y_invert;
} buffer;
bool buffer_copy_done = false;
static bool have_linux_dmabuf = false;
// wl_shm_format describes little-endian formats, libpng uses big-endian
// formats (so Wayland's ABGR is libpng's RGBA).
static const struct format formats[] = {
{DRM_FORMAT_XRGB8888, true},
{DRM_FORMAT_ARGB8888, true},
{DRM_FORMAT_XBGR8888, false},
{DRM_FORMAT_ABGR8888, false},
};
static bool find_render_node(char *node, size_t maxlen) {
bool r = false;
drmDevice *devices[64];
int n = drmGetDevices2(0, devices, sizeof(devices) / sizeof(devices[0]));
for (int i = 0; i < n; ++i) {
drmDevice *dev = devices[i];
if (!(dev->available_nodes & (1 << DRM_NODE_RENDER))) {
continue;
}
strncpy(node, dev->nodes[DRM_NODE_RENDER], maxlen - 1);
node[maxlen - 1] = '\0';
r = true;
break;
}
drmFreeDevices(devices, n);
return r;
}
static void dmabuf_created(void *data,
struct zwp_linux_buffer_params_v1 *params,
struct wl_buffer *wl_buffer) {
buffer.wl_buffer = wl_buffer;
zwlr_screencopy_frame_v1_copy(data, buffer.wl_buffer);
}
static void dmabuf_failed(void *data,
struct zwp_linux_buffer_params_v1 *params) {
fprintf(stderr, "Failed to create dmabuf\n");
exit(EXIT_FAILURE);
}
static const struct zwp_linux_buffer_params_v1_listener params_listener = {
.created = dmabuf_created,
.failed = dmabuf_failed,
};
static void frame_handle_buffer(void *data,
struct zwlr_screencopy_frame_v1 *frame, uint32_t wl_format,
uint32_t width, uint32_t height, uint32_t stride) {
// Not implemented
}
static void frame_handle_linux_dmabuf(void *data,
struct zwlr_screencopy_frame_v1 *frame, uint32_t fourcc,
uint32_t width, uint32_t height) {
buffer.width = width;
buffer.height = height;
buffer.format = fourcc;
have_linux_dmabuf = true;
}
static void frame_handle_buffer_done(void *data,
struct zwlr_screencopy_frame_v1 *frame) {
assert(!buffer.bo);
if (!have_linux_dmabuf) {
fprintf(stderr, "linux-dmabuf is not supported\n");
exit(EXIT_FAILURE);
}
buffer.bo = gbm_bo_create(gbm_device, buffer.width, buffer.height,
buffer.format,
GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING);
if (buffer.bo == NULL) {
fprintf(stderr, "failed to create GBM buffer object\n");
exit(EXIT_FAILURE);
}
struct zwp_linux_buffer_params_v1 *params;
params = zwp_linux_dmabuf_v1_create_params(dmabuf);
assert(params);
int fd = gbm_bo_get_fd(buffer.bo);
uint32_t off = gbm_bo_get_offset(buffer.bo, 0);
uint32_t bo_stride = gbm_bo_get_stride(buffer.bo);
uint64_t mod = gbm_bo_get_modifier(buffer.bo);
zwp_linux_buffer_params_v1_add(params, fd, 0, off, bo_stride, mod >> 32,
mod & 0xffffffff);
zwp_linux_buffer_params_v1_add_listener(params, &params_listener, frame);
zwp_linux_buffer_params_v1_create(params, buffer.width, buffer.height,
buffer.format, /* flags */ 0);
}
static void frame_handle_flags(void *data,
struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) {
buffer.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT;
}
static void frame_handle_ready(void *data,
struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi,
uint32_t tv_sec_lo, uint32_t tv_nsec) {
buffer_copy_done = true;
}
static void frame_handle_failed(void *data,
struct zwlr_screencopy_frame_v1 *frame) {
fprintf(stderr, "failed to copy frame\n");
exit(EXIT_FAILURE);
}
static const struct zwlr_screencopy_frame_v1_listener frame_listener = {
.buffer = frame_handle_buffer,
.linux_dmabuf = frame_handle_linux_dmabuf,
.buffer_done = frame_handle_buffer_done,
.flags = frame_handle_flags,
.ready = frame_handle_ready,
.failed = frame_handle_failed,
};
static void dmabuf_format(void *data,
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format) {
// deprecated
}
static void dmabuf_modifier(void *data,
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format,
uint32_t modifier_hi, uint32_t modifier_lo) {
// TODO?
}
static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
.format = dmabuf_format,
.modifier = dmabuf_modifier,
};
static void handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version) {
if (strcmp(interface, wl_output_interface.name) == 0 && output == NULL) {
output = wl_registry_bind(registry, name, &wl_output_interface, 1);
} else if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) {
dmabuf = wl_registry_bind(registry, name,
&zwp_linux_dmabuf_v1_interface, 3);
zwp_linux_dmabuf_v1_add_listener(dmabuf, &dmabuf_listener, data);
} else if (strcmp(interface,
zwlr_screencopy_manager_v1_interface.name) == 0) {
screencopy_manager = wl_registry_bind(registry, name,
&zwlr_screencopy_manager_v1_interface, 3);
}
}
static void handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name) {
// Who cares?
}
static const struct wl_registry_listener registry_listener = {
.global = handle_global,
.global_remove = handle_global_remove,
};
static void write_image(char *filename, uint32_t format, int width,
int height, int stride, bool y_invert, png_bytep data) {
const struct format *fmt = NULL;
for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) {
if (formats[i].format == format) {
fmt = &formats[i];
break;
}
}
if (fmt == NULL) {
fprintf(stderr, "unsupported format %"PRIu32"\n", format);
exit(EXIT_FAILURE);
}
FILE *f = fopen(filename, "wb");
if (f == NULL) {
fprintf(stderr, "failed to open output file\n");
exit(EXIT_FAILURE);
}
png_structp png =
png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
png_init_io(png, f);
png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGBA,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
if (fmt->is_bgr) {
png_set_bgr(png);
}
png_write_info(png, info);
for (size_t i = 0; i < (size_t)height; ++i) {
png_bytep row;
if (y_invert) {
row = data + (height - i - 1) * stride;
} else {
row = data + i * stride;
}
png_write_row(png, row);
}
png_write_end(png, NULL);
png_destroy_write_struct(&png, &info);
fclose(f);
}
int main(int argc, char *argv[]) {
char render_node[256];
if (!find_render_node(render_node, sizeof(render_node))) {
fprintf(stderr, "Failed to find a DRM render node\n");
return EXIT_FAILURE;
}
printf("Using render node: %s\n", render_node);
drm_fd = open(render_node, O_RDWR);
if (drm_fd < 0) {
perror("Failed to open drm render node");
return EXIT_FAILURE;
}
gbm_device = gbm_create_device(drm_fd);
if (!gbm_device) {
fprintf(stderr, "Failed to create gbm device\n");
return EXIT_FAILURE;
}
struct wl_display *display = wl_display_connect(NULL);
if (display == NULL) {
perror("failed to create display");
return EXIT_FAILURE;
}
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_roundtrip(display);
if (dmabuf == NULL) {
fprintf(stderr, "compositor is missing linux-dmabuf-unstable-v1\n");
return EXIT_FAILURE;
}
if (screencopy_manager == NULL) {
fprintf(stderr, "compositor doesn't support wlr-screencopy-unstable-v1\n");
return EXIT_FAILURE;
}
if (output == NULL) {
fprintf(stderr, "no output available\n");
return EXIT_FAILURE;
}
struct zwlr_screencopy_frame_v1 *frame =
zwlr_screencopy_manager_v1_capture_output(screencopy_manager, 0, output);
zwlr_screencopy_frame_v1_add_listener(frame, &frame_listener, NULL);
while (!buffer_copy_done && wl_display_dispatch(display) != -1) {
// This space is intentionally left blank
}
uint32_t stride = 0;
void *map_data = NULL;
void *data = gbm_bo_map(buffer.bo, 0, 0, buffer.width, buffer.height,
GBM_BO_TRANSFER_READ, &stride, &map_data);
if (!data) {
perror("Failed to map gbm bo");
return EXIT_FAILURE;
}
write_image("wayland-screenshot.png", buffer.format, buffer.width,
buffer.height, stride, buffer.y_invert, data);
gbm_bo_unmap(buffer.bo, map_data);
gbm_bo_destroy(buffer.bo);
wl_buffer_destroy(buffer.wl_buffer);
gbm_device_destroy(gbm_device);
close(drm_fd);
return EXIT_SUCCESS;
}

View File

@ -32,8 +32,10 @@
#include <string.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <assert.h>
#include <wayland-client-protocol.h>
#include "wlr-screencopy-unstable-v1-client-protocol.h"
@ -69,7 +71,7 @@ static struct wl_buffer *create_shm_buffer(enum wl_shm_format fmt,
int size = stride * height;
const char shm_name[] = "/wlroots-screencopy";
int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0);
int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fd < 0) {
fprintf(stderr, "shm_open failed\n");
return NULL;
@ -88,7 +90,7 @@ static struct wl_buffer *create_shm_buffer(enum wl_shm_format fmt,
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
fprintf(stderr, "mmap failed: %m\n");
perror("mmap failed");
close(fd);
return NULL;
}
@ -110,6 +112,9 @@ static void frame_handle_buffer(void *data,
buffer.width = width;
buffer.height = height;
buffer.stride = stride;
// Make sure the buffer is not allocated
assert(!buffer.wl_buffer);
buffer.wl_buffer =
create_shm_buffer(format, width, height, stride, &buffer.data);
if (buffer.wl_buffer == NULL) {
@ -223,13 +228,12 @@ static void write_image(char *filename, enum wl_shm_format wl_fmt, int width,
int main(int argc, char *argv[]) {
struct wl_display * display = wl_display_connect(NULL);
if (display == NULL) {
fprintf(stderr, "failed to create display: %m\n");
perror("failed to create display");
return EXIT_FAILURE;
}
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (shm == NULL) {
@ -256,6 +260,7 @@ int main(int argc, char *argv[]) {
write_image("wayland-screenshot.png", buffer.format, buffer.width,
buffer.height, buffer.stride, buffer.y_invert, buffer.data);
wl_buffer_destroy(buffer.wl_buffer);
munmap(buffer.data, buffer.stride * buffer.height);
return EXIT_SUCCESS;
}

View File

@ -1,237 +0,0 @@
/*
* Copyright © 2008 Kristian Høgsberg
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#define _POSIX_C_SOURCE 200112L
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <unistd.h>
#include <wayland-client.h>
#include "screenshooter-client-protocol.h"
static struct wl_shm *shm = NULL;
static struct orbital_screenshooter *screenshooter = NULL;
static struct wl_list output_list;
static bool buffer_copy_done;
struct screenshooter_output {
struct wl_output *output;
int width, height;
struct wl_list link;
};
static void output_handle_geometry(void *data, struct wl_output *wl_output,
int x, int y, int physical_width, int physical_height, int subpixel,
const char *make, const char *model, int transform) {
// No-op
}
static void output_handle_mode(void *data, struct wl_output *wl_output,
uint32_t flags, int width, int height, int refresh) {
struct screenshooter_output *output = wl_output_get_user_data(wl_output);
if (wl_output == output->output && (flags & WL_OUTPUT_MODE_CURRENT)) {
output->width = width;
output->height = height;
}
}
static void output_handle_done(void *data, struct wl_output *wl_output) {
// No-op
}
static const struct wl_output_listener output_listener = {
.geometry = output_handle_geometry,
.mode = output_handle_mode,
.done = output_handle_done,
};
static void screenshot_done(void *data, struct orbital_screenshot *screenshot) {
buffer_copy_done = true;
}
static const struct orbital_screenshot_listener screenshot_listener = {
.done = screenshot_done,
};
static void handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version) {
static struct screenshooter_output *output;
if (strcmp(interface, "wl_output") == 0) {
output = calloc(1, sizeof(*output));
output->output = wl_registry_bind(registry, name, &wl_output_interface,
1);
wl_list_insert(&output_list, &output->link);
wl_output_add_listener(output->output, &output_listener, output);
} else if (strcmp(interface, "wl_shm") == 0) {
shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
} else if (strcmp(interface, "orbital_screenshooter") == 0) {
screenshooter = wl_registry_bind(registry, name,
&orbital_screenshooter_interface, 1);
}
}
static void handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name) {
// Who cares?
}
static const struct wl_registry_listener registry_listener = {
.global = handle_global,
.global_remove = handle_global_remove,
};
static struct wl_buffer *create_shm_buffer(int width, int height,
void **data_out) {
int stride = width * 4;
int size = stride * height;
const char shm_name[] = "/wlroots-screenshot";
int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0);
if (fd < 0) {
fprintf(stderr, "shm_open failed\n");
return NULL;
}
shm_unlink(shm_name);
int ret;
while ((ret = ftruncate(fd, size)) == EINTR) {
// No-op
}
if (ret < 0) {
close(fd);
fprintf(stderr, "ftruncate failed\n");
return NULL;
}
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
fprintf(stderr, "mmap failed: %m\n");
close(fd);
return NULL;
}
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
close(fd);
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height,
stride, WL_SHM_FORMAT_XRGB8888);
wl_shm_pool_destroy(pool);
*data_out = data;
return buffer;
}
static void write_image(const char *filename, int width, int height,
void *data) {
char size[10 + 1 + 10 + 2 + 1]; // int32_t are max 10 digits
sprintf(size, "%dx%d+0", width, height);
int fd[2];
if (pipe(fd) != 0) {
fprintf(stderr, "cannot create pipe: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
pid_t child = fork();
if (child < 0) {
fprintf(stderr, "fork() failed\n");
exit(EXIT_FAILURE);
} else if (child != 0) {
close(fd[0]);
if (write(fd[1], data, 4 * width * height) < 0) {
fprintf(stderr, "write() failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
close(fd[1]);
waitpid(child, NULL, 0);
} else {
close(fd[1]);
if (dup2(fd[0], 0) != 0) {
fprintf(stderr, "cannot dup the pipe\n");
exit(EXIT_FAILURE);
}
close(fd[0]);
// We requested WL_SHM_FORMAT_XRGB8888 in little endian, so that's BGRA
// in big endian.
execlp("convert", "convert", "-depth", "8", "-size", size, "bgra:-",
"-alpha", "opaque", filename, NULL);
fprintf(stderr, "cannot execute convert\n");
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[]) {
struct wl_display * display = wl_display_connect(NULL);
if (display == NULL) {
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
wl_list_init(&output_list);
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (screenshooter == NULL) {
fprintf(stderr, "display doesn't support screenshooter\n");
return -1;
}
int i = 0;
struct screenshooter_output *output;
wl_list_for_each(output, &output_list, link) {
void *data = NULL;
struct wl_buffer *buffer =
create_shm_buffer(output->width, output->height, &data);
if (buffer == NULL) {
return -1;
}
struct orbital_screenshot *screenshot = orbital_screenshooter_shoot(
screenshooter, output->output, buffer);
orbital_screenshot_add_listener(screenshot, &screenshot_listener,
screenshot);
buffer_copy_done = false;
while (!buffer_copy_done) {
wl_display_roundtrip(display);
}
char filename[24 + 10]; // int32_t are max 10 digits
snprintf(filename, sizeof(filename), "wayland-screenshot-%d.png", i);
write_image(filename, output->width, output->height, data);
wl_buffer_destroy(buffer);
++i;
}
return EXIT_SUCCESS;
}

View File

@ -1,15 +1,17 @@
#define _POSIX_C_SOURCE 200112L
#include <GLES2/gl2.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/backend/session.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
@ -17,8 +19,10 @@ struct sample_state {
struct wl_display *display;
struct wl_listener new_output;
struct wl_listener new_input;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct timespec last_frame;
float color[3];
float color[4];
int dec;
};
@ -36,10 +40,12 @@ struct sample_keyboard {
struct wl_listener destroy;
};
void output_frame_notify(struct wl_listener *listener, void *data) {
static void output_frame_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output =
wl_container_of(listener, sample_output, frame);
struct sample_state *sample = sample_output->sample;
struct wlr_output *wlr_output = sample_output->output;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
@ -56,16 +62,18 @@ void output_frame_notify(struct wl_listener *listener, void *data) {
sample->dec = inc;
}
wlr_output_make_current(sample_output->output, NULL);
wlr_output_attach_render(wlr_output, NULL);
glClearColor(sample->color[0], sample->color[1], sample->color[2], 1.0);
glClear(GL_COLOR_BUFFER_BIT);
struct wlr_renderer *renderer = sample->renderer;
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(renderer, sample->color);
wlr_renderer_end(renderer);
wlr_output_swap_buffers(sample_output->output, NULL, NULL);
wlr_output_commit(wlr_output);
sample->last_frame = now;
}
void output_remove_notify(struct wl_listener *listener, void *data) {
static void output_remove_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output =
wl_container_of(listener, sample_output, destroy);
wlr_log(WLR_DEBUG, "Output removed");
@ -74,26 +82,31 @@ void output_remove_notify(struct wl_listener *listener, void *data) {
free(sample_output);
}
void new_output_notify(struct wl_listener *listener, void *data) {
static void new_output_notify(struct wl_listener *listener, void *data) {
struct wlr_output *output = data;
struct sample_state *sample =
wl_container_of(listener, sample, new_output);
wlr_output_init_render(output, sample->allocator, sample->renderer);
struct sample_output *sample_output =
calloc(1, sizeof(struct sample_output));
if (!wl_list_empty(&output->modes)) {
struct wlr_output_mode *mode =
wl_container_of(output->modes.prev, mode, link);
wlr_output_set_mode(output, mode);
}
sample_output->output = output;
sample_output->sample = sample;
wl_signal_add(&output->events.frame, &sample_output->frame);
sample_output->frame.notify = output_frame_notify;
wl_signal_add(&output->events.destroy, &sample_output->destroy);
sample_output->destroy.notify = output_remove_notify;
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
if (mode != NULL) {
wlr_output_set_mode(output, mode);
}
wlr_output_commit(sample_output->output);
}
void keyboard_key_notify(struct wl_listener *listener, void *data) {
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
struct sample_state *sample = keyboard->sample;
struct wlr_event_keyboard_key *event = data;
@ -109,7 +122,7 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
}
}
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard =
wl_container_of(listener, keyboard, destroy);
wl_list_remove(&keyboard->destroy.link);
@ -117,7 +130,7 @@ void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
free(keyboard);
}
void new_input_notify(struct wl_listener *listener, void *data) {
static void new_input_notify(struct wl_listener *listener, void *data) {
struct wlr_input_device *device = data;
struct sample_state *sample = wl_container_of(listener, sample, new_input);
switch (device->type) {
@ -130,18 +143,12 @@ void new_input_notify(struct wl_listener *listener, void *data) {
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
keyboard->key.notify = keyboard_key_notify;
struct xkb_rule_names rules = { 0 };
rules.rules = getenv("XKB_DEFAULT_RULES");
rules.model = getenv("XKB_DEFAULT_MODEL");
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
rules.variant = getenv("XKB_DEFAULT_VARIANT");
rules.options = getenv("XKB_DEFAULT_OPTIONS");
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
exit(1);
}
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!keymap) {
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
@ -160,15 +167,19 @@ int main(void) {
wlr_log_init(WLR_DEBUG, NULL);
struct wl_display *display = wl_display_create();
struct sample_state state = {
.color = { 1.0, 0.0, 0.0 },
.color = { 1.0, 0.0, 0.0, 1.0 },
.dec = 0,
.last_frame = { 0 },
.display = display
};
struct wlr_backend *backend = wlr_backend_autocreate(display, NULL);
struct wlr_backend *backend = wlr_backend_autocreate(display);
if (!backend) {
exit(1);
}
state.renderer = wlr_renderer_autocreate(backend);
state.allocator = wlr_allocator_autocreate(backend, state.renderer);
wl_signal_add(&backend->events.new_output, &state.new_output);
state.new_output.notify = new_output_notify;
wl_signal_add(&backend->events.new_input, &state.new_input);

View File

@ -1,28 +1,29 @@
#define _XOPEN_SOURCE 600
#include <GLES2/gl2.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <wayland-server-protocol.h>
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/backend/session.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_tablet_pad.h>
#include <wlr/types/wlr_tablet_tool.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
struct sample_state {
struct wl_display *display;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
bool proximity, tap, button;
double distance;
double pressure;
@ -86,7 +87,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
int32_t width, height;
wlr_output_effective_resolution(wlr_output, &width, &height);
wlr_output_make_current(wlr_output, NULL);
wlr_output_attach_render(wlr_output, NULL);
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
@ -129,7 +130,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
}
wlr_renderer_end(sample->renderer);
wlr_output_swap_buffers(wlr_output, NULL, NULL);
wlr_output_commit(wlr_output);
sample->last_frame = now;
}
@ -228,30 +229,36 @@ static void tablet_pad_destroy_notify(struct wl_listener *listener, void *data)
free(pstate);
}
void output_remove_notify(struct wl_listener *listener, void *data) {
static void output_remove_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
wl_list_remove(&sample_output->frame.link);
wl_list_remove(&sample_output->destroy.link);
free(sample_output);
}
void new_output_notify(struct wl_listener *listener, void *data) {
static void new_output_notify(struct wl_listener *listener, void *data) {
struct wlr_output *output = data;
struct sample_state *sample = wl_container_of(listener, sample, new_output);
wlr_output_init_render(output, sample->allocator, sample->renderer);
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
if (!wl_list_empty(&output->modes)) {
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
wlr_output_set_mode(output, mode);
}
sample_output->output = output;
sample_output->sample = sample;
wl_signal_add(&output->events.frame, &sample_output->frame);
sample_output->frame.notify = output_frame_notify;
wl_signal_add(&output->events.destroy, &sample_output->destroy);
sample_output->destroy.notify = output_remove_notify;
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
if (mode != NULL) {
wlr_output_set_mode(output, mode);
}
wlr_output_commit(output);
}
void keyboard_key_notify(struct wl_listener *listener, void *data) {
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
struct sample_state *sample = keyboard->sample;
struct wlr_event_keyboard_key *event = data;
@ -267,14 +274,14 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
}
}
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
wl_list_remove(&keyboard->destroy.link);
wl_list_remove(&keyboard->key.link);
free(keyboard);
}
void new_input_notify(struct wl_listener *listener, void *data) {
static void new_input_notify(struct wl_listener *listener, void *data) {
struct wlr_input_device *device = data;
struct sample_state *sample = wl_container_of(listener, sample, new_input);
switch (device->type) {
@ -286,18 +293,12 @@ void new_input_notify(struct wl_listener *listener, void *data) {
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
keyboard->key.notify = keyboard_key_notify;
struct xkb_rule_names rules = { 0 };
rules.rules = getenv("XKB_DEFAULT_RULES");
rules.model = getenv("XKB_DEFAULT_MODEL");
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
rules.variant = getenv("XKB_DEFAULT_VARIANT");
rules.options = getenv("XKB_DEFAULT_OPTIONS");
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
exit(1);
}
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!keymap) {
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
@ -354,7 +355,7 @@ int main(int argc, char *argv[]) {
};
wl_list_init(&state.tablet_pads);
wl_list_init(&state.tablet_tools);
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
struct wlr_backend *wlr = wlr_backend_autocreate(display);
if (!wlr) {
exit(1);
}
@ -365,11 +366,14 @@ int main(int argc, char *argv[]) {
state.new_input.notify = new_input_notify;
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
state.renderer = wlr_backend_get_renderer(wlr);
state.renderer = wlr_renderer_autocreate(wlr);
if (!state.renderer) {
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
exit(EXIT_FAILURE);
}
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
if (!wlr_backend_start(wlr)) {
wlr_log(WLR_ERROR, "Failed to start backend");
wlr_backend_destroy(wlr);

View File

@ -6,7 +6,7 @@
#include <unistd.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <wlr/render/egl.h>
#include "egl_common.h"
#include "text-input-unstable-v3-client-protocol.h"
#include "xdg-shell-client-protocol.h"
@ -63,12 +63,11 @@ static struct xdg_wm_base *wm_base = NULL;
static struct zwp_text_input_manager_v3 *text_input_manager = NULL;
static struct zwp_text_input_v3 *text_input = NULL;
struct wlr_egl egl;
struct wl_egl_window *egl_window;
struct wlr_egl_surface *egl_surface;
static void draw(void) {
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
float color[] = {1.0, 1.0, 0.0, 1.0};
color[0] = enabled * 1.0;
@ -78,7 +77,7 @@ static void draw(void) {
glClearColor(color[0], color[1], color[2], 1.0);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(egl.display, egl_surface);
eglSwapBuffers(egl_display, egl_surface);
}
static size_t utf8_strlen(char *str) {
@ -102,7 +101,7 @@ static size_t utf8_offset(char *utf8_str, size_t byte_offset) {
}
// TODO: would be nicer to have this text display inside the window
static void show_status() {
static void show_status(void) {
printf("State %d:", serial);
if (!enabled) {
printf(" disabled");
@ -137,8 +136,7 @@ static void show_status() {
goto end;
}
if ((unsigned)current.preedit.cursor_begin > strlen(preedit_text)
|| (unsigned)current.preedit.cursor_begin > strlen(preedit_text)) {
if ((unsigned)current.preedit.cursor_begin > strlen(preedit_text)) {
printf("Cursor out of bounds\n");
goto end;
}
@ -345,7 +343,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (compositor == NULL) {
@ -365,9 +362,7 @@ int main(int argc, char **argv) {
zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL);
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
WL_SHM_FORMAT_ARGB8888);
egl_init(display);
struct wl_surface *surface = wl_compositor_create_surface(compositor);
struct xdg_surface *xdg_surface =
@ -380,7 +375,8 @@ int main(int argc, char **argv) {
wl_surface_commit(surface);
egl_window = wl_egl_window_create(surface, width, height);
egl_surface = wlr_egl_create_surface(&egl, egl_window);
egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, egl_window, NULL);
wl_display_roundtrip(display);

View File

@ -4,7 +4,7 @@
#include <string.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <wlr/render/egl.h>
#include "egl_common.h"
#include "xdg-shell-client-protocol.h"
#include "xdg-decoration-unstable-v1-client-protocol.h"
@ -20,7 +20,6 @@ static struct wl_compositor *compositor = NULL;
static struct xdg_wm_base *wm_base = NULL;
static struct zxdg_decoration_manager_v1 *decoration_manager = NULL;
struct wlr_egl egl;
struct wl_egl_window *egl_window;
struct wlr_egl_surface *egl_surface;
@ -53,7 +52,7 @@ static void request_preferred_mode(void) {
}
static void draw(void) {
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
float color[] = {1.0, 1.0, 0.0, 1.0};
if (current_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) {
@ -64,7 +63,7 @@ static void draw(void) {
glClearColor(color[0], color[1], color[2], 1.0);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(egl.display, egl_surface);
eglSwapBuffers(egl_display, egl_surface);
}
static void xdg_surface_handle_configure(void *data,
@ -203,7 +202,6 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (compositor == NULL) {
@ -219,8 +217,7 @@ int main(int argc, char **argv) {
return EXIT_FAILURE;
}
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
WL_SHM_FORMAT_ARGB8888);
egl_init(display);
struct wl_surface *surface = wl_compositor_create_surface(compositor);
struct xdg_surface *xdg_surface =
@ -239,7 +236,8 @@ int main(int argc, char **argv) {
wl_surface_commit(surface);
egl_window = wl_egl_window_create(surface, width, height);
egl_surface = wlr_egl_create_surface(&egl, egl_window);
egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, egl_window, NULL);
wl_display_roundtrip(display);

View File

@ -1,5 +1,5 @@
#define _POSIX_C_SOURCE 200112L
#include <GLES2/gl2.h>
#include <drm_fourcc.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
@ -7,15 +7,16 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <wayland-server-protocol.h>
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_output.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_list.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
#include "cat.h"
@ -23,6 +24,7 @@
struct sample_state {
struct wl_display *display;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_texture *cat_texture;
struct wl_list touch_points;
struct timespec last_frame;
@ -73,23 +75,20 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
int32_t width, height;
wlr_output_effective_resolution(wlr_output, &width, &height);
wlr_output_make_current(wlr_output, NULL);
wlr_output_attach_render(wlr_output, NULL);
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
int tex_width, tex_height;
wlr_texture_get_size(sample->cat_texture, &tex_width, &tex_height);
struct touch_point *p;
wl_list_for_each(p, &sample->touch_points, link) {
int x = (int)(p->x * width) - tex_width / 2;
int y = (int)(p->y * height) - tex_height / 2;
int x = (int)(p->x * width) - sample->cat_texture->width / 2;
int y = (int)(p->y * height) - sample->cat_texture->height / 2;
wlr_render_texture(sample->renderer, sample->cat_texture,
wlr_output->transform_matrix, x, y, 1.0f);
}
wlr_renderer_end(sample->renderer);
wlr_output_swap_buffers(wlr_output, NULL, NULL);
wlr_output_commit(wlr_output);
sample->last_frame = now;
}
@ -141,30 +140,36 @@ static void touch_destroy_notify(struct wl_listener *listener, void *data) {
free(tstate);
}
void output_remove_notify(struct wl_listener *listener, void *data) {
static void output_remove_notify(struct wl_listener *listener, void *data) {
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
wl_list_remove(&sample_output->frame.link);
wl_list_remove(&sample_output->destroy.link);
free(sample_output);
}
void new_output_notify(struct wl_listener *listener, void *data) {
static void new_output_notify(struct wl_listener *listener, void *data) {
struct wlr_output *output = data;
struct sample_state *sample = wl_container_of(listener, sample, new_output);
wlr_output_init_render(output, sample->allocator, sample->renderer);
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
if (!wl_list_empty(&output->modes)) {
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
wlr_output_set_mode(output, mode);
}
sample_output->output = output;
sample_output->sample = sample;
wl_signal_add(&output->events.frame, &sample_output->frame);
sample_output->frame.notify = output_frame_notify;
wl_signal_add(&output->events.destroy, &sample_output->destroy);
sample_output->destroy.notify = output_remove_notify;
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
if (mode != NULL) {
wlr_output_set_mode(output, mode);
}
wlr_output_commit(output);
}
void keyboard_key_notify(struct wl_listener *listener, void *data) {
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
struct sample_state *sample = keyboard->sample;
struct wlr_event_keyboard_key *event = data;
@ -180,14 +185,14 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
}
}
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
wl_list_remove(&keyboard->destroy.link);
wl_list_remove(&keyboard->key.link);
free(keyboard);
}
void new_input_notify(struct wl_listener *listener, void *data) {
static void new_input_notify(struct wl_listener *listener, void *data) {
struct wlr_input_device *device = data;
struct sample_state *sample = wl_container_of(listener, sample, new_input);
switch (device->type) {
@ -199,18 +204,12 @@ void new_input_notify(struct wl_listener *listener, void *data) {
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
keyboard->key.notify = keyboard_key_notify;
struct xkb_rule_names rules = { 0 };
rules.rules = getenv("XKB_DEFAULT_RULES");
rules.model = getenv("XKB_DEFAULT_MODEL");
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
rules.variant = getenv("XKB_DEFAULT_VARIANT");
rules.options = getenv("XKB_DEFAULT_OPTIONS");
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
exit(1);
}
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!keymap) {
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
@ -249,7 +248,7 @@ int main(int argc, char *argv[]) {
wl_list_init(&state.touch_points);
wl_list_init(&state.touch);
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
struct wlr_backend *wlr = wlr_backend_autocreate(display);
if (!wlr) {
exit(1);
}
@ -260,20 +259,21 @@ int main(int argc, char *argv[]) {
state.new_input.notify = new_input_notify;
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
state.renderer = wlr_backend_get_renderer(wlr);
state.renderer = wlr_renderer_autocreate(wlr);
if (!state.renderer) {
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
exit(EXIT_FAILURE);
}
state.cat_texture = wlr_texture_from_pixels(state.renderer,
WL_SHM_FORMAT_ARGB8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
DRM_FORMAT_ARGB8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
cat_tex.pixel_data);
if (!state.cat_texture) {
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
exit(EXIT_FAILURE);
}
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
if (!wlr_backend_start(wlr)) {
wlr_log(WLR_ERROR, "Failed to start backend");
wlr_backend_destroy(wlr);

138
examples/virtual-pointer.c Normal file
View File

@ -0,0 +1,138 @@
/*
* Copyright © 2019 Josef Gajdusek
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#define _POSIX_C_SOURCE 200112L
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-client-protocol.h>
#include "wlr-virtual-pointer-unstable-v1-client-protocol.h"
static struct wl_seat *seat = NULL;
static struct zwlr_virtual_pointer_manager_v1 *pointer_manager = NULL;
static void handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version) {
if (strcmp(interface, wl_seat_interface.name) == 0) {
seat = wl_registry_bind(registry, name,
&wl_seat_interface, version);
} else if (strcmp(interface,
zwlr_virtual_pointer_manager_v1_interface.name) == 0) {
pointer_manager = wl_registry_bind(registry, name,
&zwlr_virtual_pointer_manager_v1_interface, 1);
}
}
static void handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name) {
// Who cares?
}
static const struct wl_registry_listener registry_listener = {
.global = handle_global,
.global_remove = handle_global_remove,
};
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: ./virtual-pointer <subcommand>\n");
return EXIT_FAILURE;
}
struct wl_display * display = wl_display_connect(NULL);
if (display == NULL) {
perror("failed to create display");
return EXIT_FAILURE;
}
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_roundtrip(display);
if (pointer_manager == NULL) {
fprintf(stderr, "compositor does not support wlr-virtual-pointer-unstable-v1\n");
return EXIT_FAILURE;
}
struct zwlr_virtual_pointer_v1 *pointer =
zwlr_virtual_pointer_manager_v1_create_virtual_pointer(
pointer_manager, seat);
const char *cmd = argv[1];
if (strcmp(cmd, "motion") == 0) {
if (argc < 4) {
fprintf(stderr, "Usage: ./virtual-pointer motion <dx> <dy>\n");
return EXIT_FAILURE;
}
wl_fixed_t dx = wl_fixed_from_double(atof(argv[2]));
wl_fixed_t dy = wl_fixed_from_double(atof(argv[3]));
zwlr_virtual_pointer_v1_motion(pointer, 0, dx, dy);
} else if (strcmp(cmd, "absolute") == 0) {
if (argc < 6) {
fprintf(stderr, "Usage: ./virtual-pointer absolute <x> <y> <x_extent> <y_extent>\n");
return EXIT_FAILURE;
}
uint32_t x = atoi(argv[2]);
uint32_t y = atoi(argv[3]);
uint32_t x_extent = atoi(argv[4]);
uint32_t y_extent = atoi(argv[5]);
zwlr_virtual_pointer_v1_motion_absolute(pointer, 0, x, y, x_extent, y_extent);
} else if (strcmp(cmd, "button") == 0) {
if (argc < 4) {
fprintf(stderr, "Usage: ./virtual-pointer button <button> press|release\n");
return EXIT_FAILURE;
}
uint32_t button = atoi(argv[2]);
bool press = !!strcmp(argv[3], "release");
zwlr_virtual_pointer_v1_button(pointer, 0, button, press);
} else if (strcmp(cmd, "axis") == 0) {
if (argc < 4) {
fprintf(stderr, "Usage: ./virtual-pointer axis <axis> <value>\n");
return EXIT_FAILURE;
}
uint32_t axis = atoi(argv[2]);
wl_fixed_t value = wl_fixed_from_double(atof(argv[3]));
zwlr_virtual_pointer_v1_axis(pointer, 0, axis, value);
zwlr_virtual_pointer_v1_axis_stop(pointer, 0, axis);
} else if (strcmp(cmd, "axis_discrete") == 0) {
if (argc < 5) {
fprintf(stderr, "Usage: ./virtual-pointer axis <axis> <value> <value_discrete>\n");
return EXIT_FAILURE;
}
uint32_t axis = atoi(argv[2]);
wl_fixed_t value = wl_fixed_from_double(atof(argv[3]));
uint32_t discrete = atoi(argv[4]);
zwlr_virtual_pointer_v1_axis_discrete(pointer, 0, axis, value, discrete);
zwlr_virtual_pointer_v1_axis_stop(pointer, 0, axis);
} else {
fprintf(stderr, "Invalid subcommand\n");
return EXIT_FAILURE;
}
zwlr_virtual_pointer_v1_frame(pointer);
zwlr_virtual_pointer_v1_destroy(pointer);
wl_display_flush(display);
return EXIT_SUCCESS;
}

View File

@ -1,92 +0,0 @@
#!/bin/sh
# Generates a simple GL/EGL extension function loader.
#
# The input is a .txt file, with each function to load on its own line.
# If a line starts with a -, it is optional, and will not cause the loader
# to fail if it can't load the function. You'll need to check if that function
# is NULL before using it.
if [ $# -ne 2 ]; then
exit 1
fi
SPEC=$1
OUTDIR=$2
BASE=$(basename "$SPEC" .txt)
INCLUDE_GUARD=$(printf %s_%s_H "$OUTDIR" "$BASE" | tr -c [:alnum:] _ | tr [:lower:] [:upper:])
DECL=""
DEFN=""
LOADER=""
DECL_FMT='extern %s %s;'
DEFN_FMT='%s %s;'
LOADER_FMT='%s = (%s)eglGetProcAddress("%s");'
CHECK_FMT='if (!%s) {
wlr_log(WLR_ERROR, "Unable to load %s");
return false;
}'
while read -r COMMAND; do
OPTIONAL=0
FUNC_PTR_FMT='PFN%sPROC'
case $COMMAND in
-*)
OPTIONAL=1
;;
esac
case $COMMAND in
*WL)
FUNC_PTR_FMT='PFN%s'
;;
esac
COMMAND=${COMMAND#-}
FUNC_PTR=$(printf "$FUNC_PTR_FMT" "$COMMAND" | tr [:lower:] [:upper:])
DECL="$DECL$(printf "\n$DECL_FMT" "$FUNC_PTR" "$COMMAND")"
DEFN="$DEFN$(printf "\n$DEFN_FMT" "$FUNC_PTR" "$COMMAND")"
LOADER="$LOADER$(printf "\n$LOADER_FMT" "$COMMAND" "$FUNC_PTR" "$COMMAND")"
if [ $OPTIONAL -eq 0 ]; then
LOADER="$LOADER$(printf "\n$CHECK_FMT" "$COMMAND" "$COMMAND")"
fi
done < "$SPEC"
cat > "$OUTDIR/$BASE.h" << EOF
#ifndef $INCLUDE_GUARD
#define $INCLUDE_GUARD
#include <stdbool.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
bool load_$BASE(void);
$DECL
#endif
EOF
cat > "$OUTDIR/$BASE.c" << EOF
#include <wlr/util/log.h>
#include "$BASE.h"
$DEFN
bool load_$BASE(void) {
static bool done = false;
if (done) {
return true;
}
$LOADER
done = true;
return true;
}
EOF

13
include/backend/backend.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef BACKEND_WLR_BACKEND_H
#define BACKEND_WLR_BACKEND_H
#include <wlr/backend.h>
/**
* Get the supported buffer capabilities.
*
* This functions returns a bitfield of supported wlr_buffer_cap.
*/
uint32_t backend_get_buffer_caps(struct wlr_backend *backend);
#endif

13
include/backend/drm/cvt.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef BACKEND_DRM_CVT_H
#define BACKEND_DRM_CVT_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <xf86drmMode.h>
void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay,
float vrefresh, bool reduced, bool interlaced);
#endif

View File

@ -1,67 +1,54 @@
#ifndef BACKEND_DRM_DRM_H
#define BACKEND_DRM_DRM_H
#include <EGL/egl.h>
#include <gbm.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <time.h>
#include <wayland-server.h>
#include <wayland-server-core.h>
#include <wayland-util.h>
#include <wlr/backend/drm.h>
#include <wlr/backend/session.h>
#include <wlr/render/egl.h>
#include <wlr/render/drm_format_set.h>
#include <xf86drmMode.h>
#include "iface.h"
#include "properties.h"
#include "renderer.h"
#include "backend/drm/iface.h"
#include "backend/drm/properties.h"
#include "backend/drm/renderer.h"
struct wlr_drm_plane {
uint32_t type;
uint32_t id;
uint32_t possible_crtcs;
struct wlr_drm_surface surf;
/* Only initialized on multi-GPU setups */
struct wlr_drm_surface mgpu_surf;
uint32_t drm_format; // ARGB8888 or XRGB8888
/* Buffer to be submitted to the kernel on the next page-flip */
struct wlr_drm_fb *pending_fb;
/* Buffer submitted to the kernel, will be presented on next vblank */
struct wlr_drm_fb *queued_fb;
/* Buffer currently displayed on screen */
struct wlr_drm_fb *current_fb;
// Only used by cursor
float matrix[9];
bool cursor_enabled;
int32_t cursor_hotspot_x, cursor_hotspot_y;
struct wlr_drm_format_set formats;
union wlr_drm_plane_props props;
};
struct wlr_drm_crtc {
uint32_t id;
struct wlr_drm_lease *lease;
// Atomic modesetting only
uint32_t mode_id;
uint32_t gamma_lut;
drmModeAtomicReq *atomic;
// Legacy only
drmModeCrtc *legacy_crtc;
union {
struct {
struct wlr_drm_plane *overlay;
struct wlr_drm_plane *primary;
struct wlr_drm_plane *cursor;
};
struct wlr_drm_plane *planes[3];
};
struct wlr_drm_plane *primary;
struct wlr_drm_plane *cursor;
union wlr_drm_crtc_props props;
struct wl_list connectors;
uint16_t *gamma_table;
size_t gamma_table_size;
};
struct wlr_drm_backend {
@ -70,54 +57,45 @@ struct wlr_drm_backend {
struct wlr_drm_backend *parent;
const struct wlr_drm_interface *iface;
clockid_t clock;
bool addfb2_modifiers;
int fd;
char *name;
struct wlr_device *dev;
size_t num_crtcs;
struct wlr_drm_crtc *crtcs;
size_t num_planes;
struct wlr_drm_plane *planes;
union {
struct {
size_t num_overlay_planes;
size_t num_primary_planes;
size_t num_cursor_planes;
};
size_t num_type_planes[3];
};
union {
struct {
struct wlr_drm_plane *overlay_planes;
struct wlr_drm_plane *primary_planes;
struct wlr_drm_plane *cursor_planes;
};
struct wlr_drm_plane *type_planes[3];
};
struct wl_display *display;
struct wl_event_source *drm_event;
struct wl_listener display_destroy;
struct wl_listener session_signal;
struct wl_listener drm_invalidated;
struct wl_listener session_destroy;
struct wl_listener session_active;
struct wl_listener parent_destroy;
struct wl_listener dev_change;
struct wl_listener dev_remove;
struct wl_list fbs; // wlr_drm_fb.link
struct wl_list outputs;
struct wlr_drm_renderer renderer;
/* Only initialized on multi-GPU setups */
struct wlr_drm_renderer mgpu_renderer;
struct wlr_session *session;
uint64_t cursor_width, cursor_height;
struct wlr_drm_format_set mgpu_formats;
};
enum wlr_drm_connector_state {
enum wlr_drm_connector_status {
// Connector is available but no output is plugged in
WLR_DRM_CONN_DISCONNECTED,
// An output just has been plugged in and is waiting for a modeset
WLR_DRM_CONN_NEEDS_MODESET,
WLR_DRM_CONN_CLEANUP,
WLR_DRM_CONN_CONNECTED,
// Connector disappeared, waiting for being destroyed on next page-flip
WLR_DRM_CONN_DISAPPEARED,
};
struct wlr_drm_mode {
@ -125,27 +103,43 @@ struct wlr_drm_mode {
drmModeModeInfo drm_mode;
};
struct wlr_drm_connector {
struct wlr_output output;
struct wlr_drm_connector_state {
const struct wlr_output_state *base;
bool modeset;
bool active;
drmModeModeInfo mode;
};
enum wlr_drm_connector_state state;
struct wlr_output_mode *desired_mode;
struct wlr_drm_connector {
struct wlr_output output; // only valid if status != DISCONNECTED
struct wlr_drm_backend *backend;
char name[24];
enum wlr_drm_connector_status status;
bool desired_enabled;
uint32_t id;
struct wlr_drm_lease *lease;
struct wlr_drm_crtc *crtc;
uint32_t possible_crtc;
uint32_t possible_crtcs;
union wlr_drm_connector_props props;
uint32_t width, height;
int32_t cursor_x, cursor_y;
bool cursor_enabled;
int cursor_x, cursor_y;
int cursor_width, cursor_height;
int cursor_hotspot_x, cursor_hotspot_y;
drmModeCrtc *old_crtc;
bool pageflip_pending;
struct wl_event_source *retry_pageflip;
struct wl_list link;
/* CRTC ID if a page-flip is pending, zero otherwise.
*
* We've asked for a state change in the kernel, and yet to receive a
* notification for its completion. Currently, the kernel only has a
* queue length of 1, and no way to modify your submissions after
* they're sent.
*/
uint32_t pending_page_flip_crtc;
};
struct wlr_drm_backend *get_drm_backend_from_backend(
@ -153,13 +147,24 @@ struct wlr_drm_backend *get_drm_backend_from_backend(
bool check_drm_features(struct wlr_drm_backend *drm);
bool init_drm_resources(struct wlr_drm_backend *drm);
void finish_drm_resources(struct wlr_drm_backend *drm);
void restore_drm_outputs(struct wlr_drm_backend *drm);
void scan_drm_connectors(struct wlr_drm_backend *state);
void scan_drm_connectors(struct wlr_drm_backend *state,
struct wlr_device_hotplug_event *event);
void scan_drm_leases(struct wlr_drm_backend *drm);
int handle_drm_event(int fd, uint32_t mask, void *data);
bool enable_drm_connector(struct wlr_output *output, bool enable);
bool set_drm_connector_gamma(struct wlr_output *output, size_t size,
const uint16_t *r, const uint16_t *g, const uint16_t *b);
bool drm_connector_set_mode(struct wlr_output *output,
struct wlr_output_mode *mode);
void destroy_drm_connector(struct wlr_drm_connector *conn);
bool drm_connector_commit_state(struct wlr_drm_connector *conn,
const struct wlr_output_state *state);
bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn);
bool drm_connector_supports_vrr(struct wlr_drm_connector *conn);
size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc);
void drm_lease_destroy(struct wlr_drm_lease *lease);
struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane);
#define wlr_drm_conn_log(conn, verb, fmt, ...) \
wlr_log(verb, "connector %s: " fmt, conn->name, ##__VA_ARGS__)
#define wlr_drm_conn_log_errno(conn, verb, fmt, ...) \
wlr_log_errno(verb, "connector %s: " fmt, conn->name, ##__VA_ARGS__)
#endif

View File

@ -1,7 +1,6 @@
#ifndef BACKEND_DRM_IFACE_H
#define BACKEND_DRM_IFACE_H
#include <gbm.h>
#include <stdbool.h>
#include <stdint.h>
#include <xf86drm.h>
@ -10,32 +9,20 @@
struct wlr_drm_backend;
struct wlr_drm_connector;
struct wlr_drm_crtc;
struct wlr_drm_connector_state;
// Used to provide atomic or legacy DRM functions
struct wlr_drm_interface {
// Enable or disable DPMS for connector
bool (*conn_enable)(struct wlr_drm_backend *drm,
struct wlr_drm_connector *conn, bool enable);
// Pageflip on crtc. If mode is non-NULL perform a full modeset using it.
bool (*crtc_pageflip)(struct wlr_drm_backend *drm,
struct wlr_drm_connector *conn, struct wlr_drm_crtc *crtc,
uint32_t fb_id, drmModeModeInfo *mode);
// Enable the cursor buffer on crtc. Set bo to NULL to disable
bool (*crtc_set_cursor)(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, struct gbm_bo *bo);
// Move the cursor on crtc
bool (*crtc_move_cursor)(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, int x, int y);
// Set the gamma lut on crtc
bool (*crtc_set_gamma)(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, size_t size,
uint16_t *r, uint16_t *g, uint16_t *b);
// Get the gamma lut size of a crtc
size_t (*crtc_get_gamma_size)(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc);
// Commit all pending changes on a CRTC.
bool (*crtc_commit)(struct wlr_drm_connector *conn,
const struct wlr_drm_connector_state *state, uint32_t flags,
bool test_only);
};
extern const struct wlr_drm_interface atomic_iface;
extern const struct wlr_drm_interface legacy_iface;
bool drm_legacy_crtc_set_gamma(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, size_t size, uint16_t *lut);
#endif

View File

@ -0,0 +1,24 @@
#ifndef BACKEND_DRM_MONITOR_H
#define BACKEND_DRM_MONITOR_H
#include <wlr/backend/drm.h>
/**
* Helper to create new DRM sub-backends on GPU hotplug.
*/
struct wlr_drm_backend_monitor {
struct wlr_backend *multi;
struct wlr_backend *primary_drm;
struct wlr_session *session;
struct wl_listener multi_destroy;
struct wl_listener primary_drm_destroy;
struct wl_listener session_destroy;
struct wl_listener session_add_drm_card;
};
struct wlr_drm_backend_monitor *drm_backend_monitor_create(
struct wlr_backend *multi, struct wlr_backend *primary_drm,
struct wlr_session *session);
#endif

View File

@ -16,6 +16,10 @@ union wlr_drm_connector_props {
uint32_t dpms;
uint32_t link_status; // not guaranteed to exist
uint32_t path;
uint32_t vrr_capable; // not guaranteed to exist
uint32_t subconnector; // not guaranteed to exist
uint32_t non_desktop;
uint32_t panel_orientation; // not guaranteed to exist
// atomic-modesetting only
@ -27,15 +31,14 @@ union wlr_drm_connector_props {
union wlr_drm_crtc_props {
struct {
// Neither of these are guaranteed to exist
uint32_t rotation;
uint32_t scaling_mode;
uint32_t vrr_enabled;
uint32_t gamma_lut;
uint32_t gamma_lut_size;
// atomic-modesetting only
uint32_t active;
uint32_t mode_id;
uint32_t gamma_lut;
uint32_t gamma_lut_size;
};
uint32_t props[6];
};
@ -44,6 +47,7 @@ union wlr_drm_plane_props {
struct {
uint32_t type;
uint32_t rotation; // Not guaranteed to exist
uint32_t in_formats; // Not guaranteed to exist
// atomic-modesetting only
@ -57,8 +61,9 @@ union wlr_drm_plane_props {
uint32_t crtc_h;
uint32_t fb_id;
uint32_t crtc_id;
uint32_t fb_damage_clips;
};
uint32_t props[12];
uint32_t props[14];
};
bool get_drm_connector_props(int fd, uint32_t id,
@ -68,5 +73,6 @@ bool get_drm_plane_props(int fd, uint32_t id, union wlr_drm_plane_props *out);
bool get_drm_prop(int fd, uint32_t obj, uint32_t prop, uint64_t *ret);
void *get_drm_prop_blob(int fd, uint32_t obj, uint32_t prop, size_t *ret_len);
char *get_drm_prop_enum(int fd, uint32_t obj, uint32_t prop);
#endif

View File

@ -1,8 +1,6 @@
#ifndef BACKEND_DRM_RENDERER_H
#define BACKEND_DRM_RENDERER_H
#include <EGL/egl.h>
#include <gbm.h>
#include <stdbool.h>
#include <stdint.h>
#include <wlr/backend.h>
@ -10,15 +8,13 @@
struct wlr_drm_backend;
struct wlr_drm_plane;
struct wlr_buffer;
struct wlr_drm_renderer {
int fd;
struct gbm_device *gbm;
struct wlr_egl egl;
uint32_t gbm_format;
struct wlr_drm_backend *backend;
struct wlr_renderer *wlr_rend;
struct wlr_allocator *allocator;
};
struct wlr_drm_surface {
@ -27,33 +23,38 @@ struct wlr_drm_surface {
uint32_t width;
uint32_t height;
struct gbm_surface *gbm;
EGLSurface egl;
struct wlr_swapchain *swapchain;
};
struct gbm_bo *front;
struct gbm_bo *back;
struct wlr_drm_fb {
struct wlr_buffer *wlr_buf;
struct wlr_addon addon;
struct wlr_drm_backend *backend;
struct wl_list link; // wlr_drm_backend.fbs
uint32_t id;
};
bool init_drm_renderer(struct wlr_drm_backend *drm,
struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_render);
struct wlr_drm_renderer *renderer);
void finish_drm_renderer(struct wlr_drm_renderer *renderer);
bool init_drm_surface(struct wlr_drm_surface *surf,
struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
uint32_t format, uint32_t flags);
const struct wlr_drm_format *drm_format);
bool init_drm_plane_surfaces(struct wlr_drm_plane *plane,
struct wlr_drm_backend *drm, int32_t width, uint32_t height,
uint32_t format);
bool drm_fb_import(struct wlr_drm_fb **fb, struct wlr_drm_backend *drm,
struct wlr_buffer *buf, const struct wlr_drm_format_set *formats);
void drm_fb_destroy(struct wlr_drm_fb *fb);
void finish_drm_surface(struct wlr_drm_surface *surf);
bool make_drm_surface_current(struct wlr_drm_surface *surf, int *buffer_age);
struct gbm_bo *swap_drm_surface_buffers(struct wlr_drm_surface *surf,
pixman_region32_t *damage);
struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf);
void post_drm_surface(struct wlr_drm_surface *surf);
struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest,
struct gbm_bo *src);
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs);
void drm_fb_clear(struct wlr_drm_fb **fb);
void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old);
struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf,
struct wlr_buffer *buffer);
struct wlr_drm_format *drm_plane_pick_render_format(
struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer);
void drm_plane_finish_surface(struct wlr_drm_plane *plane);
#endif

View File

@ -13,8 +13,6 @@ void parse_edid(struct wlr_output *restrict output, size_t len,
const uint8_t *data);
// Returns the string representation of a DRM output type
const char *conn_get_name(uint32_t type_id);
// Returns the DRM framebuffer id for a gbm_bo
uint32_t get_fb_for_bo(struct gbm_bo *bo, uint32_t drm_format);
// Part of match_obj
enum {

View File

@ -8,10 +8,9 @@
struct wlr_headless_backend {
struct wlr_backend backend;
struct wlr_egl egl;
struct wlr_renderer *renderer;
struct wl_display *display;
struct wl_list outputs;
size_t last_output_num;
struct wl_list input_devices;
struct wl_listener display_destroy;
bool started;
@ -23,14 +22,13 @@ struct wlr_headless_output {
struct wlr_headless_backend *backend;
struct wl_list link;
void *egl_surface;
struct wl_event_source *frame_timer;
int frame_delay; // ms
};
struct wlr_headless_input_device {
struct wlr_input_device wlr_input_device;
struct wl_list link;
struct wlr_headless_backend *backend;
};

View File

@ -7,7 +7,6 @@
#include <wlr/backend/libinput.h>
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_list.h>
struct wlr_libinput_backend {
struct wlr_backend backend;
@ -19,14 +18,15 @@ struct wlr_libinput_backend {
struct wl_event_source *input_event;
struct wl_listener display_destroy;
struct wl_listener session_destroy;
struct wl_listener session_signal;
struct wlr_list wlr_device_lists; // list of struct wl_list
struct wl_array wlr_device_lists; // struct wl_list *
};
struct wlr_libinput_input_device {
struct wlr_input_device wlr_input_device;
struct wl_list link;
struct libinput_device *handle;
};
@ -66,6 +66,10 @@ void handle_pointer_pinch_update(struct libinput_event *event,
struct libinput_device *device);
void handle_pointer_pinch_end(struct libinput_event *event,
struct libinput_device *device);
void handle_pointer_hold_begin(struct libinput_event *event,
struct libinput_device *device);
void handle_pointer_hold_end(struct libinput_event *event,
struct libinput_device *device);
struct wlr_switch *create_libinput_switch(
struct libinput_device *device);
@ -82,6 +86,8 @@ void handle_touch_motion(struct libinput_event *event,
struct libinput_device *device);
void handle_touch_cancel(struct libinput_event *event,
struct libinput_device *device);
void handle_touch_frame(struct libinput_event *event,
struct libinput_device *device);
struct wlr_tablet *create_libinput_tablet(
struct libinput_device *device);

View File

@ -1,24 +0,0 @@
#ifndef BACKEND_NOOP_H
#define BACKEND_NOOP_H
#include <wlr/backend/noop.h>
#include <wlr/backend/interface.h>
struct wlr_noop_backend {
struct wlr_backend backend;
struct wl_display *display;
struct wl_list outputs;
bool started;
};
struct wlr_noop_output {
struct wlr_output wlr_output;
struct wlr_noop_backend *backend;
struct wl_list link;
};
struct wlr_noop_backend *noop_backend_from_backend(
struct wlr_backend *wlr_backend);
#endif

View File

@ -1,12 +0,0 @@
#ifndef BACKEND_SESSION_DIRECT_IPC_H
#define BACKEND_SESSION_DIRECT_IPC_H
#include <sys/types.h>
int direct_ipc_open(int sock, const char *path);
void direct_ipc_setmaster(int sock, int fd);
void direct_ipc_dropmaster(int sock, int fd);
void direct_ipc_finish(int sock, pid_t pid);
int direct_ipc_init(pid_t *pid_out);
#endif

View File

@ -0,0 +1,17 @@
#ifndef BACKEND_SESSION_SESSION_H
#define BACKEND_SESSION_SESSION_H
struct wlr_session;
struct wlr_session *libseat_session_create(struct wl_display *disp);
void libseat_session_destroy(struct wlr_session *base);
int libseat_session_open_device(struct wlr_session *base, const char *path);
void libseat_session_close_device(struct wlr_session *base, int fd);
bool libseat_change_vt(struct wlr_session *base, unsigned vt);
void session_init(struct wlr_session *session);
struct wlr_device *session_open_if_kms(struct wlr_session *restrict session,
const char *restrict path);
#endif

View File

@ -4,14 +4,12 @@
#include <stdbool.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <wayland-server.h>
#include <wayland-util.h>
#include <wayland-server-core.h>
#include <wlr/backend/wayland.h>
#include <wlr/render/egl.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/render/drm_format_set.h>
struct wlr_wl_backend {
struct wlr_backend backend;
@ -21,22 +19,48 @@ struct wlr_wl_backend {
struct wl_display *local_display;
struct wl_list devices;
struct wl_list outputs;
struct wlr_egl egl;
struct wlr_renderer *renderer;
int drm_fd;
struct wl_list buffers; // wlr_wl_buffer.link
size_t requested_outputs;
size_t last_output_num;
struct wl_listener local_display_destroy;
char *activation_token;
/* remote state */
struct wl_display *remote_display;
struct wl_event_source *remote_display_src;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct xdg_wm_base *xdg_wm_base;
struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1;
struct zwp_pointer_gestures_v1 *zwp_pointer_gestures_v1;
struct wp_presentation *presentation;
struct wl_shm *shm;
struct wl_seat *seat;
struct wl_pointer *pointer;
struct wl_keyboard *keyboard;
struct wlr_wl_pointer *current_pointer;
char *seat_name;
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1;
struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1;
struct wl_list seats; // wlr_wl_seat.link
struct zwp_tablet_manager_v2 *tablet_manager;
clockid_t presentation_clock;
struct wlr_drm_format_set shm_formats;
struct wlr_drm_format_set linux_dmabuf_v1_formats;
struct wl_drm *legacy_drm;
struct xdg_activation_v1 *activation_v1;
char *drm_render_name;
};
struct wlr_wl_buffer {
struct wlr_buffer *buffer;
struct wl_buffer *wl_buffer;
bool released;
struct wl_list link; // wlr_wl_backend.buffers
struct wl_listener buffer_destroy;
};
struct wlr_wl_presentation_feedback {
struct wlr_wl_output *output;
struct wl_list link;
struct wp_presentation_feedback *feedback;
uint32_t commit_seq;
};
struct wlr_wl_output {
@ -49,23 +73,25 @@ struct wlr_wl_output {
struct wl_callback *frame_callback;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct wl_egl_window *egl_window;
EGLSurface egl_surface;
struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1;
struct wl_list presentation_feedbacks;
uint32_t enter_serial;
struct {
struct wlr_wl_pointer *pointer;
struct wl_surface *surface;
struct wl_egl_window *egl_window;
int32_t hotspot_x, hotspot_y;
int32_t width, height;
} cursor;
};
struct wlr_wl_input_device {
struct wlr_input_device wlr_input_device;
struct wl_list link;
uint32_t fingers;
struct wlr_wl_backend *backend;
struct wlr_wl_seat *seat;
void *resource;
};
@ -74,6 +100,10 @@ struct wlr_wl_pointer {
struct wlr_wl_input_device *input_device;
struct wl_pointer *wl_pointer;
struct zwp_pointer_gesture_swipe_v1 *gesture_swipe;
struct zwp_pointer_gesture_pinch_v1 *gesture_pinch;
struct zwp_pointer_gesture_hold_v1 *gesture_hold;
struct zwp_relative_pointer_v1 *relative_pointer;
enum wlr_axis_source axis_source;
int32_t axis_discrete;
struct wlr_wl_output *output;
@ -81,12 +111,35 @@ struct wlr_wl_pointer {
struct wl_listener output_destroy;
};
struct wlr_wl_seat {
struct wl_seat *wl_seat;
struct wl_list link; // wlr_wl_backend.seats
char *name;
struct wl_touch *touch;
struct wl_pointer *pointer;
struct wl_keyboard *keyboard;
struct wlr_wl_backend *backend;
struct wlr_wl_pointer *active_pointer;
};
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend);
void update_wl_output_cursor(struct wlr_wl_output *output);
struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer);
void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *output);
void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *wl);
void create_wl_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output);
void create_wl_keyboard(struct wlr_wl_seat *seat);
void create_wl_touch(struct wlr_wl_seat *seat);
struct wlr_wl_input_device *create_wl_input_device(
struct wlr_wl_seat *seat, enum wlr_input_device_type type);
bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl);
void destroy_wl_seats(struct wlr_wl_backend *wl);
void destroy_wl_buffer(struct wlr_wl_buffer *buffer);
extern const struct wl_seat_listener seat_listener;
struct wlr_wl_tablet_seat *wl_add_tablet_seat(
struct zwp_tablet_manager_v2 *manager,
struct wlr_wl_seat *seat);
#endif

Some files were not shown because too many files have changed in this diff Show More