diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index b469880..91eb3ec 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -132,6 +132,8 @@ void W_UnregisterBacking(unsigned long id); /* Forward declarations */ #include "wl_types.h" + +extern void wl_protocols_init(void); extern void wm_dock_app_add(struct wl_toplevel_view *v); extern void wm_dock_app_remove(WNativeWindow id); @@ -196,9 +198,9 @@ extern void handle_pointer_motion(struct wl_listener *listener, void *data); extern struct wlr_surface *view_surface_at(int px, int py, double *sx_out, double *sy_out, struct wl_toplevel_view **view_out); extern void handle_seat_request_cursor(struct wl_listener *listener, void *data); -static void handle_seat_request_set_selection(struct wl_listener *listener, void *data); +void handle_seat_request_set_selection(struct wl_listener *listener, void *data); void handle_seat_request_start_drag(struct wl_listener *listener, void *data); -static void handle_seat_request_primary_selection(struct wl_listener *listener, void *data); +void handle_seat_request_primary_selection(struct wl_listener *listener, void *data); /* Compositor-internal clipboard hooks for WINGs. */ void wl_clipboard_store_to_seat(const char *text, int len); char *wl_clipboard_fetch_from_seat(int *len_out); @@ -208,10 +210,10 @@ void wl_drag_icon_show(struct W_View *view, int x, int y); void wl_drag_icon_move(int x, int y); void wl_drag_icon_hide(void); void wl_drag_cursor_feedback(Bool accepted); -static void handle_idle_new_inhibitor(struct wl_listener *listener, void *data); +void handle_idle_new_inhibitor(struct wl_listener *listener, void *data); extern void handle_pointer_axis(struct wl_listener *listener, void *data); -static void handle_new_pointer_constraint(struct wl_listener *listener, void *data); -static void handle_new_shortcuts_inhibitor(struct wl_listener *listener, void *data); +void handle_new_pointer_constraint(struct wl_listener *listener, void *data); +void handle_new_shortcuts_inhibitor(struct wl_listener *listener, void *data); extern void handle_output_mgr_apply(struct wl_listener *listener, void *data); extern void handle_output_mgr_test(struct wl_listener *listener, void *data); extern void handle_output_power_set_mode(struct wl_listener *listener, void *data); @@ -524,250 +526,7 @@ wl_display_open(const char *name) wl_state.socket_name, getenv("DISPLAY") ? getenv("DISPLAY") : "(unset)"); - wl_state.compositor = - wlr_compositor_create(wl_state.display, 6, wl_state.renderer); - wlr_subcompositor_create(wl_state.display); - wl_state.seat = wlr_seat_create(wl_state.display, "seat0"); - wl_state.data_device_mgr = - wlr_data_device_manager_create(wl_state.display); - - if (wl_state.seat) { - wl_state.seat_request_cursor_listener.notify = handle_seat_request_cursor; - wl_signal_add(&wl_state.seat->events.request_set_cursor, - &wl_state.seat_request_cursor_listener); - } - /* handle clipboard set-selection requests on the seat so - * Ctrl-C / copy operations from clients propagate to the seat selection - * and become available for paste in other clients. */ - if (wl_state.seat) { - wl_state.seat_request_set_selection_listener.notify = - handle_seat_request_set_selection; - wl_signal_add(&wl_state.seat->events.request_set_selection, - &wl_state.seat_request_set_selection_listener); - wwarning("wl_backend: clipboard selection wired"); - - wl_state.seat_request_start_drag_listener.notify = - handle_seat_request_start_drag; - wl_signal_add(&wl_state.seat->events.request_start_drag, - &wl_state.seat_request_start_drag_listener); - wwarning("wl_backend: DND start_drag wired"); - } - - /* primary selection (middle-click paste) via - * zwp_primary_selection_device_manager_v1. */ - wl_state.primary_selection_mgr = - wlr_primary_selection_v1_device_manager_create(wl_state.display); - if (wl_state.primary_selection_mgr && wl_state.seat) { - wl_state.seat_request_primary_selection_listener.notify = - handle_seat_request_primary_selection; - wl_signal_add(&wl_state.seat->events.request_set_primary_selection, - &wl_state.seat_request_primary_selection_listener); - wwarning("wl_backend: primary selection (middle-click) wired"); - } - - /* xdg-output — panels and HiDPI apps query logical output - * geometry via zxdg_output_manager_v1. Requires output_layout. */ - if (wl_state.output_layout) { - wl_state.xdg_output_mgr = - wlr_xdg_output_manager_v1_create(wl_state.display, - wl_state.output_layout); - if (wl_state.xdg_output_mgr) - wwarning("wl_backend: xdg-output-manager created"); - } - - /* idle-inhibit — video players and games create inhibitor - * objects to suppress screen blanking while active. */ - wl_state.idle_inhibit_mgr = - wlr_idle_inhibit_v1_create(wl_state.display); - if (wl_state.idle_inhibit_mgr) { - wl_state.idle_new_inhibitor_listener.notify = handle_idle_new_inhibitor; - wl_signal_add(&wl_state.idle_inhibit_mgr->events.new_inhibitor, - &wl_state.idle_new_inhibitor_listener); - wwarning("wl_backend: idle-inhibit manager created"); - } - - /* wp_viewporter — hardware video scaling and subsurface damage - * subset used by mpv, Chromium, Firefox, SDL2. */ - wl_state.viewporter = wlr_viewporter_create(wl_state.display); - if (wl_state.viewporter) - wwarning("wl_backend: wp_viewporter created"); - else - wwarning("wl_backend: wp_viewporter unavailable"); - - wl_state.relative_pointer_mgr = - wlr_relative_pointer_manager_v1_create(wl_state.display); - if (wl_state.relative_pointer_mgr) - wwarning("wl_backend: relative-pointer-manager created"); - - wl_state.pointer_constraints = - wlr_pointer_constraints_v1_create(wl_state.display); - if (wl_state.pointer_constraints) { - wl_state.new_constraint_listener.notify = handle_new_pointer_constraint; - wl_signal_add(&wl_state.pointer_constraints->events.new_constraint, - &wl_state.new_constraint_listener); - wwarning("wl_backend: pointer-constraints created"); - } - - /* keyboard shortcuts inhibitor — fullscreen apps / remote desktop - * request to suppress WM keyboard shortcuts. */ - wl_state.shortcuts_inhibit_mgr = - wlr_keyboard_shortcuts_inhibit_v1_create(wl_state.display); - if (wl_state.shortcuts_inhibit_mgr) { - wl_state.new_shortcuts_inhibitor_listener.notify = - handle_new_shortcuts_inhibitor; - wl_signal_add(&wl_state.shortcuts_inhibit_mgr->events.new_inhibitor, - &wl_state.new_shortcuts_inhibitor_listener); - wwarning("wl_backend: keyboard-shortcuts-inhibit created"); - } - /* GPU zero-copy (linux-dmabuf), screencopy, export-dmabuf. - * All three are purely passive globals: wlroots handles the protocol - * implementation internally; no event listeners are required. */ - wl_state.linux_dmabuf = - wlr_linux_dmabuf_v1_create_with_renderer(wl_state.display, 4, wl_state.renderer); - if (wl_state.linux_dmabuf) - wwarning("wl_backend: linux-dmabuf created (zwp_linux_dmabuf_v1)"); - else - wwarning("wl_backend: linux-dmabuf unavailable (renderer has no DRM FD)"); - wl_state.screencopy_mgr = - wlr_screencopy_manager_v1_create(wl_state.display); - if (wl_state.screencopy_mgr) - wwarning("wl_backend: screencopy manager created (zwlr_screencopy_manager_v1)"); - wl_state.export_dmabuf_mgr = - wlr_export_dmabuf_manager_v1_create(wl_state.display); - if (wl_state.export_dmabuf_mgr) - wwarning("wl_backend: export-dmabuf manager created (zwlr_export_dmabuf_manager_v1)"); - - /* output management (wlr-output-management), output power (DPMS), - * gamma control (Night Light / redshift), and presentation time feedback. */ - wl_state.output_mgr = wlr_output_manager_v1_create(wl_state.display); - if (wl_state.output_mgr) { - wl_state.output_mgr_apply_listener.notify = handle_output_mgr_apply; - wl_signal_add(&wl_state.output_mgr->events.apply, - &wl_state.output_mgr_apply_listener); - wl_state.output_mgr_test_listener.notify = handle_output_mgr_test; - wl_signal_add(&wl_state.output_mgr->events.test, - &wl_state.output_mgr_test_listener); - wwarning("wl_backend: output-management created (zwlr_output_management_v1)"); - } - wl_state.output_power_mgr = wlr_output_power_manager_v1_create(wl_state.display); - if (wl_state.output_power_mgr) { - wl_state.output_power_set_mode_listener.notify = handle_output_power_set_mode; - wl_signal_add(&wl_state.output_power_mgr->events.set_mode, - &wl_state.output_power_set_mode_listener); - wwarning("wl_backend: output-power-management created (zwlr_output_power_management_v1)"); - } - wl_state.gamma_control_mgr = wlr_gamma_control_manager_v1_create(wl_state.display); - if (wl_state.gamma_control_mgr) - wwarning("wl_backend: gamma-control created (zwlr_gamma_control_manager_v1)"); - wl_state.presentation = wlr_presentation_create(wl_state.display, wl_state.backend, 1); - if (wl_state.presentation) - wwarning("wl_backend: presentation-time created (wp_presentation)"); - - /* text input v3, input method v2, virtual keyboard v1. - * All three are passive globals for IME / on-screen keyboard support. */ - wl_state.text_input_mgr = - wlr_text_input_manager_v3_create(wl_state.display); - if (wl_state.text_input_mgr) { - wl_state.text_input_new_listener.notify = handle_text_input_new; - wl_signal_add(&wl_state.text_input_mgr->events.text_input, - &wl_state.text_input_new_listener); - wwarning("wl_backend: text-input-v3 created (zwp_text_input_manager_v3)"); - } - wl_state.input_method_mgr = - wlr_input_method_manager_v2_create(wl_state.display); - if (wl_state.input_method_mgr) { - wl_state.input_method_new_listener.notify = handle_input_method_new; - wl_signal_add(&wl_state.input_method_mgr->events.input_method, - &wl_state.input_method_new_listener); - wwarning("wl_backend: input-method-v2 created (zwp_input_method_manager_v2)"); - } - wl_state.virtual_keyboard_mgr = - wlr_virtual_keyboard_manager_v1_create(wl_state.display); - if (wl_state.virtual_keyboard_mgr) { - wl_state.virtual_keyboard_new_listener.notify = handle_virtual_keyboard_new; - wl_signal_add(&wl_state.virtual_keyboard_mgr->events.new_virtual_keyboard, - &wl_state.virtual_keyboard_new_listener); - wwarning("wl_backend: virtual-keyboard-v1 created (zwp_virtual_keyboard_manager_v1)"); - } - - /* foreign toplevel management — lets taskbars enumerate windows. - * xdg_activation — focus-token protocol replacing _NET_ACTIVE_WINDOW. */ - wl_state.foreign_toplevel_mgr = - wlr_foreign_toplevel_manager_v1_create(wl_state.display); - if (wl_state.foreign_toplevel_mgr) - wwarning("wl_backend: foreign-toplevel-management created (zwlr_foreign_toplevel_management_v1)"); - wl_state.xdg_activation = wlr_xdg_activation_v1_create(wl_state.display); - if (wl_state.xdg_activation) { - wl_state.xdg_activation_request_listener.notify = handle_xdg_activation_request; - wl_signal_add(&wl_state.xdg_activation->events.request_activate, - &wl_state.xdg_activation_request_listener); - wwarning("wl_backend: xdg-activation created (xdg_activation_v1)"); - } - - wl_state.layer_shell = wlr_layer_shell_v1_create(wl_state.display, 4); - if (wl_state.layer_shell) { - wl_state.layer_shell_new_surface_listener.notify = - handle_layer_shell_new_surface; - wl_signal_add(&wl_state.layer_shell->events.new_surface, - &wl_state.layer_shell_new_surface_listener); - wwarning("wl_backend: layer-shell created (zwlr_layer_shell_v1)"); - } - - /* clipboard manager (wl-clipboard/cliphist), input inhibitor - * (lock screen input gating), virtual pointer (remote desktop / VNC), - * KDE idle protocol, xdg-foreign v2 (cross-client window embedding). */ - wl_state.data_control_mgr = - wlr_data_control_manager_v1_create(wl_state.display); - if (wl_state.data_control_mgr) - wwarning("wl_backend: data-control created (zwlr_data_control_manager_v1)"); - - /* zwlr_input_inhibitor_v1 removed from wlroots 0.18 — use ext-session-lock-v1 */ - - wl_state.virtual_pointer_mgr = - wlr_virtual_pointer_manager_v1_create(wl_state.display); - if (wl_state.virtual_pointer_mgr) { - wl_state.virtual_pointer_new_listener.notify = - handle_virtual_pointer_new; - wl_signal_add(&wl_state.virtual_pointer_mgr->events.new_virtual_pointer, - &wl_state.virtual_pointer_new_listener); - wwarning("wl_backend: virtual-pointer created (zwlr_virtual_pointer_manager_v1)"); - } - - /* org_kde_kwin_idle (wlr_idle) removed from wlroots 0.18 — use ext-idle-notify-v1 */ - - wl_state.xdg_foreign_registry = - wlr_xdg_foreign_registry_create(wl_state.display); - if (wl_state.xdg_foreign_registry) { - wl_state.xdg_foreign_v2 = - wlr_xdg_foreign_v2_create(wl_state.display, - wl_state.xdg_foreign_registry); - if (wl_state.xdg_foreign_v2) - wwarning("wl_backend: xdg-foreign-v2 created (zxdg_exporter/importer_v2)"); - } - - wl_state.xdg_shell = wlr_xdg_shell_create(wl_state.display, 3); - - /* Advertise wmaker-popup-v1 — clients use it to drive popup positioning. */ - wl_state.wmaker_popup_manager = wl_global_create(wl_state.display, - &wmaker_popup_manager_v1_interface, 1, NULL, - wmaker_popup_manager_bind); - if (wl_state.wmaker_popup_manager) - wwarning("wl_backend: wmaker-popup-v1 manager created"); - - /* advertise zxdg_decoration_manager_v1 so clients that - * support server-side decorations can negotiate; we respond with - * CLIENT_SIDE since the Wayland path has no X11 frame rendering. */ - wl_state.decoration_manager = - wlr_xdg_decoration_manager_v1_create(wl_state.display); - if (wl_state.decoration_manager) { - wl_state.decoration_new_listener.notify = handle_new_toplevel_decoration; - wl_signal_add(&wl_state.decoration_manager->events.new_toplevel_decoration, - &wl_state.decoration_new_listener); - wwarning("wl_backend: xdg-decoration-manager created"); - } - - wlr_renderer_init_wl_shm(wl_state.renderer, wl_state.display); - /* wl_output global is created in handle_backend_new_output. */ + wl_protocols_init(); /* 53: In wlroots 0.19, the headless backend has no input devices. * Real keyboard/pointer devices arrive via handle_backend_new_input when @@ -1395,94 +1154,6 @@ wl_event_loop_terminate(void) /* * seat request_set_selection — client calls wl_data_device.set_selection * (i.e. Ctrl-C or other clipboard write). Forward to wlr_seat so the - * selection becomes available to the next client that reads it. - */ -static void -handle_seat_request_set_selection(struct wl_listener *listener, void *data) -{ - (void)listener; - struct wlr_seat_request_set_selection_event *ev = data; - if (!wl_state.seat) - return; - wlr_seat_set_selection(wl_state.seat, ev->source, ev->serial); -} - - -/* - * seat request_set_primary_selection — client writes to the - * primary selection buffer (e.g. by selecting text). Forward to - * wlr_seat so middle-click paste works in other clients. - */ -static void -handle_seat_request_primary_selection(struct wl_listener *listener, void *data) -{ - (void)listener; - struct wlr_seat_request_set_primary_selection_event *ev = data; - if (!wl_state.seat) - return; - wlr_seat_set_primary_selection(wl_state.seat, ev->source, ev->serial); -} - -/* - * idle_inhibit new_inhibitor — a client (e.g. mpv, a game) created - * an idle-inhibit object. wlroots tracks the inhibitor list internally; - * the compositor can query wl_state.idle_inhibit_mgr->inhibitors to decide - * whether to suppress screen blanking. For now we just log the event so - * the capability is advertised and the inhibitor object doesn't error out. - */ -static void -handle_idle_new_inhibitor(struct wl_listener *listener, void *data) -{ - (void)listener; - struct wlr_idle_inhibitor_v1 *inhibitor = data; - (void)inhibitor; - wwarning("wl_backend: idle inhibitor created (client suppressing screen blank)"); -} - -/* - * scroll-wheel axis event handler. - * wlr_event_pointer_axis carries orientation (vertical/horizontal), delta, - * delta_discrete (notches), and source (wheel vs finger). Forward directly - * to the seat so the focused Wayland client receives wl_pointer.axis events. - * Without this, scrolling is silently dropped in every Wayland client. - */ -/* - * new pointer constraint — a client requested pointer lock or - * pointer confinement (e.g. a game locking the cursor to its window). - * We activate the constraint immediately so the client gets confirmed. - * In headless mode the pointer is virtual so there is nothing physically - * to lock, but activating keeps the client happy. - */ -static void -handle_new_pointer_constraint(struct wl_listener *listener, void *data) -{ - (void)listener; - struct wlr_pointer_constraint_v1 *constraint = data; - (void)constraint; - /* Activate immediately — in nested mode the real pointer is bounded - * by the host compositor's window anyway. */ - wlr_pointer_constraint_v1_send_activated(constraint); - wwarning("wl_backend: pointer constraint activated"); -} - -/* - * new keyboard shortcuts inhibitor — a fullscreen app or remote - * desktop client wants to suppress WM keyboard shortcuts. - * Activate the inhibitor immediately so the client knows it is granted. - * The existing wWaylandKeyPress() dispatcher in handle_keyboard_key already - * forwards all non-shortcut keys; with the inhibitor active we should skip - * the shortcut check for that focused client. For now we simply activate - * and log; per-client inhibitor tracking can be refined later. - */ -static void -handle_new_shortcuts_inhibitor(struct wl_listener *listener, void *data) -{ - (void)listener; - struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor = data; - wlr_keyboard_shortcuts_inhibitor_v1_activate(inhibitor); - wwarning("wl_backend: keyboard shortcuts inhibitor activated"); -} - diff --git a/src/backend/wayland/wl_extensions.c b/src/backend/wayland/wl_extensions.c index f3aa387..a16ee66 100644 --- a/src/backend/wayland/wl_extensions.c +++ b/src/backend/wayland/wl_extensions.c @@ -14,6 +14,50 @@ #include "../../screen.h" #include "wl_types.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "protocols/pointer-constraints-unstable-v1-protocol.h" +#include +#include +#include +#include +#include +#include +#include "protocols/wlr-output-power-management-unstable-v1-protocol.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "protocols/wlr-layer-shell-unstable-v1-protocol.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wmaker-popup-v1-server-protocol.h" + + + /* ------------------------------------------------------------------ */ /* Extensions / cursors / error handlers */ @@ -164,6 +208,286 @@ wl_texture_create_gc(WScreen *scr, unsigned long pixel) (void) scr; (void) pixel; return NULL; } +/* ------------------------------------------------------------------ */ +/* Protocol globals — called from wl_display_open() */ + + +/* Forward declarations for handlers defined in other backend modules */ +extern void handle_seat_request_cursor(struct wl_listener *listener, void *data); +extern void handle_seat_request_start_drag(struct wl_listener *listener, void *data); +extern void handle_output_mgr_apply(struct wl_listener *listener, void *data); +extern void handle_output_mgr_test(struct wl_listener *listener, void *data); +extern void handle_output_power_set_mode(struct wl_listener *listener, void *data); +extern void handle_text_input_new(struct wl_listener *listener, void *data); +extern void handle_input_method_new(struct wl_listener *listener, void *data); +extern void handle_virtual_keyboard_new(struct wl_listener *listener, void *data); +extern void handle_xdg_activation_request(struct wl_listener *listener, void *data); +extern void handle_layer_shell_new_surface(struct wl_listener *listener, void *data); +extern void handle_virtual_pointer_new(struct wl_listener *listener, void *data); +extern void handle_new_toplevel_decoration(struct wl_listener *listener, void *data); +extern void wmaker_popup_manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id); + + +/* Forward declarations for handlers defined in wl_handlers.c */ +extern void handle_seat_request_set_selection(struct wl_listener *listener, void *data); +extern void handle_seat_request_primary_selection(struct wl_listener *listener, void *data); +extern void handle_idle_new_inhibitor(struct wl_listener *listener, void *data); +extern void handle_new_pointer_constraint(struct wl_listener *listener, void *data); +extern void handle_new_shortcuts_inhibitor(struct wl_listener *listener, void *data); + +void +wl_protocols_init(void) +{ + wl_state.compositor = + wlr_compositor_create(wl_state.display, 6, wl_state.renderer); + wlr_subcompositor_create(wl_state.display); + wl_state.seat = wlr_seat_create(wl_state.display, "seat0"); + wl_state.data_device_mgr = + wlr_data_device_manager_create(wl_state.display); + + if (wl_state.seat) { + wl_state.seat_request_cursor_listener.notify = handle_seat_request_cursor; + wl_signal_add(&wl_state.seat->events.request_set_cursor, + &wl_state.seat_request_cursor_listener); + } + /* handle clipboard set-selection requests on the seat so + * Ctrl-C / copy operations from clients propagate to the seat selection + * and become available for paste in other clients. */ + if (wl_state.seat) { + wl_state.seat_request_set_selection_listener.notify = + handle_seat_request_set_selection; + wl_signal_add(&wl_state.seat->events.request_set_selection, + &wl_state.seat_request_set_selection_listener); + wwarning("wl_backend: clipboard selection wired"); + + wl_state.seat_request_start_drag_listener.notify = + handle_seat_request_start_drag; + wl_signal_add(&wl_state.seat->events.request_start_drag, + &wl_state.seat_request_start_drag_listener); + wwarning("wl_backend: DND start_drag wired"); + } + + /* primary selection (middle-click paste) via + * zwp_primary_selection_device_manager_v1. */ + wl_state.primary_selection_mgr = + wlr_primary_selection_v1_device_manager_create(wl_state.display); + if (wl_state.primary_selection_mgr && wl_state.seat) { + wl_state.seat_request_primary_selection_listener.notify = + handle_seat_request_primary_selection; + wl_signal_add(&wl_state.seat->events.request_set_primary_selection, + &wl_state.seat_request_primary_selection_listener); + wwarning("wl_backend: primary selection (middle-click) wired"); + } + + /* xdg-output — panels and HiDPI apps query logical output + * geometry via zxdg_output_manager_v1. Requires output_layout. */ + if (wl_state.output_layout) { + wl_state.xdg_output_mgr = + wlr_xdg_output_manager_v1_create(wl_state.display, + wl_state.output_layout); + if (wl_state.xdg_output_mgr) + wwarning("wl_backend: xdg-output-manager created"); + } + + /* idle-inhibit — video players and games create inhibitor + * objects to suppress screen blanking while active. */ + wl_state.idle_inhibit_mgr = + wlr_idle_inhibit_v1_create(wl_state.display); + if (wl_state.idle_inhibit_mgr) { + wl_state.idle_new_inhibitor_listener.notify = handle_idle_new_inhibitor; + wl_signal_add(&wl_state.idle_inhibit_mgr->events.new_inhibitor, + &wl_state.idle_new_inhibitor_listener); + wwarning("wl_backend: idle-inhibit manager created"); + } + + /* wp_viewporter — hardware video scaling and subsurface damage + * subset used by mpv, Chromium, Firefox, SDL2. */ + wl_state.viewporter = wlr_viewporter_create(wl_state.display); + if (wl_state.viewporter) + wwarning("wl_backend: wp_viewporter created"); + else + wwarning("wl_backend: wp_viewporter unavailable"); + + wl_state.relative_pointer_mgr = + wlr_relative_pointer_manager_v1_create(wl_state.display); + if (wl_state.relative_pointer_mgr) + wwarning("wl_backend: relative-pointer-manager created"); + + wl_state.pointer_constraints = + wlr_pointer_constraints_v1_create(wl_state.display); + if (wl_state.pointer_constraints) { + wl_state.new_constraint_listener.notify = handle_new_pointer_constraint; + wl_signal_add(&wl_state.pointer_constraints->events.new_constraint, + &wl_state.new_constraint_listener); + wwarning("wl_backend: pointer-constraints created"); + } + + /* keyboard shortcuts inhibitor — fullscreen apps / remote desktop + * request to suppress WM keyboard shortcuts. */ + wl_state.shortcuts_inhibit_mgr = + wlr_keyboard_shortcuts_inhibit_v1_create(wl_state.display); + if (wl_state.shortcuts_inhibit_mgr) { + wl_state.new_shortcuts_inhibitor_listener.notify = + handle_new_shortcuts_inhibitor; + wl_signal_add(&wl_state.shortcuts_inhibit_mgr->events.new_inhibitor, + &wl_state.new_shortcuts_inhibitor_listener); + wwarning("wl_backend: keyboard-shortcuts-inhibit created"); + } + /* GPU zero-copy (linux-dmabuf), screencopy, export-dmabuf. + * All three are purely passive globals: wlroots handles the protocol + * implementation internally; no event listeners are required. */ + wl_state.linux_dmabuf = + wlr_linux_dmabuf_v1_create_with_renderer(wl_state.display, 4, wl_state.renderer); + if (wl_state.linux_dmabuf) + wwarning("wl_backend: linux-dmabuf created (zwp_linux_dmabuf_v1)"); + else + wwarning("wl_backend: linux-dmabuf unavailable (renderer has no DRM FD)"); + wl_state.screencopy_mgr = + wlr_screencopy_manager_v1_create(wl_state.display); + if (wl_state.screencopy_mgr) + wwarning("wl_backend: screencopy manager created (zwlr_screencopy_manager_v1)"); + wl_state.export_dmabuf_mgr = + wlr_export_dmabuf_manager_v1_create(wl_state.display); + if (wl_state.export_dmabuf_mgr) + wwarning("wl_backend: export-dmabuf manager created (zwlr_export_dmabuf_manager_v1)"); + + /* output management (wlr-output-management), output power (DPMS), + * gamma control (Night Light / redshift), and presentation time feedback. */ + wl_state.output_mgr = wlr_output_manager_v1_create(wl_state.display); + if (wl_state.output_mgr) { + wl_state.output_mgr_apply_listener.notify = handle_output_mgr_apply; + wl_signal_add(&wl_state.output_mgr->events.apply, + &wl_state.output_mgr_apply_listener); + wl_state.output_mgr_test_listener.notify = handle_output_mgr_test; + wl_signal_add(&wl_state.output_mgr->events.test, + &wl_state.output_mgr_test_listener); + wwarning("wl_backend: output-management created (zwlr_output_management_v1)"); + } + wl_state.output_power_mgr = wlr_output_power_manager_v1_create(wl_state.display); + if (wl_state.output_power_mgr) { + wl_state.output_power_set_mode_listener.notify = handle_output_power_set_mode; + wl_signal_add(&wl_state.output_power_mgr->events.set_mode, + &wl_state.output_power_set_mode_listener); + wwarning("wl_backend: output-power-management created (zwlr_output_power_management_v1)"); + } + wl_state.gamma_control_mgr = wlr_gamma_control_manager_v1_create(wl_state.display); + if (wl_state.gamma_control_mgr) + wwarning("wl_backend: gamma-control created (zwlr_gamma_control_manager_v1)"); + wl_state.presentation = wlr_presentation_create(wl_state.display, wl_state.backend, 1); + if (wl_state.presentation) + wwarning("wl_backend: presentation-time created (wp_presentation)"); + + /* text input v3, input method v2, virtual keyboard v1. + * All three are passive globals for IME / on-screen keyboard support. */ + wl_state.text_input_mgr = + wlr_text_input_manager_v3_create(wl_state.display); + if (wl_state.text_input_mgr) { + wl_state.text_input_new_listener.notify = handle_text_input_new; + wl_signal_add(&wl_state.text_input_mgr->events.text_input, + &wl_state.text_input_new_listener); + wwarning("wl_backend: text-input-v3 created (zwp_text_input_manager_v3)"); + } + wl_state.input_method_mgr = + wlr_input_method_manager_v2_create(wl_state.display); + if (wl_state.input_method_mgr) { + wl_state.input_method_new_listener.notify = handle_input_method_new; + wl_signal_add(&wl_state.input_method_mgr->events.input_method, + &wl_state.input_method_new_listener); + wwarning("wl_backend: input-method-v2 created (zwp_input_method_manager_v2)"); + } + wl_state.virtual_keyboard_mgr = + wlr_virtual_keyboard_manager_v1_create(wl_state.display); + if (wl_state.virtual_keyboard_mgr) { + wl_state.virtual_keyboard_new_listener.notify = handle_virtual_keyboard_new; + wl_signal_add(&wl_state.virtual_keyboard_mgr->events.new_virtual_keyboard, + &wl_state.virtual_keyboard_new_listener); + wwarning("wl_backend: virtual-keyboard-v1 created (zwp_virtual_keyboard_manager_v1)"); + } + + /* foreign toplevel management — lets taskbars enumerate windows. + * xdg_activation — focus-token protocol replacing _NET_ACTIVE_WINDOW. */ + wl_state.foreign_toplevel_mgr = + wlr_foreign_toplevel_manager_v1_create(wl_state.display); + if (wl_state.foreign_toplevel_mgr) + wwarning("wl_backend: foreign-toplevel-management created (zwlr_foreign_toplevel_management_v1)"); + wl_state.xdg_activation = wlr_xdg_activation_v1_create(wl_state.display); + if (wl_state.xdg_activation) { + wl_state.xdg_activation_request_listener.notify = handle_xdg_activation_request; + wl_signal_add(&wl_state.xdg_activation->events.request_activate, + &wl_state.xdg_activation_request_listener); + wwarning("wl_backend: xdg-activation created (xdg_activation_v1)"); + } + + wl_state.layer_shell = wlr_layer_shell_v1_create(wl_state.display, 4); + if (wl_state.layer_shell) { + wl_state.layer_shell_new_surface_listener.notify = + handle_layer_shell_new_surface; + wl_signal_add(&wl_state.layer_shell->events.new_surface, + &wl_state.layer_shell_new_surface_listener); + wwarning("wl_backend: layer-shell created (zwlr_layer_shell_v1)"); + } + + /* clipboard manager (wl-clipboard/cliphist), input inhibitor + * (lock screen input gating), virtual pointer (remote desktop / VNC), + * KDE idle protocol, xdg-foreign v2 (cross-client window embedding). */ + wl_state.data_control_mgr = + wlr_data_control_manager_v1_create(wl_state.display); + if (wl_state.data_control_mgr) + wwarning("wl_backend: data-control created (zwlr_data_control_manager_v1)"); + + /* zwlr_input_inhibitor_v1 removed from wlroots 0.18 — use ext-session-lock-v1 */ + + wl_state.virtual_pointer_mgr = + wlr_virtual_pointer_manager_v1_create(wl_state.display); + if (wl_state.virtual_pointer_mgr) { + wl_state.virtual_pointer_new_listener.notify = + handle_virtual_pointer_new; + wl_signal_add(&wl_state.virtual_pointer_mgr->events.new_virtual_pointer, + &wl_state.virtual_pointer_new_listener); + wwarning("wl_backend: virtual-pointer created (zwlr_virtual_pointer_manager_v1)"); + } + + /* org_kde_kwin_idle (wlr_idle) removed from wlroots 0.18 — use ext-idle-notify-v1 */ + + wl_state.xdg_foreign_registry = + wlr_xdg_foreign_registry_create(wl_state.display); + if (wl_state.xdg_foreign_registry) { + wl_state.xdg_foreign_v2 = + wlr_xdg_foreign_v2_create(wl_state.display, + wl_state.xdg_foreign_registry); + if (wl_state.xdg_foreign_v2) + wwarning("wl_backend: xdg-foreign-v2 created (zxdg_exporter/importer_v2)"); + } + + wl_state.xdg_shell = wlr_xdg_shell_create(wl_state.display, 3); + + /* Advertise wmaker-popup-v1 — clients use it to drive popup positioning. */ + wl_state.wmaker_popup_manager = wl_global_create(wl_state.display, + &wmaker_popup_manager_v1_interface, 1, NULL, + wmaker_popup_manager_bind); + if (wl_state.wmaker_popup_manager) + wwarning("wl_backend: wmaker-popup-v1 manager created"); + + /* advertise zxdg_decoration_manager_v1 so clients that + * support server-side decorations can negotiate; we respond with + * CLIENT_SIDE since the Wayland path has no X11 frame rendering. */ + wl_state.decoration_manager = + wlr_xdg_decoration_manager_v1_create(wl_state.display); + if (wl_state.decoration_manager) { + wl_state.decoration_new_listener.notify = handle_new_toplevel_decoration; + wl_signal_add(&wl_state.decoration_manager->events.new_toplevel_decoration, + &wl_state.decoration_new_listener); + wwarning("wl_backend: xdg-decoration-manager created"); + } + + wlr_renderer_init_wl_shm(wl_state.renderer, wl_state.display); + /* wl_output global is created in handle_backend_new_output. */ + +} + + + void wl_texture_free_gc(GC gc)