1221 lines
47 KiB
C
1221 lines
47 KiB
C
/*
|
||
* wl_backend.c -- wlroots-based Wayland compositor backend for Window Maker
|
||
*
|
||
* Phase 31: complete stub vtable. Every slot compiles and returns a safe
|
||
* zero/null/false value. Subsequent phases will replace stubs with real
|
||
* wlroots implementations one functional area at a time.
|
||
* Phase 32: display_open/close/screen_count implemented with wlroots headless
|
||
* backend.
|
||
* Phase 34: Wayland compositor socket created (clients can connect);
|
||
* wlr_output_layout added; monitors_query returns real head geometry.
|
||
* Phase 35: Core Wayland protocol globals advertised (wl_compositor,
|
||
* wl_subcompositor, wl_seat, wl_data_device_manager, xdg_wm_base).
|
||
* Phase 36: wl_shm and wl_output advertised; XDG toplevel listener wired.
|
||
* Phase 37: frame callbacks driven by output frame event; XDG configure
|
||
* sent on new toplevel so clients complete their first frame.
|
||
* Phase 38: per-toplevel view struct with map/unmap/destroy lifecycle;
|
||
* synthetic WNativeWindow IDs; context_save/find/delete implemented.
|
||
* Phase 39: handle_toplevel_map wires XDG toplevel map to wManageWindow so
|
||
* Wayland surfaces appear in scr->focused_window list.
|
||
* Phase 40: handle_toplevel_unmap wires XDG toplevel unmap to wUnmanageWindow.
|
||
* Phase 41: prop_get_wm_class and window_fetch_name return XDG app_id/title.
|
||
* Phase 42: window_get_client_attrs returns WCLIENT_VIEWABLE + surface
|
||
* geometry for mapped XDG toplevels.
|
||
* Phase 43: focus_set delivers keyboard focus via wlr_seat; headless keyboard
|
||
* device created and attached to seat; mapped toplevels auto-focused.
|
||
* Phase 44: client_synth_configure, window_resize send XDG configure events
|
||
* so clients receive size changes from the WM.
|
||
* Phase 45: pointer_query returns 1 (always on-screen); wWindowFocus/Unfocus
|
||
* guarded on frame!=NULL so wSetFocusTo works without a frame window.
|
||
* Phase 46: client_close vtable slot (wlr_xdg_toplevel_send_close);
|
||
* title_listener updates wwin->wm_instance/wm_class on set_title signal;
|
||
* wClientSendProtocol/wClientKill guarded for Wayland.
|
||
* Phase 47: prop_get_normal_hints returns XDG toplevel min/max/current size.
|
||
*/
|
||
|
||
#ifdef HAVE_CONFIG_H
|
||
# include "config.h"
|
||
#endif
|
||
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <unistd.h>
|
||
|
||
#include <wayland-server-core.h>
|
||
#include <wlr/backend.h>
|
||
#include <wlr/backend/headless.h>
|
||
#include <wlr/render/wlr_renderer.h>
|
||
#include <wlr/interfaces/wlr_output.h>
|
||
#include <wlr/types/wlr_compositor.h>
|
||
#include <wlr/types/wlr_data_device.h>
|
||
#include <wlr/types/wlr_output.h>
|
||
#include <wlr/types/wlr_output_layout.h>
|
||
#include <wlr/types/wlr_seat.h>
|
||
#include <wlr/types/wlr_keyboard.h>
|
||
#include <wlr/types/wlr_xdg_shell.h>
|
||
#include <wlr/util/box.h>
|
||
/* Generated xdg-shell protocol implementation (Wayland glue code) */
|
||
#include "protocols/xdg-shell-protocol.c"
|
||
#include <wlr/util/log.h>
|
||
|
||
#include "../../WindowMaker.h"
|
||
#include "../../screen.h"
|
||
#include "../../window.h"
|
||
#include "../backend.h"
|
||
#include "wl_backend.h"
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Phase 38: per-toplevel view and context store */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
/*
|
||
* wl_toplevel_view -- one entry per XDG toplevel managed by the compositor.
|
||
* Holds the wlroots surface pointer, three lifecycle listeners, and a
|
||
* synthetic WNativeWindow ID used as the policy-layer window handle.
|
||
*/
|
||
struct wl_toplevel_view {
|
||
struct wlr_xdg_surface *xdg_surface;
|
||
struct wl_listener map_listener;
|
||
struct wl_listener unmap_listener;
|
||
struct wl_listener destroy_listener;
|
||
struct wl_listener title_listener; /* Phase 46: xdg_toplevel set_title */
|
||
WNativeWindow id; /* synthetic handle, >= 0x10000000 */
|
||
struct wl_list link; /* wl_state.toplevel_list */
|
||
};
|
||
|
||
/*
|
||
* Context store: three per-key linked lists keyed by WNativeWindow.
|
||
* Backed by a flat list of (win, data) pairs — fine for the small
|
||
* number of windows a typical user has open.
|
||
*/
|
||
struct wl_ctx_entry {
|
||
WNativeWindow win;
|
||
void *data;
|
||
struct wl_list link;
|
||
};
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Global compositor state */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
struct wl_compositor_state {
|
||
struct wl_display *display;
|
||
struct wlr_backend *backend;
|
||
struct wlr_renderer *renderer;
|
||
struct wlr_output *output; /* headless output (screen 0) */
|
||
struct wlr_output_layout *output_layout; /* Phase 34 */
|
||
/* Phase 35: core Wayland protocol globals */
|
||
struct wlr_compositor *compositor;
|
||
struct wlr_seat *seat;
|
||
struct wlr_data_device_manager *data_device_mgr;
|
||
struct wlr_xdg_shell *xdg_shell;
|
||
/* Phase 36: XDG toplevel tracking */
|
||
struct wl_listener xdg_new_surface_listener;
|
||
int managed_surface_count; /* surfaces seen */
|
||
/* Phase 37: periodic timer that drives client frame callbacks */
|
||
struct wl_event_source *frame_timer;
|
||
/* Phase 38: per-toplevel view list and context store */
|
||
struct wl_list toplevel_list; /* wl_toplevel_view.link */
|
||
struct wl_list ctx_lists[3]; /* one per WContextKey */
|
||
unsigned long next_synthetic_id; /* >= 0x10000000 */
|
||
/* Phase 43: headless keyboard device for seat keyboard focus */
|
||
struct wlr_input_device *keyboard_dev;
|
||
char socket_name[64]; /* Phase 34: e.g. "wayland-0" */
|
||
int started; /* non-zero after successful display_open */
|
||
};
|
||
|
||
static struct wl_compositor_state wl_state;
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Phase 36 / 37 / 38: XDG surface lifecycle, frame loop, view mgmt */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
/*
|
||
* Phase 37: Wayland event loop timer — drives client frame callbacks.
|
||
*/
|
||
static int
|
||
frame_timer_cb(void *data)
|
||
{
|
||
(void)data;
|
||
if (wl_state.output)
|
||
wlr_output_send_frame(wl_state.output);
|
||
if (wl_state.frame_timer)
|
||
wl_event_source_timer_update(wl_state.frame_timer, 16);
|
||
return 0;
|
||
}
|
||
|
||
/* Helper: recover the view from one of its embedded listeners. */
|
||
#define VIEW_FROM_LISTENER(ptr, field) \
|
||
((struct wl_toplevel_view *)((char *)(ptr) - offsetof(struct wl_toplevel_view, field)))
|
||
|
||
/* Phase 43: forward declaration — defined in the focus section below. */
|
||
static void wl_focus_set(WNativeWindow win, int revert_to, unsigned long timestamp);
|
||
|
||
/* Phase 41: look up the view for a given synthetic WNativeWindow ID. */
|
||
static struct wl_toplevel_view *
|
||
wl_find_view_by_id(WNativeWindow id)
|
||
{
|
||
struct wl_toplevel_view *v;
|
||
wl_list_for_each(v, &wl_state.toplevel_list, link) {
|
||
if (v->id == id)
|
||
return v;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* Phase 46: XDG toplevel title changed — update wwin->wm_instance.
|
||
*/
|
||
static void
|
||
handle_toplevel_title(struct wl_listener *listener, void *data)
|
||
{
|
||
struct wl_toplevel_view *view = VIEW_FROM_LISTENER(listener, title_listener);
|
||
(void)data;
|
||
const char *new_title = view->xdg_surface->toplevel->title;
|
||
|
||
WObjDescriptor *desc = wm_backend->context_find(view->id, WM_CTX_CLIENT_WIN);
|
||
if (!desc || desc->parent_type != WCLASS_WINDOW)
|
||
return;
|
||
WWindow *wwin = (WWindow *)desc->parent;
|
||
|
||
if (wwin->wm_instance)
|
||
wfree(wwin->wm_instance);
|
||
wwin->wm_instance = new_title ? strdup(new_title) : NULL;
|
||
|
||
if (wwin->wm_class)
|
||
wfree(wwin->wm_class);
|
||
const char *app_id = (view->xdg_surface->toplevel->app_id)
|
||
? view->xdg_surface->toplevel->app_id : "wayland-client";
|
||
wwin->wm_class = strdup(app_id);
|
||
|
||
wWindowUpdateName(wwin, new_title);
|
||
}
|
||
|
||
/*
|
||
* Phase 38: toplevel map — surface is ready to be managed.
|
||
*/
|
||
static void
|
||
handle_toplevel_map(struct wl_listener *listener, void *data)
|
||
{
|
||
struct wl_toplevel_view *view = VIEW_FROM_LISTENER(listener, map_listener);
|
||
(void)data;
|
||
|
||
wwarning("wl_backend: toplevel mapped id=0x%lx app_id=%s title=%s",
|
||
view->id,
|
||
view->xdg_surface->toplevel->app_id ? view->xdg_surface->toplevel->app_id : "(none)",
|
||
view->xdg_surface->toplevel->title ? view->xdg_surface->toplevel->title : "(none)");
|
||
|
||
/* Phase 39/41: register the surface as a managed window. */
|
||
{
|
||
WScreen *scr = wScreenWithNumber(0);
|
||
if (scr) {
|
||
WWindow *wwin = wManageWindow(scr, view->id);
|
||
if (wwin) {
|
||
wwarning("wl_backend: wManageWindow succeeded id=0x%lx "
|
||
"class=%s name=%s geo=%dx%d",
|
||
view->id,
|
||
wwin->wm_class ? wwin->wm_class : "(none)",
|
||
wwin->wm_instance ? wwin->wm_instance : "(none)",
|
||
wwin->client.width, wwin->client.height);
|
||
/* Phase 43: focus the newly mapped toplevel. */
|
||
wl_focus_set(view->id, 0, 0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Phase 40: toplevel unmap — surface hidden or destroyed; unmanage the window.
|
||
*/
|
||
static void
|
||
handle_toplevel_unmap(struct wl_listener *listener, void *data)
|
||
{
|
||
struct wl_toplevel_view *view = VIEW_FROM_LISTENER(listener, unmap_listener);
|
||
(void)data;
|
||
|
||
wwarning("wl_backend: toplevel unmapped id=0x%lx", view->id);
|
||
|
||
/* Phase 40: remove the WWindow from the WM's managed list. */
|
||
WScreen *scr = wScreenWithNumber(0);
|
||
if (scr) {
|
||
WObjDescriptor *desc = wm_backend->context_find(view->id, WM_CTX_CLIENT_WIN);
|
||
if (desc && desc->parent_type == WCLASS_WINDOW) {
|
||
WWindow *wwin = (WWindow *)desc->parent;
|
||
wUnmanageWindow(wwin, False, True);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Phase 38: toplevel destroy — free the view.
|
||
*/
|
||
static void
|
||
handle_toplevel_destroy(struct wl_listener *listener, void *data)
|
||
{
|
||
struct wl_toplevel_view *view = VIEW_FROM_LISTENER(listener, destroy_listener);
|
||
(void)data;
|
||
|
||
wl_list_remove(&view->map_listener.link);
|
||
wl_list_remove(&view->unmap_listener.link);
|
||
wl_list_remove(&view->destroy_listener.link);
|
||
wl_list_remove(&view->title_listener.link);
|
||
wl_list_remove(&view->link);
|
||
wl_state.managed_surface_count--;
|
||
wfree(view);
|
||
}
|
||
|
||
/*
|
||
* Phase 36 / 38: new XDG surface listener.
|
||
* Allocates a wl_toplevel_view, assigns a synthetic WNativeWindow ID,
|
||
* wires the three lifecycle listeners, and schedules the initial configure.
|
||
*/
|
||
static void
|
||
handle_xdg_new_surface(struct wl_listener *listener, void *data)
|
||
{
|
||
struct wlr_xdg_surface *xdg_surface = data;
|
||
struct wl_toplevel_view *view;
|
||
(void)listener;
|
||
|
||
if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL)
|
||
return; /* ignore popups for now */
|
||
|
||
view = wmalloc(sizeof(*view));
|
||
memset(view, 0, sizeof(*view));
|
||
view->xdg_surface = xdg_surface;
|
||
view->id = wl_state.next_synthetic_id++;
|
||
|
||
view->map_listener.notify = handle_toplevel_map;
|
||
view->unmap_listener.notify = handle_toplevel_unmap;
|
||
view->destroy_listener.notify = handle_toplevel_destroy;
|
||
view->title_listener.notify = handle_toplevel_title;
|
||
|
||
wl_signal_add(&xdg_surface->events.map, &view->map_listener);
|
||
wl_signal_add(&xdg_surface->events.unmap, &view->unmap_listener);
|
||
wl_signal_add(&xdg_surface->events.destroy, &view->destroy_listener);
|
||
wl_signal_add(&xdg_surface->toplevel->events.set_title, &view->title_listener);
|
||
|
||
wl_list_insert(&wl_state.toplevel_list, &view->link);
|
||
wl_state.managed_surface_count++;
|
||
|
||
wwarning("wl_backend: new XDG toplevel id=0x%lx app_id=%s title=%s",
|
||
view->id,
|
||
xdg_surface->toplevel->app_id ? xdg_surface->toplevel->app_id : "(none)",
|
||
xdg_surface->toplevel->title ? xdg_surface->toplevel->title : "(none)");
|
||
|
||
/* Phase 37: initial configure so the client can commit its first buffer. */
|
||
wlr_xdg_surface_schedule_configure(xdg_surface);
|
||
}
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Display lifecycle (Phase 32 / Phase 34) */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static void *
|
||
wl_display_open(const char *name)
|
||
{
|
||
const char *sock;
|
||
|
||
(void)name; /* Wayland compositor picks its own socket name */
|
||
|
||
wlr_log_init(WLR_ERROR, NULL);
|
||
|
||
wl_state.display = wl_display_create();
|
||
if (!wl_state.display) {
|
||
wwarning("wl_backend: failed to create wl_display");
|
||
return NULL;
|
||
}
|
||
|
||
wl_state.backend = wlr_headless_backend_create(wl_state.display);
|
||
if (!wl_state.backend) {
|
||
wwarning("wl_backend: failed to create wlroots headless backend");
|
||
wl_display_destroy(wl_state.display);
|
||
wl_state.display = NULL;
|
||
return NULL;
|
||
}
|
||
|
||
wl_state.renderer = wlr_renderer_autocreate(wl_state.backend);
|
||
if (!wl_state.renderer) {
|
||
wwarning("wl_backend: failed to create wlroots renderer");
|
||
wlr_backend_destroy(wl_state.backend);
|
||
wl_display_destroy(wl_state.display);
|
||
wl_state.backend = NULL;
|
||
wl_state.display = NULL;
|
||
return NULL;
|
||
}
|
||
|
||
if (!wlr_backend_start(wl_state.backend)) {
|
||
wwarning("wl_backend: failed to start wlroots headless backend");
|
||
wlr_renderer_destroy(wl_state.renderer);
|
||
wlr_backend_destroy(wl_state.backend);
|
||
wl_display_destroy(wl_state.display);
|
||
wl_state.renderer = NULL;
|
||
wl_state.backend = NULL;
|
||
wl_state.display = NULL;
|
||
return NULL;
|
||
}
|
||
|
||
/* Phase 34: create the UNIX socket so Wayland clients can connect. */
|
||
sock = wl_display_add_socket_auto(wl_state.display);
|
||
if (!sock) {
|
||
wwarning("wl_backend: failed to create Wayland compositor socket");
|
||
wlr_renderer_destroy(wl_state.renderer);
|
||
wlr_backend_destroy(wl_state.backend);
|
||
wl_display_destroy(wl_state.display);
|
||
wl_state.renderer = NULL;
|
||
wl_state.backend = NULL;
|
||
wl_state.display = NULL;
|
||
return NULL;
|
||
}
|
||
snprintf(wl_state.socket_name, sizeof(wl_state.socket_name), "%s", sock);
|
||
setenv("WAYLAND_DISPLAY", wl_state.socket_name, 1);
|
||
wwarning("wl_backend: compositor socket WAYLAND_DISPLAY=%s", wl_state.socket_name);
|
||
|
||
/* Phase 34: output layout for multi-head geometry tracking. */
|
||
wl_state.output_layout = wlr_output_layout_create();
|
||
|
||
/* Add a single 1920×1080 headless output (screen 0). */
|
||
wl_state.output = wlr_headless_add_output(wl_state.backend, 1920, 1080);
|
||
if (wl_state.output && wl_state.output_layout)
|
||
wlr_output_layout_add_auto(wl_state.output_layout, wl_state.output);
|
||
|
||
/* Phase 35: advertise core Wayland protocol globals. */
|
||
wl_state.compositor =
|
||
wlr_compositor_create(wl_state.display, wl_state.renderer);
|
||
wl_state.seat = wlr_seat_create(wl_state.display, "seat0");
|
||
wl_state.data_device_mgr =
|
||
wlr_data_device_manager_create(wl_state.display);
|
||
wl_state.xdg_shell = wlr_xdg_shell_create(wl_state.display);
|
||
|
||
/* Phase 36: advertise wl_shm (needed by SHM buffer clients) and
|
||
* expose the headless output as a wl_output global. */
|
||
wlr_renderer_init_wl_shm(wl_state.renderer, wl_state.display);
|
||
if (wl_state.output)
|
||
wlr_output_create_global(wl_state.output);
|
||
|
||
/* Phase 43: create a headless keyboard and attach it to the seat so
|
||
* wlr_seat_keyboard_notify_enter() can deliver keyboard focus. */
|
||
wl_state.keyboard_dev =
|
||
wlr_headless_add_input_device(wl_state.backend, WLR_INPUT_DEVICE_KEYBOARD);
|
||
if (wl_state.keyboard_dev) {
|
||
wlr_seat_set_keyboard(wl_state.seat, wl_state.keyboard_dev);
|
||
wlr_seat_set_capabilities(wl_state.seat, WL_SEAT_CAPABILITY_KEYBOARD);
|
||
}
|
||
|
||
/* Phase 38: initialise per-toplevel tracking and context store. */
|
||
wl_list_init(&wl_state.toplevel_list);
|
||
{
|
||
int ki;
|
||
for (ki = 0; ki < 3; ki++)
|
||
wl_list_init(&wl_state.ctx_lists[ki]);
|
||
}
|
||
wl_state.next_synthetic_id = 0x10000000UL;
|
||
wl_state.managed_surface_count = 0;
|
||
|
||
/* Phase 36: listen for new XDG toplevel surfaces. */
|
||
wl_state.xdg_new_surface_listener.notify = handle_xdg_new_surface;
|
||
wl_signal_add(&wl_state.xdg_shell->events.new_surface,
|
||
&wl_state.xdg_new_surface_listener);
|
||
|
||
/* Phase 37: periodic timer to drive client frame callbacks at ~60 fps.
|
||
* Using a timer avoids the swapchain/allocator complexity of the full
|
||
* render pipeline while still keeping Wayland clients unblocked. */
|
||
{
|
||
struct wl_event_loop *loop =
|
||
wl_display_get_event_loop(wl_state.display);
|
||
wl_state.frame_timer =
|
||
wl_event_loop_add_timer(loop, frame_timer_cb, NULL);
|
||
if (wl_state.frame_timer)
|
||
wl_event_source_timer_update(wl_state.frame_timer, 16);
|
||
}
|
||
|
||
wl_state.started = 1;
|
||
return NULL; /* no X11 Display* in the Wayland path */
|
||
}
|
||
|
||
static void
|
||
wl_display_close(void)
|
||
{
|
||
if (!wl_state.started)
|
||
return;
|
||
if (wl_state.frame_timer) {
|
||
wl_event_source_remove(wl_state.frame_timer);
|
||
wl_state.frame_timer = NULL;
|
||
}
|
||
if (wl_state.output_layout) {
|
||
wlr_output_layout_destroy(wl_state.output_layout);
|
||
wl_state.output_layout = NULL;
|
||
}
|
||
if (wl_state.renderer)
|
||
wlr_renderer_destroy(wl_state.renderer);
|
||
if (wl_state.backend)
|
||
wlr_backend_destroy(wl_state.backend);
|
||
if (wl_state.display)
|
||
wl_display_destroy(wl_state.display);
|
||
unsetenv("WAYLAND_DISPLAY");
|
||
wl_state.renderer = NULL;
|
||
wl_state.backend = NULL;
|
||
wl_state.display = NULL;
|
||
wl_state.output = NULL;
|
||
wl_state.started = 0;
|
||
}
|
||
|
||
static int
|
||
wl_display_screen_count(void)
|
||
{
|
||
return wl_state.started ? 1 : 0;
|
||
}
|
||
|
||
static int
|
||
wl_display_default_screen(void)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
static unsigned long
|
||
wl_atom_intern(const char *name)
|
||
{
|
||
(void)name;
|
||
return 0; /* atoms have no meaning in native Wayland */
|
||
}
|
||
|
||
static void
|
||
wl_event_loop_run(void)
|
||
{
|
||
if (wl_state.display)
|
||
wl_display_run(wl_state.display);
|
||
}
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Screen open/close */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static void
|
||
wl_screen_open(WScreen *scr)
|
||
{
|
||
/* Set geometry from the headless output created in display_open(). */
|
||
scr->scr_width = wl_state.output ?
|
||
(int)wl_state.output->width : 1920;
|
||
scr->scr_height = wl_state.output ?
|
||
(int)wl_state.output->height : 1080;
|
||
scr->depth = 24;
|
||
scr->root_win = WNATIVE_NONE;
|
||
scr->colormap = 0;
|
||
}
|
||
|
||
static void wl_screen_close(WScreen *scr) { (void)scr; }
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Frame / window lifecycle */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static WNativeWindow
|
||
wl_frame_create_toplevel(WScreen *scr,
|
||
int x, int y, int width, int height,
|
||
int border_width,
|
||
int depth, void *visual,
|
||
unsigned long colormap,
|
||
WMPixel border_pixel)
|
||
{
|
||
(void)scr; (void)x; (void)y; (void)width; (void)height;
|
||
(void)border_width; (void)depth; (void)visual;
|
||
(void)colormap; (void)border_pixel;
|
||
return WNATIVE_NONE;
|
||
}
|
||
|
||
static WNativeWindow
|
||
wl_frame_create_child(WNativeWindow parent_win, WScreen *scr,
|
||
int x, int y, int width, int height)
|
||
{
|
||
(void)parent_win; (void)scr;
|
||
(void)x; (void)y; (void)width; (void)height;
|
||
return WNATIVE_NONE;
|
||
}
|
||
|
||
static void wl_frame_destroy(WScreen *scr, WNativeWindow win) { (void)scr; (void)win; }
|
||
|
||
static void
|
||
wl_frame_configure(WNativeWindow win, int x, int y, int width, int height)
|
||
{ (void)win; (void)x; (void)y; (void)width; (void)height; }
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Context (window → object lookup table) — Phase 38 */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static void
|
||
wl_context_save(WNativeWindow win, WContextKey key, void *data)
|
||
{
|
||
struct wl_list *list = &wl_state.ctx_lists[(int)key];
|
||
struct wl_ctx_entry *e;
|
||
|
||
/* update existing entry if present */
|
||
wl_list_for_each(e, list, link) {
|
||
if (e->win == win) { e->data = data; return; }
|
||
}
|
||
/* new entry */
|
||
e = wmalloc(sizeof(*e));
|
||
e->win = win;
|
||
e->data = data;
|
||
wl_list_insert(list, &e->link);
|
||
}
|
||
|
||
static void *
|
||
wl_context_find(WNativeWindow win, WContextKey key)
|
||
{
|
||
struct wl_list *list = &wl_state.ctx_lists[(int)key];
|
||
struct wl_ctx_entry *e;
|
||
|
||
wl_list_for_each(e, list, link) {
|
||
if (e->win == win) return e->data;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
static int
|
||
wl_context_delete(WNativeWindow win, WContextKey key)
|
||
{
|
||
struct wl_list *list = &wl_state.ctx_lists[(int)key];
|
||
struct wl_ctx_entry *e, *tmp;
|
||
|
||
wl_list_for_each_safe(e, tmp, list, link) {
|
||
if (e->win == win) {
|
||
wl_list_remove(&e->link);
|
||
wfree(e);
|
||
return 1;
|
||
}
|
||
}
|
||
return 0; /* not found */
|
||
}
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Focus */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static void
|
||
wl_focus_set(WNativeWindow win, int revert_to, unsigned long timestamp)
|
||
{
|
||
/* Phase 43: deliver keyboard focus to the XDG toplevel surface. */
|
||
(void)revert_to; (void)timestamp;
|
||
if (!wl_state.seat || !wl_state.keyboard_dev)
|
||
return;
|
||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||
if (!v || !v->xdg_surface || !v->xdg_surface->surface)
|
||
return;
|
||
wlr_seat_keyboard_notify_enter(wl_state.seat,
|
||
v->xdg_surface->surface,
|
||
NULL, 0, NULL);
|
||
wwarning("wl_backend: focus_set id=0x%lx", win);
|
||
}
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Key / button grabs */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static void
|
||
wl_key_grab(WNativeWindow win, unsigned int keycode,
|
||
unsigned int modifier, int owner_events)
|
||
{ (void)win; (void)keycode; (void)modifier; (void)owner_events; }
|
||
|
||
static void wl_key_ungrab_all(WNativeWindow win) { (void)win; }
|
||
|
||
static void
|
||
wl_button_grab(WNativeWindow win, unsigned int button,
|
||
unsigned int modifier, unsigned int event_mask,
|
||
int owner_events)
|
||
{ (void)win; (void)button; (void)modifier; (void)event_mask; (void)owner_events; }
|
||
|
||
static void wl_button_ungrab_all(WNativeWindow win) { (void)win; }
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Pointer grabs */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static int
|
||
wl_pointer_grab(WNativeWindow grab_win, WNativeWindow confine_to,
|
||
unsigned int event_mask, int cursor_id,
|
||
int owner_events, unsigned long timestamp)
|
||
{
|
||
(void)grab_win; (void)confine_to; (void)event_mask;
|
||
(void)cursor_id; (void)owner_events; (void)timestamp;
|
||
return 0;
|
||
}
|
||
|
||
static void wl_pointer_ungrab(unsigned long timestamp) { (void)timestamp; }
|
||
|
||
static void wl_pointer_warp(int dx, int dy) { (void)dx; (void)dy; }
|
||
static void wl_pointer_warp_to(int x, int y) { (void)x; (void)y; }
|
||
|
||
static int
|
||
wl_pointer_query(WNativeWindow root, int *root_x, int *root_y)
|
||
{
|
||
/* Phase 45: the Wayland compositor IS the screen; always on-screen. */
|
||
(void)root;
|
||
*root_x = *root_y = 0;
|
||
return wl_state.started ? 1 : 0;
|
||
}
|
||
|
||
static int
|
||
wl_pointer_query_full(WNativeWindow root,
|
||
WNativeWindow *child_out,
|
||
int *root_x, int *root_y,
|
||
unsigned int *buttons_out)
|
||
{
|
||
/* Phase 45: same — always on-screen when compositor is running. */
|
||
(void)root;
|
||
if (child_out) *child_out = WNATIVE_NONE;
|
||
*root_x = *root_y = 0;
|
||
if (buttons_out) *buttons_out = 0;
|
||
return wl_state.started ? 1 : 0;
|
||
}
|
||
|
||
static void
|
||
wl_pointer_change_grab(unsigned int event_mask, int cursor_id,
|
||
unsigned long timestamp)
|
||
{ (void)event_mask; (void)cursor_id; (void)timestamp; }
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Keyboard grabs */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static int wl_keyboard_grab(WNativeWindow win) { (void)win; return 0; }
|
||
static void wl_keyboard_ungrab(void) {}
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Server grab */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static void wl_server_grab(void) {}
|
||
static void wl_server_ungrab(void) {}
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Stacking */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static void wl_stacking_raise(WNativeWindow win) { (void)win; }
|
||
static void wl_stacking_lower(WNativeWindow win) { (void)win; }
|
||
|
||
static void
|
||
wl_stacking_restack(WNativeWindow *windows, int count)
|
||
{ (void)windows; (void)count; }
|
||
|
||
static int
|
||
wl_stacking_query(WNativeWindow root,
|
||
WNativeWindow **out, unsigned int *count)
|
||
{
|
||
(void)root;
|
||
*out = NULL; *count = 0;
|
||
return 0;
|
||
}
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Events */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static void wl_event_flush(void) {}
|
||
static void wl_event_flush_expose(void) {}
|
||
static void wl_event_sync(int discard) { (void)discard; }
|
||
static int wl_event_pending(void) { return 0; }
|
||
static void wl_event_drain_motion(void) {}
|
||
static void wl_event_discard_enter_leave_until(unsigned long timestamp) { (void)timestamp; }
|
||
static void wl_event_handle_extensions(void *event) { (void)event; }
|
||
static void wl_event_randr_update(void *event) { (void)event; }
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Properties */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static int
|
||
wl_prop_get_normal_hints(WNativeWindow win, WNormalHints *out, int *pre_icccm)
|
||
{
|
||
/* Phase 47: populate size constraints from XDG toplevel. */
|
||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||
memset(out, 0, sizeof(*out));
|
||
*pre_icccm = 0;
|
||
if (!v || !v->xdg_surface || !v->xdg_surface->toplevel)
|
||
return 0;
|
||
struct wlr_xdg_toplevel *tl = v->xdg_surface->toplevel;
|
||
if (tl->current.min_width > 0 || tl->current.min_height > 0) {
|
||
out->flags |= WNH_P_MIN_SIZE;
|
||
out->min_width = (int)tl->current.min_width;
|
||
out->min_height = (int)tl->current.min_height;
|
||
}
|
||
if (tl->current.max_width > 0 || tl->current.max_height > 0) {
|
||
out->flags |= WNH_P_MAX_SIZE;
|
||
out->max_width = (int)tl->current.max_width;
|
||
out->max_height = (int)tl->current.max_height;
|
||
}
|
||
/* Report the current size as the base/initial size. */
|
||
if (v->xdg_surface->surface) {
|
||
int w = v->xdg_surface->surface->current.width;
|
||
int h = v->xdg_surface->surface->current.height;
|
||
if (w > 0 && h > 0) {
|
||
out->flags |= WNH_P_SIZE;
|
||
out->width = w;
|
||
out->height = h;
|
||
}
|
||
}
|
||
return (out->flags != 0) ? 1 : 0;
|
||
}
|
||
|
||
static WWMHints *
|
||
wl_prop_get_wm_hints(WNativeWindow win) { (void)win; return NULL; }
|
||
|
||
static int
|
||
wl_prop_get_wm_class(WNativeWindow win,
|
||
char **wm_class, char **wm_instance)
|
||
{
|
||
/* Phase 41: use XDG app_id as both WM_CLASS and WM_INSTANCE. */
|
||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||
const char *app_id = (v && v->xdg_surface && v->xdg_surface->toplevel &&
|
||
v->xdg_surface->toplevel->app_id)
|
||
? v->xdg_surface->toplevel->app_id : "wayland-client";
|
||
*wm_class = strdup(app_id);
|
||
*wm_instance = strdup(app_id);
|
||
return 0;
|
||
}
|
||
|
||
static Bool
|
||
wl_window_fetch_name(WNativeWindow win, char **name_out)
|
||
{
|
||
/* Phase 41: use XDG toplevel title as the window name. */
|
||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||
const char *title = (v && v->xdg_surface && v->xdg_surface->toplevel &&
|
||
v->xdg_surface->toplevel->title)
|
||
? v->xdg_surface->toplevel->title : NULL;
|
||
if (title) {
|
||
*name_out = strdup(title);
|
||
return True;
|
||
}
|
||
*name_out = NULL;
|
||
return False;
|
||
}
|
||
|
||
static Bool
|
||
wl_window_get_icon_name(WNativeWindow win, char **name_out)
|
||
{ (void)win; *name_out = NULL; return False; }
|
||
|
||
static int
|
||
wl_prop_get_wmaker_menu(WNativeWindow win,
|
||
char ***list_out, int *count_out)
|
||
{
|
||
(void)win;
|
||
*list_out = NULL; *count_out = 0;
|
||
return 0;
|
||
}
|
||
|
||
static WNativeWindow
|
||
wl_prop_get_transient_for(WNativeWindow win) { (void)win; return WNATIVE_NONE; }
|
||
|
||
static int wl_prop_has_wmaker_menu(WNativeWindow win) { (void)win; return 0; }
|
||
static int wl_prop_has_gtk_object_path(WNativeWindow win) { (void)win; return 0; }
|
||
|
||
static void
|
||
wl_prop_set_wm_state(WNativeWindow win, const long *data, int n)
|
||
{ (void)win; (void)data; (void)n; }
|
||
|
||
static int
|
||
wl_prop_get_wm_state(WNativeWindow win, long **data_out)
|
||
{ (void)win; *data_out = NULL; return 0; }
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Window map / unmap / geometry */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static void wl_window_map(WNativeWindow win) { (void)win; }
|
||
static void wl_window_map_raised(WNativeWindow win) { (void)win; }
|
||
static void wl_window_map_subwindows(WNativeWindow parent) { (void)parent; }
|
||
static void wl_window_unmap(WNativeWindow win) { (void)win; }
|
||
static void wl_window_move(WNativeWindow win, int x, int y)
|
||
{
|
||
/* Phase 44: position is tracked on the WM side; no server-side move needed. */
|
||
(void)win; (void)x; (void)y;
|
||
}
|
||
|
||
static void wl_window_resize(WNativeWindow win, int w, int h)
|
||
{
|
||
/* Phase 44: send a configure with the new size. */
|
||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||
if (!v || !v->xdg_surface || w <= 0 || h <= 0)
|
||
return;
|
||
wlr_xdg_toplevel_set_size(v->xdg_surface, (uint32_t)w, (uint32_t)h);
|
||
wlr_xdg_surface_schedule_configure(v->xdg_surface);
|
||
}
|
||
static void wl_window_set_border(WNativeWindow win, unsigned long pixel) { (void)win; (void)pixel; }
|
||
static void wl_window_set_border_width(WNativeWindow win, int width) { (void)win; (void)width; }
|
||
static void wl_window_set_save_under(WNativeWindow win, int save_under) { (void)win; (void)save_under; }
|
||
static void wl_window_clear(WNativeWindow win) { (void)win; }
|
||
|
||
static int
|
||
wl_window_get_rect(WNativeWindow win,
|
||
int *win_x, int *win_y, int *win_w, int *win_h)
|
||
{
|
||
(void)win;
|
||
*win_x = *win_y = 0; *win_w = *win_h = 1;
|
||
return 0;
|
||
}
|
||
|
||
static void wl_window_add_events(WNativeWindow win, unsigned long extra_mask) { (void)win; (void)extra_mask; }
|
||
static void wl_window_select_events(WNativeWindow win, unsigned long mask) { (void)win; (void)mask; }
|
||
|
||
static void wl_window_shape_clear(WWindow *wwin) { (void)wwin; }
|
||
static void wl_window_shape_set(WWindow *wwin) { (void)wwin; }
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Overlays */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static void wl_overlay_draw_line(WScreen *scr, int x1, int y1, int x2, int y2) { (void)scr; (void)x1; (void)y1; (void)x2; (void)y2; }
|
||
static void wl_overlay_draw_lines(WScreen *scr, WPoint *pts, int n) { (void)scr; (void)pts; (void)n; }
|
||
static void wl_overlay_draw_rect(WScreen *scr, int x, int y, int w, int h) { (void)scr; (void)x; (void)y; (void)w; (void)h; }
|
||
static void wl_overlay_draw_segments(WScreen *scr, WSegment *segs, int n) { (void)scr; (void)segs; (void)n; }
|
||
static void wl_overlay_draw_string(WScreen *scr, int x, int y, const char *str, int len) { (void)scr; (void)x; (void)y; (void)str; (void)len; }
|
||
static int wl_overlay_text_width(WScreen *scr, const char *str, int n) { (void)scr; (void)str; (void)n; return 0; }
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Client */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static int
|
||
wl_window_get_client_attrs(WNativeWindow win, WClientAttributes *out)
|
||
{
|
||
/* Phase 42: return real map_state and geometry from XDG surface. */
|
||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||
memset(out, 0, sizeof(*out));
|
||
if (v && v->xdg_surface && v->xdg_surface->surface) {
|
||
out->map_state = WCLIENT_VIEWABLE;
|
||
out->width = v->xdg_surface->surface->current.width;
|
||
out->height = v->xdg_surface->surface->current.height;
|
||
} else {
|
||
out->map_state = WCLIENT_UNMAPPED;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static void
|
||
wl_client_get_visual_attrs(WNativeWindow win, WClientVisual *out)
|
||
{
|
||
(void)win;
|
||
out->depth = 24; out->visual = NULL; out->colormap = 0;
|
||
}
|
||
|
||
static void
|
||
wl_client_adopt_attributes(WNativeWindow win, unsigned long event_mask)
|
||
{ (void)win; (void)event_mask; }
|
||
|
||
static void
|
||
wl_client_reparent(WNativeWindow client, WNativeWindow new_parent, int x, int y)
|
||
{ (void)client; (void)new_parent; (void)x; (void)y; }
|
||
|
||
static void
|
||
wl_client_synth_configure(WNativeWindow client_win,
|
||
int x, int y, int width, int height,
|
||
int border_width, WNativeWindow above)
|
||
{
|
||
/* Phase 44: send an XDG configure with the new size to the client. */
|
||
(void)x; (void)y; (void)border_width; (void)above;
|
||
struct wl_toplevel_view *v = wl_find_view_by_id(client_win);
|
||
if (!v || !v->xdg_surface)
|
||
return;
|
||
if (width > 0 && height > 0)
|
||
wlr_xdg_toplevel_set_size(v->xdg_surface, (uint32_t)width, (uint32_t)height);
|
||
wlr_xdg_surface_schedule_configure(v->xdg_surface);
|
||
}
|
||
|
||
static void wl_client_add_to_save_set(WNativeWindow win) { (void)win; }
|
||
static void wl_client_remove_from_save_set(WNativeWindow win) { (void)win; }
|
||
|
||
static void
|
||
wl_client_close(WNativeWindow win)
|
||
{
|
||
/* Phase 46: ask the XDG toplevel to close itself. */
|
||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||
if (v && v->xdg_surface)
|
||
wlr_xdg_toplevel_send_close(v->xdg_surface);
|
||
}
|
||
|
||
static void wl_client_query_shaped(WNativeWindow win, int *shaped_out)
|
||
{ (void)win; *shaped_out = 0; }
|
||
|
||
static int
|
||
wl_client_get_gnustep_attrs(WNativeWindow win, GNUstepWMAttributes **attr_out)
|
||
{ (void)win; *attr_out = NULL; return 0; }
|
||
|
||
static int wl_client_get_pid(WNativeWindow win) { (void)win; return 0; }
|
||
|
||
static RImage *
|
||
wl_client_snapshot(WScreen *scr, WNativeWindow client_win, WNativeWindow frame_win)
|
||
{ (void)scr; (void)client_win; (void)frame_win; return NULL; }
|
||
|
||
static int
|
||
wl_coords_translate(WNativeWindow src, WNativeWindow dst,
|
||
int src_x, int src_y, int *dst_x, int *dst_y)
|
||
{
|
||
(void)src; (void)dst;
|
||
*dst_x = src_x; *dst_y = src_y;
|
||
return 0;
|
||
}
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* ICCCM */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static void wl_icccm_set_wm_hints(WNativeWindow win, const WWMHints *hints) { (void)win; (void)hints; }
|
||
static void wl_icccm_set_class_hint(WNativeWindow win, const char *wm_class, const char *wm_instance) { (void)win; (void)wm_class; (void)wm_instance; }
|
||
static void wl_icccm_set_class_hint_if_absent(WNativeWindow win, const char *wm_class, const char *wm_instance) { (void)win; (void)wm_class; (void)wm_instance; }
|
||
static void wl_icccm_set_command(WNativeWindow win, char **argv, int argc) { (void)win; (void)argv; (void)argc; }
|
||
static int wl_icccm_get_command(WNativeWindow win, char ***argv_out, int *argc_out) { (void)win; *argv_out = NULL; *argc_out = 0; return 0; }
|
||
static void wl_icccm_free_command(char **argv) { (void)argv; }
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Fake leader */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static WNativeWindow wl_fake_leader_create(WScreen *scr) { (void)scr; return WNATIVE_NONE; }
|
||
static void wl_fake_leader_destroy(WNativeWindow win) { (void)win; }
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Screen internals */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
/* Wayland compositor is always the sole WM — succeed unconditionally. */
|
||
static int wl_screen_replace_wm(WScreen *scr) { (void)scr; return 1; }
|
||
/* No X11 event mask selection needed; succeed. */
|
||
static int wl_screen_select_input(WScreen *scr, long event_mask) { (void)scr; (void)event_mask; return 1; }
|
||
static void wl_screen_set_root_cursor(WScreen *scr, int cursor_id) { (void)scr; (void)cursor_id; }
|
||
static void wl_screen_set_icon_sizes(WScreen *scr, int min_size, int max_size) { (void)scr; (void)min_size; (void)max_size; }
|
||
static void wl_screen_setup_noticeboard(WScreen *scr) { (void)scr; }
|
||
static void wl_screen_alloc_gcs(WScreen *scr) { (void)scr; }
|
||
static void wl_screen_create_internals(WScreen *scr) { (void)scr; }
|
||
static void wl_screen_create_pixmaps(WScreen *scr) { (void)scr; }
|
||
static void wl_screen_load_tech_font(WScreen *scr) { (void)scr; }
|
||
static void wl_screen_show_mini_screenshot(WScreen *scr, RImage *img) { (void)scr; (void)img; }
|
||
static RImage *wl_screen_capture_area(WScreen *scr) { (void)scr; return NULL; }
|
||
static RImage *wl_screen_capture_window(WScreen *scr, WWindow *wwin) { (void)scr; (void)wwin; return NULL; }
|
||
static void wl_refresh_desktop(WScreen *scr) { (void)scr; }
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Monitors */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static void
|
||
wl_monitors_query(WScreen *scr, WHeadGeometry **heads_out, int *count_out)
|
||
{
|
||
(void)scr;
|
||
|
||
if (!wl_state.output) {
|
||
*heads_out = NULL;
|
||
*count_out = 0;
|
||
return;
|
||
}
|
||
|
||
/* Phase 34: return the single headless output as head 0. */
|
||
WHeadGeometry *heads = wmalloc(sizeof(WHeadGeometry));
|
||
|
||
if (wl_state.output_layout) {
|
||
const struct wlr_box *box =
|
||
wlr_output_layout_get_box(wl_state.output_layout, wl_state.output);
|
||
if (box) {
|
||
heads[0].x = box->x;
|
||
heads[0].y = box->y;
|
||
heads[0].width = box->width;
|
||
heads[0].height = box->height;
|
||
} else {
|
||
heads[0].x = heads[0].y = 0;
|
||
heads[0].width = (int)wl_state.output->width;
|
||
heads[0].height = (int)wl_state.output->height;
|
||
}
|
||
} else {
|
||
heads[0].x = heads[0].y = 0;
|
||
heads[0].width = (int)wl_state.output->width;
|
||
heads[0].height = (int)wl_state.output->height;
|
||
}
|
||
|
||
*heads_out = heads;
|
||
*count_out = 1;
|
||
}
|
||
|
||
static void wl_monitors_select_events(WScreen *scr) { (void)scr; }
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Workspace badge */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static RImage *
|
||
wl_workspace_badge_render(WScreen *scr, WNativeWindow badge_win,
|
||
const char *name, int name_len,
|
||
int width, int height)
|
||
{ (void)scr; (void)badge_win; (void)name; (void)name_len; (void)width; (void)height; return NULL; }
|
||
|
||
static void
|
||
wl_workspace_badge_update(WScreen *scr, WNativeWindow badge_win, RImage *img)
|
||
{ (void)scr; (void)badge_win; (void)img; }
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* XKB */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static WKeyCode wl_keysym_to_keycode(unsigned long keysym) { (void)keysym; return 0; }
|
||
static unsigned long wl_keysym_from_keycode(unsigned int keycode, unsigned int index) { (void)keycode; (void)index; return 0; }
|
||
static void wl_xkb_lock_group(int group) { (void)group; }
|
||
static int wl_xkb_get_group(void) { return 0; }
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Extensions / cursors / error handlers */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
static void wl_extensions_init(void) {}
|
||
static void wl_cursors_init(void) {}
|
||
static WNativeWindow wl_cursor_create_named(const char *name) { (void)name; return WNATIVE_NONE; }
|
||
static void wl_shape_combine_mask(WNativeWindow win, WNativeWindow mask) { (void)win; (void)mask; }
|
||
static void wl_register_error_handlers(void) {}
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* vtable */
|
||
/* ------------------------------------------------------------------ */
|
||
|
||
const WMBackend wl_backend = {
|
||
.display_open = wl_display_open,
|
||
.display_close = wl_display_close,
|
||
.display_screen_count = wl_display_screen_count,
|
||
.display_default_screen = wl_display_default_screen,
|
||
.atom_intern = wl_atom_intern,
|
||
.event_loop_run = wl_event_loop_run,
|
||
|
||
.screen_open = wl_screen_open,
|
||
.screen_close = wl_screen_close,
|
||
|
||
.frame_create_toplevel = wl_frame_create_toplevel,
|
||
.frame_create_child = wl_frame_create_child,
|
||
.frame_destroy = wl_frame_destroy,
|
||
.frame_configure = wl_frame_configure,
|
||
|
||
.context_save = wl_context_save,
|
||
.context_find = wl_context_find,
|
||
.context_delete = wl_context_delete,
|
||
|
||
.focus_set = wl_focus_set,
|
||
|
||
.key_grab = wl_key_grab,
|
||
.key_ungrab_all = wl_key_ungrab_all,
|
||
.button_grab = wl_button_grab,
|
||
.button_ungrab_all = wl_button_ungrab_all,
|
||
|
||
.pointer_grab = wl_pointer_grab,
|
||
.pointer_ungrab = wl_pointer_ungrab,
|
||
.pointer_warp = wl_pointer_warp,
|
||
.pointer_warp_to = wl_pointer_warp_to,
|
||
.pointer_query = wl_pointer_query,
|
||
.pointer_query_full = wl_pointer_query_full,
|
||
.pointer_change_grab = wl_pointer_change_grab,
|
||
|
||
.keyboard_grab = wl_keyboard_grab,
|
||
.keyboard_ungrab = wl_keyboard_ungrab,
|
||
|
||
.server_grab = wl_server_grab,
|
||
.server_ungrab = wl_server_ungrab,
|
||
|
||
.stacking_raise = wl_stacking_raise,
|
||
.stacking_lower = wl_stacking_lower,
|
||
.stacking_restack = wl_stacking_restack,
|
||
.stacking_query = wl_stacking_query,
|
||
|
||
.event_flush = wl_event_flush,
|
||
.event_flush_expose = wl_event_flush_expose,
|
||
.event_sync = wl_event_sync,
|
||
.event_pending = wl_event_pending,
|
||
.event_drain_motion = wl_event_drain_motion,
|
||
.event_discard_enter_leave_until = wl_event_discard_enter_leave_until,
|
||
.event_handle_extensions = wl_event_handle_extensions,
|
||
.event_randr_update = wl_event_randr_update,
|
||
|
||
.prop_get_normal_hints = wl_prop_get_normal_hints,
|
||
.prop_get_wm_hints = wl_prop_get_wm_hints,
|
||
.prop_get_wm_class = wl_prop_get_wm_class,
|
||
.window_fetch_name = wl_window_fetch_name,
|
||
.window_get_icon_name = wl_window_get_icon_name,
|
||
.prop_get_wmaker_menu = wl_prop_get_wmaker_menu,
|
||
.prop_get_transient_for = wl_prop_get_transient_for,
|
||
.prop_has_wmaker_menu = wl_prop_has_wmaker_menu,
|
||
.prop_has_gtk_object_path = wl_prop_has_gtk_object_path,
|
||
.prop_set_wm_state = wl_prop_set_wm_state,
|
||
.prop_get_wm_state = wl_prop_get_wm_state,
|
||
|
||
.window_map = wl_window_map,
|
||
.window_map_raised = wl_window_map_raised,
|
||
.window_map_subwindows = wl_window_map_subwindows,
|
||
.window_unmap = wl_window_unmap,
|
||
.window_move = wl_window_move,
|
||
.window_resize = wl_window_resize,
|
||
.window_set_border = wl_window_set_border,
|
||
.window_set_border_width = wl_window_set_border_width,
|
||
.window_set_save_under = wl_window_set_save_under,
|
||
.window_clear = wl_window_clear,
|
||
.window_get_rect = wl_window_get_rect,
|
||
.window_add_events = wl_window_add_events,
|
||
.window_select_events = wl_window_select_events,
|
||
.window_shape_clear = wl_window_shape_clear,
|
||
.window_shape_set = wl_window_shape_set,
|
||
|
||
.overlay_draw_line = wl_overlay_draw_line,
|
||
.overlay_draw_lines = wl_overlay_draw_lines,
|
||
.overlay_draw_rect = wl_overlay_draw_rect,
|
||
.overlay_draw_segments = wl_overlay_draw_segments,
|
||
.overlay_draw_string = wl_overlay_draw_string,
|
||
.overlay_text_width = wl_overlay_text_width,
|
||
|
||
.window_get_client_attrs = wl_window_get_client_attrs,
|
||
.client_get_visual_attrs = wl_client_get_visual_attrs,
|
||
.client_adopt_attributes = wl_client_adopt_attributes,
|
||
.client_reparent = wl_client_reparent,
|
||
.client_synth_configure = wl_client_synth_configure,
|
||
.client_add_to_save_set = wl_client_add_to_save_set,
|
||
.client_remove_from_save_set = wl_client_remove_from_save_set,
|
||
.client_close = wl_client_close,
|
||
.client_query_shaped = wl_client_query_shaped,
|
||
.client_get_gnustep_attrs = wl_client_get_gnustep_attrs,
|
||
.client_get_pid = wl_client_get_pid,
|
||
.client_snapshot = wl_client_snapshot,
|
||
.coords_translate = wl_coords_translate,
|
||
|
||
.icccm_set_wm_hints = wl_icccm_set_wm_hints,
|
||
.icccm_set_class_hint = wl_icccm_set_class_hint,
|
||
.icccm_set_class_hint_if_absent = wl_icccm_set_class_hint_if_absent,
|
||
.icccm_set_command = wl_icccm_set_command,
|
||
.icccm_get_command = wl_icccm_get_command,
|
||
.icccm_free_command = wl_icccm_free_command,
|
||
|
||
.fake_leader_create = wl_fake_leader_create,
|
||
.fake_leader_destroy = wl_fake_leader_destroy,
|
||
|
||
.screen_replace_wm = wl_screen_replace_wm,
|
||
.screen_select_input = wl_screen_select_input,
|
||
.screen_set_root_cursor = wl_screen_set_root_cursor,
|
||
.screen_set_icon_sizes = wl_screen_set_icon_sizes,
|
||
.screen_setup_noticeboard = wl_screen_setup_noticeboard,
|
||
.screen_alloc_gcs = wl_screen_alloc_gcs,
|
||
.screen_create_internals = wl_screen_create_internals,
|
||
.screen_create_pixmaps = wl_screen_create_pixmaps,
|
||
.screen_load_tech_font = wl_screen_load_tech_font,
|
||
.screen_show_mini_screenshot = wl_screen_show_mini_screenshot,
|
||
.screen_capture_area = wl_screen_capture_area,
|
||
.screen_capture_window = wl_screen_capture_window,
|
||
.refresh_desktop = wl_refresh_desktop,
|
||
|
||
.monitors_query = wl_monitors_query,
|
||
.monitors_select_events = wl_monitors_select_events,
|
||
|
||
.workspace_badge_render = wl_workspace_badge_render,
|
||
.workspace_badge_update = wl_workspace_badge_update,
|
||
|
||
.keysym_to_keycode = wl_keysym_to_keycode,
|
||
.keysym_from_keycode = wl_keysym_from_keycode,
|
||
.xkb_lock_group = wl_xkb_lock_group,
|
||
.xkb_get_group = wl_xkb_get_group,
|
||
|
||
.extensions_init = wl_extensions_init,
|
||
.cursors_init = wl_cursors_init,
|
||
.cursor_create_named = wl_cursor_create_named,
|
||
.shape_combine_mask = wl_shape_combine_mask,
|
||
.register_error_handlers = wl_register_error_handlers,
|
||
};
|