blob: 0c64e1eff63ad6ed640538aaa36a36e901dba453 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Needed to include both wayland-client.h and wayland-server.h.
#ifndef WL_HIDE_DEPRECATED
#define WL_HIDE_DEPRECATED
#endif
// Needed for pipe2.
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <systemd/sd-daemon.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wayland-server.h>
#include <wayland-util.h>
#include <xcb/composite.h>
#include <xcb/xcb.h>
#include <xcb/xfixes.h>
#include "aura-shell-client-protocol.h"
#include "drm-server-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "version.h"
#include "viewporter-client-protocol.h"
#include "xdg-shell-unstable-v6-client-protocol.h"
#include "xdg-shell-unstable-v6-server-protocol.h"
#ifndef XWAYLAND_PATH
#define XWAYLAND_PATH "/usr/bin"
#endif
struct xwl;
struct xwl_global {
struct xwl *xwl;
const struct wl_interface *interface;
uint32_t name;
uint32_t version;
void *data;
wl_global_bind_func_t bind;
struct wl_list link;
};
struct xwl_host_registry {
struct xwl *xwl;
struct wl_resource *resource;
struct wl_list link;
};
struct xwl_host_callback {
struct wl_resource *resource;
struct wl_callback *proxy;
};
struct xwl_compositor {
struct xwl *xwl;
uint32_t id;
uint32_t version;
struct xwl_global *host_global;
struct wl_compositor *internal;
};
struct xwl_host_surface {
struct xwl *xwl;
struct wl_resource *resource;
struct wl_surface *proxy;
struct wp_viewport *viewport;
uint32_t contents_width;
uint32_t contents_height;
int32_t contents_scale;
int is_cursor;
uint32_t last_event_serial;
};
struct xwl_host_region {
struct xwl *xwl;
struct wl_resource *resource;
struct wl_region *proxy;
};
struct xwl_host_compositor {
struct xwl_compositor *compositor;
struct wl_resource *resource;
struct wl_compositor *proxy;
};
struct xwl_host_buffer {
struct wl_resource *resource;
struct wl_buffer *proxy;
uint32_t width;
uint32_t height;
};
struct xwl_host_shm_pool {
struct wl_resource *resource;
struct wl_shm_pool *proxy;
};
struct xwl_host_shm {
struct xwl_shm *shm;
struct wl_resource *resource;
struct wl_shm *proxy;
};
struct xwl_shm {
struct xwl *xwl;
uint32_t id;
struct xwl_global *host_global;
};
struct xwl_host_shell {
struct xwl_shell *shell;
struct wl_resource *resource;
struct wl_shell *proxy;
};
struct xwl_shell {
struct xwl *xwl;
uint32_t id;
struct xwl_global *host_global;
};
struct xwl_host_output {
struct xwl_output *output;
struct wl_resource *resource;
struct wl_output *proxy;
struct zaura_output *aura_output;
uint32_t flags;
int width;
int height;
int refresh;
int factor;
double scale;
double max_scale;
};
struct xwl_output {
struct xwl *xwl;
uint32_t id;
uint32_t version;
struct xwl_global *host_global;
struct wl_list link;
};
struct xwl_seat {
struct xwl *xwl;
uint32_t id;
uint32_t version;
struct xwl_global *host_global;
uint32_t last_serial;
struct wl_list link;
};
struct xwl_host_pointer {
struct xwl_seat *seat;
struct wl_resource *resource;
struct wl_pointer *proxy;
struct wl_resource *focus_resource;
struct wl_listener focus_resource_listener;
uint32_t focus_serial;
};
struct xwl_host_keyboard {
struct xwl_seat *seat;
struct wl_resource *resource;
struct wl_keyboard *proxy;
struct wl_resource *focus_resource;
struct wl_listener focus_resource_listener;
uint32_t focus_serial;
};
struct xwl_host_touch {
struct xwl_seat *seat;
struct wl_resource *resource;
struct wl_touch *proxy;
struct wl_resource *focus_resource;
struct wl_listener focus_resource_listener;
};
struct xwl_host_seat {
struct xwl_seat *seat;
struct wl_resource *resource;
struct wl_seat *proxy;
};
struct xwl_data_device_manager {
struct xwl *xwl;
uint32_t id;
uint32_t version;
struct xwl_global *host_global;
struct wl_data_device_manager *internal;
};
struct xwl_host_data_device_manager {
struct xwl *xwl;
struct wl_resource *resource;
struct wl_data_device_manager *proxy;
};
struct xwl_host_data_device {
struct xwl *xwl;
struct wl_resource *resource;
struct wl_data_device *proxy;
};
struct xwl_host_data_source {
struct wl_resource *resource;
struct wl_data_source *proxy;
};
struct xwl_host_data_offer {
struct wl_resource *resource;
struct wl_data_offer *proxy;
};
struct xwl_data_offer {
struct xwl *xwl;
struct wl_data_offer *internal;
int utf8_text;
};
struct xwl_data_source {
struct xwl *xwl;
struct wl_data_source *internal;
};
struct xwl_xdg_shell {
struct xwl *xwl;
uint32_t id;
struct xwl_global *host_global;
struct zxdg_shell_v6 *internal;
};
struct xwl_host_xdg_shell {
struct xwl_xdg_shell *xdg_shell;
struct wl_resource *resource;
struct zxdg_shell_v6 *proxy;
};
struct xwl_host_xdg_surface {
struct xwl *xwl;
struct wl_resource *resource;
struct zxdg_surface_v6 *proxy;
};
struct xwl_host_xdg_toplevel {
struct xwl *xwl;
struct wl_resource *resource;
struct zxdg_toplevel_v6 *proxy;
};
struct xwl_host_xdg_popup {
struct xwl *xwl;
struct wl_resource *resource;
struct zxdg_popup_v6 *proxy;
};
struct xwl_host_xdg_positioner {
struct xwl *xwl;
struct wl_resource *resource;
struct zxdg_positioner_v6 *proxy;
};
struct xwl_subcompositor {
struct xwl *xwl;
uint32_t id;
struct xwl_global *host_global;
};
struct xwl_host_subcompositor {
struct xwl *xwl;
struct wl_resource *resource;
struct wl_subcompositor *proxy;
};
struct xwl_host_subsurface {
struct xwl *xwl;
struct wl_resource *resource;
struct wl_subsurface *proxy;
};
struct xwl_aura_shell {
struct xwl *xwl;
uint32_t id;
uint32_t version;
struct zaura_shell *internal;
};
struct xwl_viewporter {
struct xwl *xwl;
uint32_t id;
struct wp_viewporter *internal;
};
struct xwl_linux_dmabuf {
struct xwl *xwl;
uint32_t id;
uint32_t version;
struct xwl_global *host_drm_global;
struct zwp_linux_dmabuf_v1 *internal;
};
struct xwl_host_drm {
struct xwl_linux_dmabuf *linux_dmabuf;
uint32_t version;
struct wl_resource *resource;
};
struct xwl_config {
uint32_t serial;
uint32_t mask;
uint32_t values[5];
uint32_t states_length;
uint32_t states[3];
};
struct xwl_window {
struct xwl *xwl;
xcb_window_t id;
xcb_window_t frame_id;
uint32_t host_surface_id;
int unpaired;
int x;
int y;
int width;
int height;
int border_width;
int depth;
int managed;
int realized;
int activated;
xcb_window_t transient_for;
int decorated;
char *name;
char *clazz;
uint32_t size_flags;
struct xwl_config next_config;
struct xwl_config pending_config;
struct zxdg_surface_v6 *xdg_surface;
struct zxdg_toplevel_v6 *xdg_toplevel;
struct zxdg_popup_v6 *xdg_popup;
struct zaura_surface *aura_surface;
struct wl_list link;
};
enum {
ATOM_WM_S0,
ATOM_WM_PROTOCOLS,
ATOM_WM_STATE,
ATOM_WM_DELETE_WINDOW,
ATOM_WM_TAKE_FOCUS,
ATOM_WL_SURFACE_ID,
ATOM_UTF8_STRING,
ATOM_MOTIF_WM_HINTS,
ATOM_NET_FRAME_EXTENTS,
ATOM_NET_SUPPORTING_WM_CHECK,
ATOM_NET_WM_NAME,
ATOM_NET_WM_MOVERESIZE,
ATOM_NET_WM_STATE,
ATOM_NET_WM_STATE_FULLSCREEN,
ATOM_NET_WM_STATE_MAXIMIZED_VERT,
ATOM_NET_WM_STATE_MAXIMIZED_HORZ,
ATOM_CLIPBOARD,
ATOM_CLIPBOARD_MANAGER,
ATOM_TARGETS,
ATOM_TIMESTAMP,
ATOM_TEXT,
ATOM_INCR,
ATOM_WL_SELECTION,
ATOM_LAST = ATOM_WL_SELECTION,
};
struct xwl {
char **runprog;
struct wl_display *display;
struct wl_display *host_display;
struct wl_client *client;
struct xwl_compositor *compositor;
struct xwl_subcompositor *subcompositor;
struct xwl_shm *shm;
struct xwl_shell *shell;
struct xwl_data_device_manager *data_device_manager;
struct xwl_xdg_shell *xdg_shell;
struct xwl_aura_shell *aura_shell;
struct xwl_viewporter *viewporter;
struct xwl_linux_dmabuf *linux_dmabuf;
struct wl_list outputs;
struct wl_list seats;
struct wl_event_source *display_event_source;
struct wl_event_source *display_ready_event_source;
struct wl_event_source *sigchld_event_source;
int wm_fd;
const char *drm_device;
int xwayland;
pid_t xwayland_pid;
pid_t child_pid;
pid_t peer_pid;
struct wl_list registries;
struct wl_list globals;
int next_global_id;
xcb_connection_t *connection;
struct wl_event_source *connection_event_source;
const xcb_query_extension_reply_t *xfixes_extension;
xcb_screen_t *screen;
xcb_window_t window;
struct wl_list windows, unpaired_windows;
struct xwl_window *host_focus_window;
int needs_set_input_focus;
double scale;
const char *app_id;
int exit_with_child;
int clipboard_manager;
uint32_t frame_color;
int has_frame_color;
int show_window_title;
struct xwl_host_seat *default_seat;
xcb_window_t selection_window;
xcb_window_t selection_owner;
int selection_incremental_transfer;
xcb_selection_request_event_t selection_request;
xcb_timestamp_t selection_timestamp;
struct wl_data_device *selection_data_device;
struct xwl_data_offer *selection_data_offer;
struct xwl_data_source *selection_data_source;
int selection_data_source_send_fd;
struct wl_event_source *selection_send_event_source;
xcb_get_property_reply_t *selection_property_reply;
int selection_property_offset;
struct wl_event_source *selection_event_source;
struct wl_array selection_data;
int selection_data_offer_receive_fd;
int selection_data_ack_pending;
union {
const char *name;
xcb_intern_atom_cookie_t cookie;
xcb_atom_t value;
} atoms[ATOM_LAST + 1];
xcb_visualid_t visual_ids[256];
xcb_colormap_t colormaps[256];
};
enum {
PROPERTY_WM_NAME,
PROPERTY_WM_CLASS,
PROPERTY_WM_TRANSIENT_FOR,
PROPERTY_WM_NORMAL_HINTS,
PROPERTY_MOTIF_WM_HINTS,
};
#define US_POSITION (1L << 0)
#define US_SIZE (1L << 1)
#define P_POSITION (1L << 2)
#define P_SIZE (1L << 3)
#define P_MIN_SIZE (1L << 4)
#define P_MAX_SIZE (1L << 5)
#define P_RESIZE_INC (1L << 6)
#define P_ASPECT (1L << 7)
#define P_BASE_SIZE (1L << 8)
#define P_WIN_GRAVITY (1L << 9)
#define MWM_HINTS_FUNCTIONS (1L << 0)
#define MWM_HINTS_DECORATIONS (1L << 1)
#define MWM_HINTS_INPUT_MODE (1L << 2)
#define MWM_HINTS_STATUS (1L << 3)
#define MWM_DECOR_ALL (1L << 0)
#define MWM_DECOR_BORDER (1L << 1)
#define MWM_DECOR_RESIZEH (1L << 2)
#define MWM_DECOR_TITLE (1L << 3)
#define MWM_DECOR_MENU (1L << 4)
#define MWM_DECOR_MINIMIZE (1L << 5)
#define MWM_DECOR_MAXIMIZE (1L << 6)
#define NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
#define NET_WM_MOVERESIZE_SIZE_TOP 1
#define NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
#define NET_WM_MOVERESIZE_SIZE_RIGHT 3
#define NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
#define NET_WM_MOVERESIZE_SIZE_BOTTOM 5
#define NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
#define NET_WM_MOVERESIZE_SIZE_LEFT 7
#define NET_WM_MOVERESIZE_MOVE 8
#define NET_WM_STATE_REMOVE 0
#define NET_WM_STATE_ADD 1
#define NET_WM_STATE_TOGGLE 2
#define WM_STATE_WITHDRAWN 0
#define WM_STATE_NORMAL 1
#define WM_STATE_ICONIC 3
#define SEND_EVENT_MASK 0x80
#define CAPTION_HEIGHT 32
#define MIN_SCALE 0.1
#define MAX_SCALE 10.0
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX 108
#endif
#define LOCK_SUFFIX ".lock"
#define LOCK_SUFFIXLEN 5
static void xwl_internal_xdg_shell_ping(void *data,
struct zxdg_shell_v6 *xdg_shell,
uint32_t serial) {
zxdg_shell_v6_pong(xdg_shell, serial);
}
static const struct zxdg_shell_v6_listener xwl_internal_xdg_shell_listener = {
xwl_internal_xdg_shell_ping};
static void xwl_send_configure_notify(struct xwl_window *window) {
xcb_configure_notify_event_t event = {
.response_type = XCB_CONFIGURE_NOTIFY,
.event = window->id,
.window = window->id,
.above_sibling = XCB_WINDOW_NONE,
.x = window->x,
.y = window->y,
.width = window->width,
.height = window->height,
.border_width = window->border_width,
.override_redirect = 0,
.pad0 = 0,
.pad1 = 0,
};
xcb_send_event(window->xwl->connection, 0, window->id,
XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char *)&event);
}
static void xwl_adjust_window_size_for_screen_size(struct xwl_window *window) {
struct xwl *xwl = window->xwl;
// Clamp size to screen.
window->width = MIN(window->width, xwl->screen->width_in_pixels);
window->height = MIN(window->height, xwl->screen->height_in_pixels);
}
static void
xwl_adjust_window_position_for_screen_size(struct xwl_window *window) {
struct xwl *xwl = window->xwl;
// Center horizontally/vertically.
window->x = xwl->screen->width_in_pixels / 2 - window->width / 2;
window->y = xwl->screen->height_in_pixels / 2 - window->height / 2;
}
static void xwl_configure_window(struct xwl_window *window) {
assert(!window->pending_config.serial);
if (window->next_config.mask) {
int x = window->x;
int y = window->y;
int i = 0;
xcb_configure_window(window->xwl->connection, window->frame_id,
window->next_config.mask, window->next_config.values);
if (window->next_config.mask & XCB_CONFIG_WINDOW_X)
x = window->next_config.values[i++];
if (window->next_config.mask & XCB_CONFIG_WINDOW_Y)
y = window->next_config.values[i++];
assert(window->managed);
xcb_configure_window(window->xwl->connection, window->id,
window->next_config.mask &
~(XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y),
&window->next_config.values[i]);
if (window->next_config.mask & XCB_CONFIG_WINDOW_WIDTH)
window->width = window->next_config.values[i++];
if (window->next_config.mask & XCB_CONFIG_WINDOW_HEIGHT)
window->height = window->next_config.values[i++];
if (window->next_config.mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
window->border_width = window->next_config.values[i++];
if (x != window->x || y != window->y) {
window->x = x;
window->y = y;
xwl_send_configure_notify(window);
}
}
if (window->managed) {
xcb_change_property(window->xwl->connection, XCB_PROP_MODE_REPLACE,
window->id, window->xwl->atoms[ATOM_NET_WM_STATE].value,
XCB_ATOM_ATOM, 32, window->next_config.states_length,
window->next_config.states);
}
window->pending_config = window->next_config;
window->next_config.serial = 0;
window->next_config.mask = 0;
window->next_config.states_length = 0;
}
static void xwl_set_input_focus(struct xwl *xwl, struct xwl_window *window) {
if (window) {
xcb_client_message_event_t event = {
.response_type = XCB_CLIENT_MESSAGE,
.format = 32,
.window = window->id,
.type = xwl->atoms[ATOM_WM_PROTOCOLS].value,
.data.data32 =
{
xwl->atoms[ATOM_WM_TAKE_FOCUS].value, XCB_CURRENT_TIME,
},
};
if (!window->managed)
return;
xcb_send_event(xwl->connection, 0, window->id,
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (char *)&event);
xcb_set_input_focus(xwl->connection, XCB_INPUT_FOCUS_NONE, window->id,
XCB_CURRENT_TIME);
} else {
xcb_set_input_focus(xwl->connection, XCB_INPUT_FOCUS_NONE, XCB_NONE,
XCB_CURRENT_TIME);
}
}
static void xwl_restack_windows(struct xwl *xwl, uint32_t focus_resource_id) {
struct xwl_window *sibling;
uint32_t values[1];
wl_list_for_each(sibling, &xwl->windows, link) {
if (!sibling->managed)
continue;
// Move focus window to the top and all other windows to the bottom.
values[0] = sibling->host_surface_id == focus_resource_id
? XCB_STACK_MODE_ABOVE
: XCB_STACK_MODE_BELOW;
xcb_configure_window(xwl->connection, sibling->frame_id,
XCB_CONFIG_WINDOW_STACK_MODE, values);
}
}
static void xwl_roundtrip(struct xwl *xwl) {
free(xcb_get_input_focus_reply(xwl->connection,
xcb_get_input_focus(xwl->connection), NULL));
}
static int
xwl_process_pending_configure_acks(struct xwl_window *window,
struct xwl_host_surface *host_surface) {
if (!window->pending_config.serial)
return 0;
if (window->managed && host_surface) {
int width = window->width + window->border_width * 2;
int height = window->height + window->border_width * 2;
// Early out if we expect contents to match window size at some point in
// the future.
if (width != host_surface->contents_width ||
height != host_surface->contents_height) {
return 0;
}
}
if (window->xdg_surface) {
zxdg_surface_v6_ack_configure(window->xdg_surface,
window->pending_config.serial);
}
window->pending_config.serial = 0;
if (window->next_config.serial)
xwl_configure_window(window);
return 1;
}
static void xwl_internal_xdg_surface_configure(
void *data, struct zxdg_surface_v6 *xdg_surface, uint32_t serial) {
struct xwl_window *window = zxdg_surface_v6_get_user_data(xdg_surface);
window->next_config.serial = serial;
if (!window->pending_config.serial) {
struct wl_resource *host_resource;
struct xwl_host_surface *host_surface = NULL;
host_resource =
wl_client_get_object(window->xwl->client, window->host_surface_id);
if (host_resource)
host_surface = wl_resource_get_user_data(host_resource);
xwl_configure_window(window);
if (xwl_process_pending_configure_acks(window, host_surface)) {
if (host_surface)
wl_surface_commit(host_surface->proxy);
}
}
}
static const struct zxdg_surface_v6_listener xwl_internal_xdg_surface_listener =
{xwl_internal_xdg_surface_configure};
static void xwl_internal_xdg_toplevel_configure(
void *data, struct zxdg_toplevel_v6 *xdg_toplevel, int32_t width,
int32_t height, struct wl_array *states) {
struct xwl_window *window = zxdg_toplevel_v6_get_user_data(xdg_toplevel);
int activated = 0;
uint32_t *state;
int i = 0;
if (!window->managed)
return;
if (width && height) {
int32_t width_in_pixels = width * window->xwl->scale;
int32_t height_in_pixels = height * window->xwl->scale;
int i = 0;
window->next_config.mask = XCB_CONFIG_WINDOW_WIDTH |
XCB_CONFIG_WINDOW_HEIGHT |
XCB_CONFIG_WINDOW_BORDER_WIDTH;
if (!(window->size_flags & (US_POSITION | P_POSITION))) {
window->next_config.mask |= XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
window->next_config.values[i++] =
window->xwl->screen->width_in_pixels / 2 - width_in_pixels / 2;
window->next_config.values[i++] =
window->xwl->screen->height_in_pixels / 2 - height_in_pixels / 2;
}
window->next_config.values[i++] = width_in_pixels;
window->next_config.values[i++] = height_in_pixels;
window->next_config.values[i++] = 0;
}
wl_array_for_each(state, states) {
if (*state == ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN) {
window->next_config.states[i++] =
window->xwl->atoms[ATOM_NET_WM_STATE_FULLSCREEN].value;
}
if (*state == ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED) {
window->next_config.states[i++] =
window->xwl->atoms[ATOM_NET_WM_STATE_MAXIMIZED_VERT].value;
window->next_config.states[i++] =
window->xwl->atoms[ATOM_NET_WM_STATE_MAXIMIZED_HORZ].value;
}
if (*state == ZXDG_TOPLEVEL_V6_STATE_ACTIVATED)
activated = 1;
}
if (activated != window->activated) {
if (activated != (window->xwl->host_focus_window == window)) {
window->xwl->host_focus_window = activated ? window : NULL;
window->xwl->needs_set_input_focus = 1;
}
window->activated = activated;
}
window->next_config.states_length = i;
}
static void
xwl_internal_xdg_toplevel_close(void *data,
struct zxdg_toplevel_v6 *xdg_toplevel) {
struct xwl_window *window = zxdg_toplevel_v6_get_user_data(xdg_toplevel);
xcb_client_message_event_t event = {
.response_type = XCB_CLIENT_MESSAGE,
.format = 32,
.window = window->id,
.type = window->xwl->atoms[ATOM_WM_PROTOCOLS].value,
.data.data32 =
{
window->xwl->atoms[ATOM_WM_DELETE_WINDOW].value, XCB_CURRENT_TIME,
},
};
xcb_send_event(window->xwl->connection, 0, window->id,
XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
}
static const struct zxdg_toplevel_v6_listener
xwl_internal_xdg_toplevel_listener = {xwl_internal_xdg_toplevel_configure,
xwl_internal_xdg_toplevel_close};
static void xwl_internal_xdg_popup_configure(void *data,
struct zxdg_popup_v6 *xdg_popup,
int32_t x, int32_t y,
int32_t width, int32_t height) {}
static void xwl_internal_xdg_popup_done(void *data,
struct zxdg_popup_v6 *zxdg_popup_v6) {}
static const struct zxdg_popup_v6_listener xwl_internal_xdg_popup_listener = {
xwl_internal_xdg_popup_configure, xwl_internal_xdg_popup_done};
static void xwl_window_set_wm_state(struct xwl_window *window, int state) {
struct xwl *xwl = window->xwl;
uint32_t values[2];
values[0] = state;
values[1] = XCB_WINDOW_NONE;
xcb_change_property(xwl->connection, XCB_PROP_MODE_REPLACE, window->id,
xwl->atoms[ATOM_WM_STATE].value,
xwl->atoms[ATOM_WM_STATE].value, 32, 2, values);
}
static void xwl_window_update(struct xwl_window *window) {
struct wl_resource *host_resource = NULL;
struct xwl_host_surface *host_surface;
struct xwl *xwl = window->xwl;
struct xwl_window *parent = NULL;
const char *app_id = NULL;
if (window->host_surface_id) {
host_resource = wl_client_get_object(xwl->client, window->host_surface_id);
if (host_resource && window->unpaired) {
wl_list_remove(&window->link);
wl_list_insert(&xwl->windows, &window->link);
window->unpaired = 0;
}
} else if (!window->unpaired) {
wl_list_remove(&window->link);
wl_list_insert(&xwl->unpaired_windows, &window->link);
window->unpaired = 1;
}
if (!host_resource) {
if (window->aura_surface) {
zaura_surface_destroy(window->aura_surface);
window->aura_surface = NULL;
}
if (window->xdg_toplevel) {
zxdg_toplevel_v6_destroy(window->xdg_toplevel);
window->xdg_toplevel = NULL;
}
if (window->xdg_popup) {
zxdg_popup_v6_destroy(window->xdg_popup);
window->xdg_popup = NULL;
}
if (window->xdg_surface) {
zxdg_surface_v6_destroy(window->xdg_surface);
window->xdg_surface = NULL;
}
return;
}
host_surface = wl_resource_get_user_data(host_resource);
assert(host_surface);
assert(!host_surface->is_cursor);
assert(xwl->xdg_shell);
assert(xwl->xdg_shell->internal);
if (window->managed) {
app_id = xwl->app_id ? xwl->app_id : window->clazz;
if (window->transient_for != XCB_WINDOW_NONE) {
struct xwl_window *sibling;
wl_list_for_each(sibling, &xwl->windows, link) {
if (sibling->id == window->transient_for) {
if (sibling->xdg_toplevel)
parent = sibling;
break;
}
}
}
} else {
struct xwl_window *sibling;
uint32_t parent_last_event_serial = 0;
wl_list_for_each(sibling, &xwl->windows, link) {
struct wl_resource *sibling_host_resource;
struct xwl_host_surface *sibling_host_surface;
if (!sibling->realized)
continue;
sibling_host_resource =
wl_client_get_object(xwl->client, sibling->host_surface_id);
if (!sibling_host_resource)
continue;
// Any parent will do but prefer last event window.
sibling_host_surface = wl_resource_get_user_data(sibling_host_resource);
if (parent_last_event_serial > sibling_host_surface->last_event_serial)
continue;
parent = sibling;
parent_last_event_serial = sibling_host_surface->last_event_serial;
}
}
if (!window->depth) {
xcb_get_geometry_reply_t *geometry_reply = xcb_get_geometry_reply(
xwl->connection, xcb_get_geometry(xwl->connection, window->id), NULL);
if (geometry_reply) {
window->depth = geometry_reply->depth;
free(geometry_reply);
}
}
if (!window->xdg_surface) {
window->xdg_surface = zxdg_shell_v6_get_xdg_surface(
xwl->xdg_shell->internal, host_surface->proxy);
zxdg_surface_v6_set_user_data(window->xdg_surface, window);
zxdg_surface_v6_add_listener(window->xdg_surface,
&xwl_internal_xdg_surface_listener, window);
}
if (xwl->aura_shell) {
if (!window->aura_surface) {
window->aura_surface = zaura_shell_get_aura_surface(
xwl->aura_shell->internal, host_surface->proxy);
}
zaura_surface_set_frame(window->aura_surface,
window->decorated
? ZAURA_SURFACE_FRAME_TYPE_NORMAL
: window->depth == 32
? ZAURA_SURFACE_FRAME_TYPE_NONE
: ZAURA_SURFACE_FRAME_TYPE_SHADOW);
if (xwl->has_frame_color &&
xwl->aura_shell->version >=
ZAURA_SURFACE_SET_FRAME_COLORS_SINCE_VERSION) {
zaura_surface_set_frame_colors(window->aura_surface, xwl->frame_color,
xwl->frame_color);
}
}
if (window->managed || !parent) {
if (!window->xdg_toplevel) {
window->xdg_toplevel = zxdg_surface_v6_get_toplevel(window->xdg_surface);
zxdg_toplevel_v6_set_user_data(window->xdg_toplevel, window);
zxdg_toplevel_v6_add_listener(
window->xdg_toplevel, &xwl_internal_xdg_toplevel_listener, window);
}
if (parent)
zxdg_toplevel_v6_set_parent(window->xdg_toplevel, parent->xdg_toplevel);
if (window->name && xwl->show_window_title)
zxdg_toplevel_v6_set_title(window->xdg_toplevel, window->name);
if (app_id)
zxdg_toplevel_v6_set_app_id(window->xdg_toplevel, app_id);
} else if (!window->xdg_popup) {
struct zxdg_positioner_v6 *positioner;
positioner = zxdg_shell_v6_create_positioner(xwl->xdg_shell->internal);
assert(positioner);
zxdg_positioner_v6_set_anchor(positioner,
ZXDG_POSITIONER_V6_ANCHOR_TOP |
ZXDG_POSITIONER_V6_ANCHOR_LEFT);
zxdg_positioner_v6_set_gravity(positioner,
ZXDG_POSITIONER_V6_GRAVITY_BOTTOM |
ZXDG_POSITIONER_V6_GRAVITY_RIGHT);
zxdg_positioner_v6_set_anchor_rect(
positioner, (window->x - parent->x) / xwl->scale,
(window->y - parent->y) / xwl->scale, 1, 1);
window->xdg_popup = zxdg_surface_v6_get_popup(
window->xdg_surface, parent->xdg_surface, positioner);
zxdg_popup_v6_set_user_data(window->xdg_popup, window);
zxdg_popup_v6_add_listener(window->xdg_popup,
&xwl_internal_xdg_popup_listener, window);
zxdg_positioner_v6_destroy(positioner);
}
if ((window->size_flags & (US_POSITION | P_POSITION)) && parent &&
xwl->aura_shell &&
xwl->aura_shell->version >= ZAURA_SURFACE_SET_PARENT_SINCE_VERSION) {
zaura_surface_set_parent(window->aura_surface, parent->aura_surface,
(window->x - parent->x) / xwl->scale,
(window->y - parent->y) / xwl->scale);
}
wl_surface_commit(host_surface->proxy);
if (host_surface->contents_width && host_surface->contents_height)
window->realized = 1;
}
static void xwl_host_surface_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void xwl_set_host_surface_scale(struct xwl_host_surface *host) {
double scale = host->xwl->scale * host->contents_scale;
if (!host->contents_width || !host->contents_height)
return;
if (host->viewport) {
wp_viewport_set_destination(host->viewport,
ceil(host->contents_width / scale),
ceil(host->contents_height / scale));
} else {
wl_surface_set_buffer_scale(host->proxy, scale);
}
}
static void xwl_host_surface_attach(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *buffer_resource,
int32_t x, int32_t y) {
struct xwl_host_surface *host = wl_resource_get_user_data(resource);
struct xwl_host_buffer *host_buffer =
buffer_resource ? wl_resource_get_user_data(buffer_resource) : NULL;
struct wl_buffer *buffer_proxy = NULL;
struct xwl_window *window;
double scale = host->xwl->scale;
if (host_buffer) {
host->contents_width = host_buffer->width;
host->contents_height = host_buffer->height;
buffer_proxy = host_buffer->proxy;
}
wl_surface_attach(host->proxy, buffer_proxy, x / scale, y / scale);
xwl_set_host_surface_scale(host);
wl_list_for_each(window, &host->xwl->windows, link) {
if (window->host_surface_id == wl_resource_get_id(resource)) {
while (xwl_process_pending_configure_acks(window, host))
continue;
break;
}
}
}
static void xwl_host_surface_damage(struct wl_client *client,
struct wl_resource *resource, int32_t x,
int32_t y, int32_t width, int32_t height) {
struct xwl_host_surface *host = wl_resource_get_user_data(resource);
double scale = host->xwl->scale;
int32_t x1, y1, x2, y2;
// Enclosing rect after scaling and outset by one pixel to account for
// potential filtering.
x1 = (x - 1) / scale;
y1 = (y - 1) / scale;
x2 = ceil((x + width + 1) / scale);
y2 = ceil((y + height + 1) / scale);
wl_surface_damage(host->proxy, x1, y1, x2 - x1, y2 - y1);
}
static void xwl_frame_callback_done(void *data, struct wl_callback *callback,
uint32_t time) {
struct xwl_host_callback *host = wl_callback_get_user_data(callback);
wl_callback_send_done(host->resource, time);
wl_resource_destroy(host->resource);
}
static const struct wl_callback_listener xwl_frame_callback_listener = {
xwl_frame_callback_done};
static void xwl_host_callback_destroy(struct wl_resource *resource) {
struct xwl_host_callback *host = wl_resource_get_user_data(resource);
wl_callback_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_host_surface_frame(struct wl_client *client,
struct wl_resource *resource,
uint32_t callback) {
struct xwl_host_surface *host = wl_resource_get_user_data(resource);
struct xwl_host_callback *host_callback;
host_callback = malloc(sizeof(*host_callback));
assert(host_callback);
host_callback->resource =
wl_resource_create(client, &wl_callback_interface, 1, callback);
wl_resource_set_implementation(host_callback->resource, NULL, host_callback,
xwl_host_callback_destroy);
host_callback->proxy = wl_surface_frame(host->proxy);
wl_callback_set_user_data(host_callback->proxy, host_callback);
wl_callback_add_listener(host_callback->proxy, &xwl_frame_callback_listener,
host_callback);
}
static void
xwl_host_surface_set_opaque_region(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region_resource) {
struct xwl_host_surface *host = wl_resource_get_user_data(resource);
struct xwl_host_region *host_region =
region_resource ? wl_resource_get_user_data(region_resource) : NULL;
wl_surface_set_opaque_region(host->proxy,
host_region ? host_region->proxy : NULL);
}
static void
xwl_host_surface_set_input_region(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region_resource) {
struct xwl_host_surface *host = wl_resource_get_user_data(resource);
struct xwl_host_region *host_region =
region_resource ? wl_resource_get_user_data(region_resource) : NULL;
wl_surface_set_input_region(host->proxy,
host_region ? host_region->proxy : NULL);
}
static void xwl_host_surface_commit(struct wl_client *client,
struct wl_resource *resource) {
struct xwl_host_surface *host = wl_resource_get_user_data(resource);
struct xwl_window *window;
// No need to defer cursor or non-xwayland client commits.
if (host->is_cursor || !host->xwl->xwayland) {
wl_surface_commit(host->proxy);
return;
}
// Commit if surface is associated with a window. Otherwise, defer
// commit until window is created.
wl_list_for_each(window, &host->xwl->windows, link) {
if (window->host_surface_id == wl_resource_get_id(resource)) {
if (window->xdg_surface) {
wl_surface_commit(host->proxy);
if (host->contents_width && host->contents_height)
window->realized = 1;
}
break;
}
}
}
static void xwl_host_surface_set_buffer_transform(struct wl_client *client,
struct wl_resource *resource,
int32_t transform) {
struct xwl_host_surface *host = wl_resource_get_user_data(resource);
wl_surface_set_buffer_transform(host->proxy, transform);
}
static void xwl_host_surface_set_buffer_scale(struct wl_client *client,
struct wl_resource *resource,
int32_t scale) {
struct xwl_host_surface *host = wl_resource_get_user_data(resource);
host->contents_scale = scale;
xwl_set_host_surface_scale(host);
}
static void xwl_host_surface_damage_buffer(struct wl_client *client,
struct wl_resource *resource,
int32_t x, int32_t y, int32_t width,
int32_t height) {
assert(0);
}
static const struct wl_surface_interface xwl_surface_implementation = {
xwl_host_surface_destroy,
xwl_host_surface_attach,
xwl_host_surface_damage,
xwl_host_surface_frame,
xwl_host_surface_set_opaque_region,
xwl_host_surface_set_input_region,
xwl_host_surface_commit,
xwl_host_surface_set_buffer_transform,
xwl_host_surface_set_buffer_scale,
xwl_host_surface_damage_buffer};
static void xwl_destroy_host_surface(struct wl_resource *resource) {
struct xwl_host_surface *host = wl_resource_get_user_data(resource);
struct xwl_window *window, *surface_window = NULL;
wl_list_for_each(window, &host->xwl->windows, link) {
if (window->host_surface_id == wl_resource_get_id(resource)) {
surface_window = window;
break;
}
}
if (surface_window) {
surface_window->host_surface_id = 0;
xwl_window_update(surface_window);
}
if (host->viewport)
wp_viewport_destroy(host->viewport);
wl_surface_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_region_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void xwl_region_add(struct wl_client *client,
struct wl_resource *resource, int32_t x, int32_t y,
int32_t width, int32_t height) {
struct xwl_host_region *host = wl_resource_get_user_data(resource);
double scale = host->xwl->scale;
int32_t x1, y1, x2, y2;
x1 = x / scale;
y1 = y / scale;
x2 = (x + width) / scale;
y2 = (y + height) / scale;
wl_region_add(host->proxy, x1, y1, x2 - x1, y2 - y1);
}
static void xwl_region_subtract(struct wl_client *client,
struct wl_resource *resource, int32_t x,
int32_t y, int32_t width, int32_t height) {
struct xwl_host_region *host = wl_resource_get_user_data(resource);
double scale = host->xwl->scale;
int32_t x1, y1, x2, y2;
x1 = x / scale;
y1 = y / scale;
x2 = (x + width) / scale;
y2 = (y + height) / scale;
wl_region_subtract(host->proxy, x1, y1, x2 - x1, y2 - y1);
}
static const struct wl_region_interface xwl_region_implementation = {
xwl_region_destroy, xwl_region_add, xwl_region_subtract};
static void xwl_destroy_host_region(struct wl_resource *resource) {
struct xwl_host_region *host = wl_resource_get_user_data(resource);
wl_region_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_compositor_create_host_surface(struct wl_client *client,
struct wl_resource *resource,
uint32_t id) {
struct xwl_host_compositor *host = wl_resource_get_user_data(resource);
struct xwl_host_surface *host_surface;
struct xwl_window *window, *unpaired_window = NULL;
host_surface = malloc(sizeof(*host_surface));
assert(host_surface);
host_surface->xwl = host->compositor->xwl;
host_surface->contents_width = 0;
host_surface->contents_height = 0;
host_surface->contents_scale = 1;
host_surface->is_cursor = 0;
host_surface->last_event_serial = 0;
host_surface->resource = wl_resource_create(
client, &wl_surface_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(host_surface->resource,
&xwl_surface_implementation, host_surface,
xwl_destroy_host_surface);
host_surface->proxy = wl_compositor_create_surface(host->proxy);
wl_surface_set_user_data(host_surface->proxy, host_surface);
if (host_surface->xwl->viewporter) {
host_surface->viewport = wp_viewporter_get_viewport(
host_surface->xwl->viewporter->internal, host_surface->proxy);
}
wl_list_for_each(window, &host->compositor->xwl->unpaired_windows, link) {
if (window->host_surface_id == id) {
unpaired_window = window;
break;
}
}
if (unpaired_window)
xwl_window_update(window);
}
static void xwl_compositor_create_host_region(struct wl_client *client,
struct wl_resource *resource,
uint32_t id) {
struct xwl_host_compositor *host = wl_resource_get_user_data(resource);
struct xwl_host_region *host_region;
host_region = malloc(sizeof(*host_region));
assert(host_region);
host_region->xwl = host->compositor->xwl;
host_region->resource = wl_resource_create(
client, &wl_region_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(host_region->resource,
&xwl_region_implementation, host_region,
xwl_destroy_host_region);
host_region->proxy = wl_compositor_create_region(host->proxy);
wl_region_set_user_data(host_region->proxy, host_region);
}
static const struct wl_compositor_interface xwl_compositor_implementation = {
xwl_compositor_create_host_surface, xwl_compositor_create_host_region};
static void xwl_destroy_host_compositor(struct wl_resource *resource) {
struct xwl_host_compositor *host = wl_resource_get_user_data(resource);
wl_compositor_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_bind_host_compositor(struct wl_client *client, void *data,
uint32_t version, uint32_t id) {
struct xwl_compositor *compositor = (struct xwl_compositor *)data;
struct xwl_host_compositor *host;
host = malloc(sizeof(*host));
assert(host);
host->compositor = compositor;
host->resource = wl_resource_create(client, &wl_compositor_interface,
MIN(version, compositor->version), id);
wl_resource_set_implementation(host->resource, &xwl_compositor_implementation,
host, xwl_destroy_host_compositor);
host->proxy = wl_registry_bind(
wl_display_get_registry(compositor->xwl->display), compositor->id,
&wl_compositor_interface, compositor->version);
wl_compositor_set_user_data(host->proxy, host);
}
static void xwl_host_buffer_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static const struct wl_buffer_interface xwl_buffer_implementation = {
xwl_host_buffer_destroy};
static void xwl_buffer_release(void *data, struct wl_buffer *buffer) {
struct xwl_host_buffer *host = wl_buffer_get_user_data(buffer);
wl_buffer_send_release(host->resource);
}
static const struct wl_buffer_listener xwl_buffer_listener = {
xwl_buffer_release};
static void xwl_destroy_host_buffer(struct wl_resource *resource) {
struct xwl_host_buffer *host = wl_resource_get_user_data(resource);
wl_buffer_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_host_shm_pool_create_host_buffer(struct wl_client *client,
struct wl_resource *resource,
uint32_t id, int32_t offset,
int32_t width, int32_t height,
int32_t stride,
uint32_t format) {
struct xwl_host_shm_pool *host = wl_resource_get_user_data(resource);
struct xwl_host_buffer *host_buffer;
host_buffer = malloc(sizeof(*host_buffer));
assert(host_buffer);
host_buffer->width = width;
host_buffer->height = height;
host_buffer->resource =
wl_resource_create(client, &wl_buffer_interface, 1, id);
wl_resource_set_implementation(host_buffer->resource,
&xwl_buffer_implementation, host_buffer,
xwl_destroy_host_buffer);
host_buffer->proxy = wl_shm_pool_create_buffer(host->proxy, offset, width,
height, stride, format);
wl_buffer_set_user_data(host_buffer->proxy, host_buffer);
wl_buffer_add_listener(host_buffer->proxy, &xwl_buffer_listener, host_buffer);
}
static void xwl_host_shm_pool_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void xwl_host_shm_pool_resize(struct wl_client *client,
struct wl_resource *resource,
int32_t size) {
struct xwl_host_shm_pool *host = wl_resource_get_user_data(resource);
wl_shm_pool_resize(host->proxy, size);
}
static const struct wl_shm_pool_interface xwl_shm_pool_implementation = {
xwl_host_shm_pool_create_host_buffer, xwl_host_shm_pool_destroy,
xwl_host_shm_pool_resize};
static void xwl_destroy_host_shm_pool(struct wl_resource *resource) {
struct xwl_host_shm_pool *host = wl_resource_get_user_data(resource);
wl_shm_pool_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_shm_create_host_pool(struct wl_client *client,
struct wl_resource *resource, uint32_t id,
int fd, int32_t size) {
struct xwl_host_shm *host = wl_resource_get_user_data(resource);
struct xwl_host_shm_pool *host_shm_pool;
host_shm_pool = malloc(sizeof(*host_shm_pool));
assert(host_shm_pool);
host_shm_pool->resource =
wl_resource_create(client, &wl_shm_pool_interface, 1, id);
wl_resource_set_implementation(host_shm_pool->resource,
&xwl_shm_pool_implementation, host_shm_pool,
xwl_destroy_host_shm_pool);
host_shm_pool->proxy = wl_shm_create_pool(host->proxy, fd, size);
wl_shm_pool_set_user_data(host_shm_pool->proxy, host_shm_pool);
close(fd);
}
static const struct wl_shm_interface xwl_shm_implementation = {
xwl_shm_create_host_pool};
static void xwl_shm_format(void *data, struct wl_shm *shm, uint32_t format) {
struct xwl_host_shm *host = wl_shm_get_user_data(shm);
wl_shm_send_format(host->resource, format);
}
static const struct wl_shm_listener xwl_shm_listener = {xwl_shm_format};
static void xwl_destroy_host_shm(struct wl_resource *resource) {
struct xwl_host_shm *host = wl_resource_get_user_data(resource);
wl_shm_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_bind_host_shm(struct wl_client *client, void *data,
uint32_t version, uint32_t id) {
struct xwl_shm *shm = (struct xwl_shm *)data;
struct xwl_host_shm *host;
host = malloc(sizeof(*host));
assert(host);
host->shm = shm;
host->resource = wl_resource_create(client, &wl_shm_interface, 1, id);
wl_resource_set_implementation(host->resource, &xwl_shm_implementation, host,
xwl_destroy_host_shm);
host->proxy = wl_registry_bind(wl_display_get_registry(shm->xwl->display),
shm->id, &wl_shm_interface,
wl_resource_get_version(host->resource));
wl_shm_set_user_data(host->proxy, host);
wl_shm_add_listener(host->proxy, &xwl_shm_listener, host);
}
static void
xwl_host_shell_get_host_shell_surface(struct wl_client *client,
struct wl_resource *resource, uint32_t id,
struct wl_resource *surface_resource) {
assert(0);
}
static const struct wl_shell_interface xwl_shell_implementation = {
xwl_host_shell_get_host_shell_surface};
static void xwl_destroy_host_shell(struct wl_resource *resource) {
struct xwl_host_shell *host = wl_resource_get_user_data(resource);
wl_shell_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_bind_host_shell(struct wl_client *client, void *data,
uint32_t version, uint32_t id) {
struct xwl_shell *shell = (struct xwl_shell *)data;
struct xwl_host_shell *host;
host = malloc(sizeof(*host));
assert(host);
host->shell = shell;
host->resource = wl_resource_create(client, &wl_shell_interface, 1, id);
wl_resource_set_implementation(host->resource, &xwl_shell_implementation,
host, xwl_destroy_host_shell);
host->proxy = wl_registry_bind(wl_display_get_registry(shell->xwl->display),
shell->id, &wl_shell_interface,
wl_resource_get_version(host->resource));
wl_shell_set_user_data(host->proxy, host);
}
static void xwl_output_geometry(void *data, struct wl_output *output, int x,
int y, int physical_width, int physical_height,
int subpixel, const char *make,
const char *model, int transform) {
struct xwl_host_output *host = wl_output_get_user_data(output);
wl_output_send_geometry(host->resource, x, y, physical_width, physical_height,
subpixel, make, model, transform);
}
static void xwl_output_mode(void *data, struct wl_output *output,
uint32_t flags, int width, int height,
int refresh) {
struct xwl_host_output *host = wl_output_get_user_data(output);
// Wait until scale is known before sending mode.
host->flags = flags;
host->width = width;
host->height = height;
host->refresh = refresh;
}
static void xwl_output_done(void *data, struct wl_output *output) {
struct xwl_host_output *host = wl_output_get_user_data(output);
double scale = host->output->xwl->scale;
if (!host->max_scale)
return;
// Send mode now that scale is known.
wl_output_send_mode(host->resource, host->flags | WL_OUTPUT_MODE_CURRENT,
(scale * host->scale * host->width) / host->max_scale,
(scale * host->scale * host->height) / host->max_scale,
host->refresh);
wl_output_send_scale(host->resource, ceil(host->factor / scale));
wl_output_send_done(host->resource);
host->max_scale = 1.0;
if (host->aura_output)
host->max_scale = 0.0;
}
static void xwl_output_scale(void *data, struct wl_output *output,
int32_t factor) {
struct xwl_host_output *host = wl_output_get_user_data(output);
host->factor = factor;
}
static const struct wl_output_listener xwl_output_listener = {
xwl_output_geometry, xwl_output_mode, xwl_output_done, xwl_output_scale};
static void xwl_aura_output_scale(void *data, struct zaura_output *output,
uint32_t flags, uint32_t scale) {
struct xwl_host_output *host = zaura_output_get_user_data(output);
switch (scale) {
case ZAURA_OUTPUT_SCALE_FACTOR_0500:
case ZAURA_OUTPUT_SCALE_FACTOR_0600:
case ZAURA_OUTPUT_SCALE_FACTOR_0625:
case ZAURA_OUTPUT_SCALE_FACTOR_0750:
case ZAURA_OUTPUT_SCALE_FACTOR_0800:
case ZAURA_OUTPUT_SCALE_FACTOR_1000:
case ZAURA_OUTPUT_SCALE_FACTOR_1125:
case ZAURA_OUTPUT_SCALE_FACTOR_1200:
case ZAURA_OUTPUT_SCALE_FACTOR_1250:
case ZAURA_OUTPUT_SCALE_FACTOR_1500:
case ZAURA_OUTPUT_SCALE_FACTOR_1600:
case ZAURA_OUTPUT_SCALE_FACTOR_2000:
break;
default:
fprintf(stderr, "Warning: Unknown scale factor: %d\n", scale);
break;
}
host->max_scale = MAX(host->max_scale, scale / 1000.0);
if (flags & ZAURA_OUTPUT_SCALE_PROPERTY_CURRENT)
host->scale = scale / 1000.0;
}
static const struct zaura_output_listener xwl_aura_output_listener = {
xwl_aura_output_scale};
static void xwl_destroy_host_output(struct wl_resource *resource) {
struct xwl_host_output *host = wl_resource_get_user_data(resource);
if (host->aura_output)
zaura_output_destroy(host->aura_output);
if (wl_output_get_version(host->proxy) >= WL_OUTPUT_RELEASE_SINCE_VERSION) {
wl_output_release(host->proxy);
} else {
wl_output_destroy(host->proxy);
}
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_bind_host_output(struct wl_client *client, void *data,
uint32_t version, uint32_t id) {
struct xwl_output *output = (struct xwl_output *)data;
struct xwl *xwl = output->xwl;
struct xwl_host_output *host;
host = malloc(sizeof(*host));
assert(host);
host->output = output;
host->resource = wl_resource_create(client, &wl_output_interface,
MIN(version, output->version), id);
wl_resource_set_implementation(host->resource, NULL, host,
xwl_destroy_host_output);
host->proxy = wl_registry_bind(wl_display_get_registry(xwl->display),
output->id, &wl_output_interface,
wl_resource_get_version(host->resource));
wl_output_set_user_data(host->proxy, host);
wl_output_add_listener(host->proxy, &xwl_output_listener, host);
host->aura_output = NULL;
host->flags = 0;
host->width = 1024;
host->height = 768;
host->refresh = 60000;
host->factor = 1;
host->scale = 1.0;
host->max_scale = 1.0;
if (xwl->aura_shell &&
(xwl->aura_shell->version >= ZAURA_SHELL_GET_AURA_OUTPUT_SINCE_VERSION)) {
host->max_scale = 0.0;
host->aura_output =
zaura_shell_get_aura_output(xwl->aura_shell->internal, host->proxy);
zaura_output_set_user_data(host->aura_output, host);
zaura_output_add_listener(host->aura_output, &xwl_aura_output_listener,
host);
}
}
static void xwl_internal_data_offer_destroy(struct xwl_data_offer *host) {
wl_data_offer_destroy(host->internal);
free(host);
}
static void xwl_set_selection(struct xwl *xwl,
struct xwl_data_offer *data_offer) {
if (xwl->selection_data_offer) {
xwl_internal_data_offer_destroy(xwl->selection_data_offer);
xwl->selection_data_offer = NULL;
}
if (xwl->clipboard_manager) {
if (!data_offer) {
if (xwl->selection_owner == xwl->selection_window)
xcb_set_selection_owner(xwl->connection, XCB_ATOM_NONE,
xwl->atoms[ATOM_CLIPBOARD].value,
xwl->selection_timestamp);
return;
}
xcb_set_selection_owner(xwl->connection, xwl->selection_window,
xwl->atoms[ATOM_CLIPBOARD].value, XCB_CURRENT_TIME);
}
xwl->selection_data_offer = data_offer;
}
static const char *xwl_utf8_mime_type = "text/plain;charset=utf-8";
static void xwl_internal_data_offer_offer(void *data,
struct wl_data_offer *data_offer,
const char *type) {
struct xwl_data_offer *host = data;
if (strcmp(type, xwl_utf8_mime_type) == 0)
host->utf8_text = 1;
}
static void xwl_internal_data_offer_source_actions(
void *data, struct wl_data_offer *data_offer, uint32_t source_actions) {}
static void xwl_internal_data_offer_action(void *data,
struct wl_data_offer *data_offer,
uint32_t dnd_action) {}
static const struct wl_data_offer_listener xwl_internal_data_offer_listener = {
xwl_internal_data_offer_offer, xwl_internal_data_offer_source_actions,
xwl_internal_data_offer_action};
static void
xwl_internal_data_device_data_offer(void *data,
struct wl_data_device *data_device,
struct wl_data_offer *data_offer) {
struct xwl *xwl = (struct xwl *)data;
struct xwl_data_offer *host_data_offer;
host_data_offer = malloc(sizeof(*host_data_offer));
assert(host_data_offer);
host_data_offer->xwl = xwl;
host_data_offer->internal = data_offer;
host_data_offer->utf8_text = 0;
wl_data_offer_add_listener(host_data_offer->internal,
&xwl_internal_data_offer_listener,
host_data_offer);
}
static void xwl_internal_data_device_enter(void *data,
struct wl_data_device *data_device,
uint32_t serial,
struct wl_surface *surface,
wl_fixed_t x, wl_fixed_t y,
struct wl_data_offer *data_offer) {}
static void xwl_internal_data_device_leave(void *data,
struct wl_data_device *data_device) {
}
static void xwl_internal_data_device_motion(void *data,
struct wl_data_device *data_device,
uint32_t time, wl_fixed_t x,
wl_fixed_t y) {}
static void xwl_internal_data_device_drop(void *data,
struct wl_data_device *data_device) {}
static void
xwl_internal_data_device_selection(void *data,
struct wl_data_device *data_device,
struct wl_data_offer *data_offer) {
struct xwl *xwl = (struct xwl *)data;
struct xwl_data_offer *host_data_offer =
data_offer ? wl_data_offer_get_user_data(data_offer) : NULL;
xwl_set_selection(xwl, host_data_offer);
}
static const struct wl_data_device_listener xwl_internal_data_device_listener =
{xwl_internal_data_device_data_offer, xwl_internal_data_device_enter,
xwl_internal_data_device_leave, xwl_internal_data_device_motion,
xwl_internal_data_device_drop, xwl_internal_data_device_selection};
static void xwl_host_pointer_set_cursor(struct wl_client *client,
struct wl_resource *resource,
uint32_t serial,
struct wl_resource *surface_resource,
int32_t hotspot_x, int32_t hotspot_y) {
struct xwl_host_pointer *host = wl_resource_get_user_data(resource);
struct xwl_host_surface *host_surface = NULL;
double scale = host->seat->xwl->scale;
if (surface_resource) {
host_surface = wl_resource_get_user_data(surface_resource);
host_surface->is_cursor = 1;
if (host_surface->contents_width && host_surface->contents_height)
wl_surface_commit(host_surface->proxy);
}
wl_pointer_set_cursor(host->proxy, serial,
host_surface ? host_surface->proxy : NULL,
hotspot_x / scale, hotspot_y / scale);
}
static void xwl_host_pointer_release(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static const struct wl_pointer_interface xwl_pointer_implementation = {
xwl_host_pointer_set_cursor, xwl_host_pointer_release};
static void xwl_set_last_event_serial(struct xwl_host_surface *host_surface) {
host_surface->last_event_serial =
wl_display_next_serial(host_surface->xwl->display);
}
static void xwl_pointer_set_focus(struct xwl_host_pointer *host,
uint32_t serial,
struct xwl_host_surface *host_surface,
wl_fixed_t x, wl_fixed_t y) {
struct wl_resource *surface_resource =
host_surface ? host_surface->resource : NULL;
if (surface_resource == host->focus_resource)
return;
if (host->focus_resource)
wl_pointer_send_leave(host->resource, serial, host->focus_resource);
wl_list_remove(&host->focus_resource_listener.link);
wl_list_init(&host->focus_resource_listener.link);
host->focus_resource = surface_resource;
host->focus_serial = serial;
if (surface_resource) {
double scale = host->seat->xwl->scale;
if (host->seat->xwl->xwayland) {
// Make sure focus surface is on top before sending enter event.
xwl_restack_windows(host->seat->xwl,
wl_resource_get_id(surface_resource));
xwl_roundtrip(host->seat->xwl);
}
wl_resource_add_destroy_listener(surface_resource,
&host->focus_resource_listener);
wl_pointer_send_enter(host->resource, serial, surface_resource, x * scale,
y * scale);
}
}
static void xwl_pointer_enter(void *data, struct wl_pointer *pointer,
uint32_t serial, struct wl_surface *surface,
wl_fixed_t x, wl_fixed_t y) {
struct xwl_host_pointer *host = wl_pointer_get_user_data(pointer);
struct xwl_host_surface *host_surface =
surface ? wl_surface_get_user_data(surface) : NULL;
if (!host_surface)
return;
xwl_pointer_set_focus(host, serial, host_surface, x, y);
host->seat->last_serial = serial;
}
static void xwl_pointer_leave(void *data, struct wl_pointer *pointer,
uint32_t serial, struct wl_surface *surface) {
struct xwl_host_pointer *host = wl_pointer_get_user_data(pointer);
xwl_pointer_set_focus(host, serial, NULL, 0, 0);
}
static void xwl_pointer_motion(void *data, struct wl_pointer *pointer,
uint32_t time, wl_fixed_t x, wl_fixed_t y) {
struct xwl_host_pointer *host = wl_pointer_get_user_data(pointer);
double scale = host->seat->xwl->scale;
wl_pointer_send_motion(host->resource, time, x * scale, y * scale);
}
static void xwl_pointer_button(void *data, struct wl_pointer *pointer,
uint32_t serial, uint32_t time, uint32_t button,
uint32_t state) {
struct xwl_host_pointer *host = wl_pointer_get_user_data(pointer);
wl_pointer_send_button(host->resource, serial, time, button, state);
host->seat->last_serial = serial;
}
static void xwl_pointer_axis(void *data, struct wl_pointer *pointer,
uint32_t time, uint32_t axis, wl_fixed_t value) {
struct xwl_host_pointer *host = wl_pointer_get_user_data(pointer);
double scale = host->seat->xwl->scale;
wl_pointer_send_axis(host->resource, time, axis, value * scale);
}
static void xwl_pointer_frame(void *data, struct wl_pointer *pointer) {
struct xwl_host_pointer *host = wl_pointer_get_user_data(pointer);
if (host->focus_resource)
xwl_set_last_event_serial(wl_resource_get_user_data(host->focus_resource));
wl_pointer_send_frame(host->resource);
}
void xwl_pointer_axis_source(void *data, struct wl_pointer *pointer,
uint32_t axis_source) {
struct xwl_host_pointer *host = wl_pointer_get_user_data(pointer);
wl_pointer_send_axis_source(host->resource, axis_source);
}
static void xwl_pointer_axis_stop(void *data, struct wl_pointer *pointer,
uint32_t time, uint32_t axis) {
struct xwl_host_pointer *host = wl_pointer_get_user_data(pointer);
wl_pointer_send_axis_stop(host->resource, time, axis);
}
static void xwl_pointer_axis_discrete(void *data, struct wl_pointer *pointer,
uint32_t axis, int32_t discrete) {
struct xwl_host_pointer *host = wl_pointer_get_user_data(pointer);
wl_pointer_send_axis_discrete(host->resource, axis, discrete);
}
static const struct wl_pointer_listener xwl_pointer_listener = {
xwl_pointer_enter, xwl_pointer_leave, xwl_pointer_motion,
xwl_pointer_button, xwl_pointer_axis, xwl_pointer_frame,
xwl_pointer_axis_source, xwl_pointer_axis_stop, xwl_pointer_axis_discrete};
static void xwl_host_keyboard_release(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static const struct wl_keyboard_interface xwl_keyboard_implementation = {
xwl_host_keyboard_release};
static void xwl_keyboard_keymap(void *data, struct wl_keyboard *keyboard,
uint32_t format, int32_t fd, uint32_t size) {
struct xwl_host_keyboard *host = wl_keyboard_get_user_data(keyboard);
wl_keyboard_send_keymap(host->resource, format, fd, size);
close(fd);
}
static void xwl_keyboard_set_focus(struct xwl_host_keyboard *host,
uint32_t serial,
struct xwl_host_surface *host_surface,
struct wl_array *keys) {
struct wl_resource *surface_resource =
host_surface ? host_surface->resource : NULL;
if (surface_resource == host->focus_resource)
return;
if (host->focus_resource)
wl_keyboard_send_leave(host->resource, serial, host->focus_resource);
wl_list_remove(&host->focus_resource_listener.link);
wl_list_init(&host->focus_resource_listener.link);
host->focus_resource = surface_resource;
host->focus_serial = serial;
if (surface_resource) {
wl_resource_add_destroy_listener(surface_resource,
&host->focus_resource_listener);
wl_keyboard_send_enter(host->resource, serial, surface_resource, keys);
}
host->seat->last_serial = serial;
}
static void xwl_keyboard_enter(void *data, struct wl_keyboard *keyboard,
uint32_t serial, struct wl_surface *surface,
struct wl_array *keys) {
struct xwl_host_keyboard *host = wl_keyboard_get_user_data(keyboard);
struct xwl_host_surface *host_surface =
surface ? wl_surface_get_user_data(surface) : NULL;
if (!host_surface)
return;
xwl_keyboard_set_focus(host, serial, host_surface, keys);
host->seat->last_serial = serial;
}
static void xwl_keyboard_leave(void *data, struct wl_keyboard *keyboard,
uint32_t serial, struct wl_surface *surface) {
struct xwl_host_keyboard *host = wl_keyboard_get_user_data(keyboard);
xwl_keyboard_set_focus(host, serial, NULL, NULL);
}
static void xwl_keyboard_key(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t time, uint32_t key,
uint32_t state) {
struct xwl_host_keyboard *host = wl_keyboard_get_user_data(keyboard);
if (host->focus_resource)
xwl_set_last_event_serial(wl_resource_get_user_data(host->focus_resource));
wl_keyboard_send_key(host->resource, serial, time, key, state);
host->seat->last_serial = serial;
}
static void xwl_keyboard_modifiers(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t mods_depressed,
uint32_t mods_latched, uint32_t mods_locked,
uint32_t group) {
struct xwl_host_keyboard *host = wl_keyboard_get_user_data(keyboard);
wl_keyboard_send_modifiers(host->resource, serial, mods_depressed,
mods_latched, mods_locked, group);
host->seat->last_serial = serial;
}
static void xwl_keyboard_repeat_info(void *data, struct wl_keyboard *keyboard,
int32_t rate, int32_t delay) {
struct xwl_host_keyboard *host = wl_keyboard_get_user_data(keyboard);
wl_keyboard_send_repeat_info(host->resource, rate, delay);
}
static const struct wl_keyboard_listener xwl_keyboard_listener = {
xwl_keyboard_keymap, xwl_keyboard_enter, xwl_keyboard_leave,
xwl_keyboard_key, xwl_keyboard_modifiers, xwl_keyboard_repeat_info};
static void xwl_host_touch_release(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static const struct wl_touch_interface xwl_touch_implementation = {
xwl_host_touch_release};
static void xwl_host_touch_down(void *data, struct wl_touch *touch,
uint32_t serial, uint32_t time,
struct wl_surface *surface, int32_t id,
wl_fixed_t x, wl_fixed_t y) {
struct xwl_host_touch *host = wl_touch_get_user_data(touch);
struct xwl_host_surface *host_surface =
surface ? wl_surface_get_user_data(surface) : NULL;
double scale = host->seat->xwl->scale;
if (!host_surface)
return;
if (host_surface->resource != host->focus_resource) {
wl_list_remove(&host->focus_resource_listener.link);
wl_list_init(&host->focus_resource_listener.link);
host->focus_resource = host_surface->resource;
wl_resource_add_destroy_listener(host_surface->resource,
&host->focus_resource_listener);
}
if (host->seat->xwl->xwayland) {
// Make sure focus surface is on top before sending down event.
xwl_restack_windows(host->seat->xwl,
wl_resource_get_id(host_surface->resource));
xwl_roundtrip(host->seat->xwl);
}
wl_touch_send_down(host->resource, serial, time, host_surface->resource, id,
x * scale, y * scale);
host->seat->last_serial = serial;
}
static void xwl_host_touch_up(void *data, struct wl_touch *touch,
uint32_t serial, uint32_t time, int32_t id) {
struct xwl_host_touch *host = wl_touch_get_user_data(touch);
wl_list_remove(&host->focus_resource_listener.link);
wl_list_init(&host->focus_resource_listener.link);
host->focus_resource = NULL;
wl_touch_send_up(host->resource, serial, time, id);
host->seat->last_serial = serial;
}
static void xwl_host_touch_motion(void *data, struct wl_touch *touch,
uint32_t time, int32_t id, wl_fixed_t x,
wl_fixed_t y) {
struct xwl_host_touch *host = wl_touch_get_user_data(touch);
double scale = host->seat->xwl->scale;
wl_touch_send_motion(host->resource, time, id, x * scale, y * scale);
}
static void xwl_host_touch_frame(void *data, struct wl_touch *touch) {
struct xwl_host_touch *host = wl_touch_get_user_data(touch);
if (host->focus_resource)
xwl_set_last_event_serial(wl_resource_get_user_data(host->focus_resource));
wl_touch_send_frame(host->resource);
}
static void xwl_host_touch_cancel(void *data, struct wl_touch *touch) {
struct xwl_host_touch *host = wl_touch_get_user_data(touch);
wl_touch_send_cancel(host->resource);
}
static const struct wl_touch_listener xwl_touch_listener = {
xwl_host_touch_down, xwl_host_touch_up, xwl_host_touch_motion,
xwl_host_touch_frame, xwl_host_touch_cancel};
static void xwl_destroy_host_pointer(struct wl_resource *resource) {
struct xwl_host_pointer *host = wl_resource_get_user_data(resource);
if (wl_pointer_get_version(host->proxy) >= WL_POINTER_RELEASE_SINCE_VERSION) {
wl_pointer_release(host->proxy);
} else {
wl_pointer_destroy(host->proxy);
}
wl_list_remove(&host->focus_resource_listener.link);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_pointer_focus_resource_destroyed(struct wl_listener *listener,
void *data) {
struct xwl_host_pointer *host;
host = wl_container_of(listener, host, focus_resource_listener);
xwl_pointer_set_focus(host, host->focus_serial, NULL, 0, 0);
}
static void xwl_host_seat_get_host_pointer(struct wl_client *client,
struct wl_resource *resource,
uint32_t id) {
struct xwl_host_seat *host = wl_resource_get_user_data(resource);
struct xwl_host_pointer *host_pointer;
host_pointer = malloc(sizeof(*host_pointer));
assert(host_pointer);
host_pointer->seat = host->seat;
host_pointer->resource = wl_resource_create(
client, &wl_pointer_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(host_pointer->resource,
&xwl_pointer_implementation, host_pointer,
xwl_destroy_host_pointer);
host_pointer->proxy = wl_seat_get_pointer(host->proxy);
wl_pointer_set_user_data(host_pointer->proxy, host_pointer);
wl_pointer_add_listener(host_pointer->proxy, &xwl_pointer_listener,
host_pointer);
wl_list_init(&host_pointer->focus_resource_listener.link);
host_pointer->focus_resource_listener.notify =
xwl_pointer_focus_resource_destroyed;
host_pointer->focus_resource = NULL;
host_pointer->focus_serial = 0;
}
static void xwl_destroy_host_keyboard(struct wl_resource *resource) {
struct xwl_host_keyboard *host = wl_resource_get_user_data(resource);
if (wl_keyboard_get_version(host->proxy) >=
WL_KEYBOARD_RELEASE_SINCE_VERSION) {
wl_keyboard_release(host->proxy);
} else {
wl_keyboard_destroy(host->proxy);
}
wl_list_remove(&host->focus_resource_listener.link);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_keyboard_focus_resource_destroyed(struct wl_listener *listener,
void *data) {
struct xwl_host_keyboard *host;
host = wl_container_of(listener, host, focus_resource_listener);
xwl_keyboard_set_focus(host, host->focus_serial, NULL, NULL);
}
static void xwl_host_seat_get_host_keyboard(struct wl_client *client,
struct wl_resource *resource,
uint32_t id) {
struct xwl_host_seat *host = wl_resource_get_user_data(resource);
struct xwl_host_keyboard *host_keyboard;
host_keyboard = malloc(sizeof(*host_keyboard));
assert(host_keyboard);
host_keyboard->seat = host->seat;
host_keyboard->resource = wl_resource_create(
client, &wl_keyboard_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(host_keyboard->resource,
&xwl_keyboard_implementation, host_keyboard,
xwl_destroy_host_keyboard);
host_keyboard->proxy = wl_seat_get_keyboard(host->proxy);
wl_keyboard_set_user_data(host_keyboard->proxy, host_keyboard);
wl_keyboard_add_listener(host_keyboard->proxy, &xwl_keyboard_listener,
host_keyboard);
wl_list_init(&host_keyboard->focus_resource_listener.link);
host_keyboard->focus_resource_listener.notify =
xwl_keyboard_focus_resource_destroyed;
host_keyboard->focus_resource = NULL;
host_keyboard->focus_serial = 0;
}
static void xwl_destroy_host_touch(struct wl_resource *resource) {
struct xwl_host_touch *host = wl_resource_get_user_data(resource);
if (wl_touch_get_version(host->proxy) >= WL_TOUCH_RELEASE_SINCE_VERSION) {
wl_touch_release(host->proxy);
} else {
wl_touch_destroy(host->proxy);
}
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_touch_focus_resource_destroyed(struct wl_listener *listener,
void *data) {
struct xwl_host_touch *host;
host = wl_container_of(listener, host, focus_resource_listener);
wl_list_remove(&host->focus_resource_listener.link);
wl_list_init(&host->focus_resource_listener.link);
host->focus_resource = NULL;
}
static void xwl_host_seat_get_host_touch(struct wl_client *client,
struct wl_resource *resource,
uint32_t id) {
struct xwl_host_seat *host = wl_resource_get_user_data(resource);
struct xwl_host_touch *host_touch;
host_touch = malloc(sizeof(*host_touch));
assert(host_touch);
host_touch->seat = host->seat;
host_touch->resource = wl_resource_create(
client, &wl_touch_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(host_touch->resource,
&xwl_touch_implementation, host_touch,
xwl_destroy_host_touch);
host_touch->proxy = wl_seat_get_touch(host->proxy);
wl_touch_set_user_data(host_touch->proxy, host_touch);
wl_touch_add_listener(host_touch->proxy, &xwl_touch_listener, host_touch);
wl_list_init(&host_touch->focus_resource_listener.link);
host_touch->focus_resource_listener.notify =
xwl_touch_focus_resource_destroyed;
host_touch->focus_resource = NULL;
}
static void xwl_host_seat_release(struct wl_client *client,
struct wl_resource *resource) {
struct xwl_host_seat *host = wl_resource_get_user_data(resource);
wl_seat_release(host->proxy);
}
static const struct wl_seat_interface xwl_seat_implementation = {
xwl_host_seat_get_host_pointer, xwl_host_seat_get_host_keyboard,
xwl_host_seat_get_host_touch, xwl_host_seat_release};
static void xwl_seat_capabilities(void *data, struct wl_seat *seat,
uint32_t capabilities) {
struct xwl_host_seat *host = wl_seat_get_user_data(seat);
wl_seat_send_capabilities(host->resource, capabilities);
}
static void xwl_seat_name(void *data, struct wl_seat *seat, const char *name) {
struct xwl_host_seat *host = wl_seat_get_user_data(seat);
if (wl_resource_get_version(host->resource) >= WL_SEAT_NAME_SINCE_VERSION)
wl_seat_send_name(host->resource, name);
}
static const struct wl_seat_listener xwl_seat_listener = {xwl_seat_capabilities,
xwl_seat_name};
static void xwl_destroy_host_seat(struct wl_resource *resource) {
struct xwl_host_seat *host = wl_resource_get_user_data(resource);
if (host->seat->xwl->default_seat == host)
host->seat->xwl->default_seat = NULL;
if (wl_seat_get_version(host->proxy) >= WL_SEAT_RELEASE_SINCE_VERSION)
wl_seat_release(host->proxy);
else
wl_seat_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_bind_host_seat(struct wl_client *client, void *data,
uint32_t version, uint32_t id) {
struct xwl_seat *seat = (struct xwl_seat *)data;
struct xwl_host_seat *host;
host = malloc(sizeof(*host));
assert(host);
host->seat = seat;
host->resource = wl_resource_create(client, &wl_seat_interface,
MIN(version, seat->version), id);
wl_resource_set_implementation(host->resource, &xwl_seat_implementation, host,
xwl_destroy_host_seat);
host->proxy = wl_registry_bind(wl_display_get_registry(seat->xwl->display),
seat->id, &wl_seat_interface,
wl_resource_get_version(host->resource));
wl_seat_set_user_data(host->proxy, host);
wl_seat_add_listener(host->proxy, &xwl_seat_listener, host);
if (!seat->xwl->default_seat) {
seat->xwl->default_seat = host;
if (seat->xwl->data_device_manager &&
seat->xwl->data_device_manager->internal) {
seat->xwl->selection_data_device = wl_data_device_manager_get_data_device(
seat->xwl->data_device_manager->internal, host->proxy);
wl_data_device_add_listener(seat->xwl->selection_data_device,
&xwl_internal_data_device_listener,
seat->xwl);
}
}
}
static void xwl_drm_authenticate(struct wl_client *client,
struct wl_resource *resource, uint32_t id) {}
static void xwl_drm_create_buffer(struct wl_client *client,
struct wl_resource *resource, uint32_t id,
uint32_t name, int32_t width, int32_t height,
uint32_t stride, uint32_t format) {
assert(0);
}
static void xwl_drm_create_planar_buffer(
struct wl_client *client, struct wl_resource *resource, uint32_t id,
uint32_t name, int32_t width, int32_t height, uint32_t format,
int32_t offset0, int32_t stride0, int32_t offset1, int32_t stride1,
int32_t offset2, int32_t stride2) {
assert(0);
}
static void xwl_drm_create_prime_buffer(
struct wl_client *client, struct wl_resource *resource, uint32_t id,
int32_t name, int32_t width, int32_t height, uint32_t format,
int32_t offset0, int32_t stride0, int32_t offset1, int32_t stride1,
int32_t offset2, int32_t stride2) {
struct xwl_host_drm *host = wl_resource_get_user_data(resource);
struct xwl_host_buffer *host_buffer;
struct zwp_linux_buffer_params_v1 *buffer_params;
assert(name >= 0);
assert(!offset1);
assert(!stride1);
assert(!offset2);
assert(!stride2);
host_buffer = malloc(sizeof(*host_buffer));
assert(host_buffer);
host_buffer->width = width;
host_buffer->height = height;
host_buffer->resource =
wl_resource_create(client, &wl_buffer_interface, 1, id);
wl_resource_set_implementation(host_buffer->resource,
&xwl_buffer_implementation, host_buffer,
xwl_destroy_host_buffer);
buffer_params =
zwp_linux_dmabuf_v1_create_params(host->linux_dmabuf->internal);
zwp_linux_buffer_params_v1_add(buffer_params, name, 0, offset0, stride0, 0,
0);
host_buffer->proxy = zwp_linux_buffer_params_v1_create_immed(
buffer_params, width, height, format, 0);
zwp_linux_buffer_params_v1_destroy(buffer_params);
close(name);
wl_buffer_set_user_data(host_buffer->proxy, host_buffer);
wl_buffer_add_listener(host_buffer->proxy, &xwl_buffer_listener, host_buffer);
}
static const struct wl_drm_interface xwl_drm_implementation = {
xwl_drm_authenticate, xwl_drm_create_buffer, xwl_drm_create_planar_buffer,
xwl_drm_create_prime_buffer};
static void xwl_destroy_host_drm(struct wl_resource *resource) {
struct xwl_host_drm *host = wl_resource_get_user_data(resource);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_bind_host_drm(struct wl_client *client, void *data,
uint32_t version, uint32_t id) {
struct xwl_linux_dmabuf *linux_dmabuf = (struct xwl_linux_dmabuf *)data;
struct xwl_host_drm *host;
host = malloc(sizeof(*host));
assert(host);
host->linux_dmabuf = linux_dmabuf;
host->version = MIN(version, 2);
host->resource =
wl_resource_create(client, &wl_drm_interface, host->version, id);
wl_resource_set_implementation(host->resource, &xwl_drm_implementation, host,
xwl_destroy_host_drm);
wl_drm_send_device(host->resource, linux_dmabuf->xwl->drm_device);
wl_drm_send_format(host->resource, WL_DRM_FORMAT_ARGB8888);
wl_drm_send_format(host->resource, WL_DRM_FORMAT_XRGB8888);
wl_drm_send_format(host->resource, WL_DRM_FORMAT_RGB565);
if (host->version >= WL_DRM_CREATE_PRIME_BUFFER_SINCE_VERSION)
wl_drm_send_capabilities(host->resource, WL_DRM_CAPABILITY_PRIME);
}
static void xwl_xdg_positioner_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void xwl_xdg_positioner_set_size(struct wl_client *client,
struct wl_resource *resource,
int32_t width, int32_t height) {
struct xwl_host_xdg_positioner *host = wl_resource_get_user_data(resource);
double scale = host->xwl->scale;
zxdg_positioner_v6_set_size(host->proxy, width / scale, height / scale);
}
static void xwl_xdg_positioner_set_anchor_rect(struct wl_client *client,
struct wl_resource *resource,
int32_t x, int32_t y,
int32_t width, int32_t height) {
struct xwl_host_xdg_positioner *host = wl_resource_get_user_data(resource);
double scale = host->xwl->scale;
int32_t x1, y1, x2, y2;
x1 = x / scale;
y1 = y / scale;
x2 = (x + width) / scale;
y2 = (y + height) / scale;
zxdg_positioner_v6_set_anchor_rect(host->proxy, x1, y1, x2 - x1, y2 - y1);
}
static void xwl_xdg_positioner_set_anchor(struct wl_client *client,
struct wl_resource *resource,
uint32_t anchor) {
struct xwl_host_xdg_positioner *host = wl_resource_get_user_data(resource);
zxdg_positioner_v6_set_anchor(host->proxy, anchor);
}
static void xwl_xdg_positioner_set_gravity(struct wl_client *client,
struct wl_resource *resource,
uint32_t gravity) {
struct xwl_host_xdg_positioner *host = wl_resource_get_user_data(resource);
zxdg_positioner_v6_set_gravity(host->proxy, gravity);
}
static void
xwl_xdg_positioner_set_constraint_adjustment(struct wl_client *client,
struct wl_resource *resource,
uint32_t constraint_adjustment) {
struct xwl_host_xdg_positioner *host = wl_resource_get_user_data(resource);
zxdg_positioner_v6_set_constraint_adjustment(host->proxy,
constraint_adjustment);
}
static void xwl_xdg_positioner_set_offset(struct wl_client *client,
struct wl_resource *resource,
int32_t x, int32_t y) {
struct xwl_host_xdg_positioner *host = wl_resource_get_user_data(resource);
double scale = host->xwl->scale;
zxdg_positioner_v6_set_offset(host->proxy, x / scale, y / scale);
}
static const struct zxdg_positioner_v6_interface
xwl_xdg_positioner_implementation = {
xwl_xdg_positioner_destroy,
xwl_xdg_positioner_set_size,
xwl_xdg_positioner_set_anchor_rect,
xwl_xdg_positioner_set_anchor,
xwl_xdg_positioner_set_gravity,
xwl_xdg_positioner_set_constraint_adjustment,
xwl_xdg_positioner_set_offset};
static void xwl_destroy_host_xdg_positioner(struct wl_resource *resource) {
struct xwl_host_xdg_positioner *host = wl_resource_get_user_data(resource);
zxdg_positioner_v6_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_xdg_popup_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void xwl_xdg_popup_grab(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *seat_resource,
uint32_t serial) {
struct xwl_host_xdg_popup *host = wl_resource_get_user_data(resource);
struct xwl_host_seat *host_seat = wl_resource_get_user_data(seat_resource);
zxdg_popup_v6_grab(host->proxy, host_seat->proxy, serial);
}
static const struct zxdg_popup_v6_interface xwl_xdg_popup_implementation = {
xwl_xdg_popup_destroy, xwl_xdg_popup_grab};
static void xwl_xdg_popup_configure(void *data, struct zxdg_popup_v6 *xdg_popup,
int32_t x, int32_t y, int32_t width,
int32_t height) {
struct xwl_host_xdg_popup *host = zxdg_popup_v6_get_user_data(xdg_popup);
double scale = host->xwl->scale;
int32_t x1, y1, x2, y2;
x1 = x * scale;
y1 = y * scale;
x2 = (x + width) * scale;
y2 = (y + height) * scale;
zxdg_popup_v6_send_configure(host->resource, x1, y1, x2 - x1, y2 - y1);
}
static void xwl_xdg_popup_popup_done(void *data,
struct zxdg_popup_v6 *xdg_popup) {
struct xwl_host_xdg_popup *host = zxdg_popup_v6_get_user_data(xdg_popup);
zxdg_popup_v6_send_popup_done(host->resource);
}
static const struct zxdg_popup_v6_listener xwl_xdg_popup_listener = {
xwl_xdg_popup_configure, xwl_xdg_popup_popup_done};
static void xwl_destroy_host_xdg_popup(struct wl_resource *resource) {
struct xwl_host_xdg_popup *host = wl_resource_get_user_data(resource);
zxdg_popup_v6_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_xdg_toplevel_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void xwl_xdg_toplevel_set_parent(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *parent_resource) {
struct xwl_host_xdg_toplevel *host = wl_resource_get_user_data(resource);
struct xwl_host_xdg_toplevel *host_parent =
parent_resource ? wl_resource_get_user_data(parent_resource) : NULL;
zxdg_toplevel_v6_set_parent(host->proxy,
host_parent ? host_parent->proxy : NULL);
}
static void xwl_xdg_toplevel_set_title(struct wl_client *client,
struct wl_resource *resource,
const char *title) {
struct xwl_host_xdg_toplevel *host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_set_title(host->proxy, title);
}
static void xwl_xdg_toplevel_set_app_id(struct wl_client *client,
struct wl_resource *resource,
const char *app_id) {
struct xwl_host_xdg_toplevel *host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_set_app_id(host->proxy, app_id);
}
static void xwl_xdg_toplevel_show_window_menu(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *seat,
uint32_t serial, int32_t x,
int32_t y) {
assert(0);
}
static void xwl_xdg_toplevel_move(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *seat_resource,
uint32_t serial) {
struct xwl_host_xdg_toplevel *host = wl_resource_get_user_data(resource);
struct xwl_host_seat *host_seat =
seat_resource ? wl_resource_get_user_data(seat_resource) : NULL;
zxdg_toplevel_v6_move(host->proxy, host_seat ? host_seat->proxy : NULL,
serial);
}
static void xwl_xdg_toplevel_resize(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *seat_resource,
uint32_t serial, uint32_t edges) {
struct xwl_host_xdg_toplevel *host = wl_resource_get_user_data(resource);
struct xwl_host_seat *host_seat =
seat_resource ? wl_resource_get_user_data(seat_resource) : NULL;
zxdg_toplevel_v6_resize(host->proxy, host_seat ? host_seat->proxy : NULL,
serial, edges);
}
static void xwl_xdg_toplevel_set_max_size(struct wl_client *client,
struct wl_resource *resource,
int32_t width, int32_t height) {
struct xwl_host_xdg_toplevel *host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_set_max_size(host->proxy, width, height);
}
static void xwl_xdg_toplevel_set_min_size(struct wl_client *client,
struct wl_resource *resource,
int32_t width, int32_t height) {
struct xwl_host_xdg_toplevel *host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_set_min_size(host->proxy, width, height);
}
static void xwl_xdg_toplevel_set_maximized(struct wl_client *client,
struct wl_resource *resource) {
struct xwl_host_xdg_toplevel *host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_set_maximized(host->proxy);
}
static void xwl_xdg_toplevel_unset_maximized(struct wl_client *client,
struct wl_resource *resource) {
struct xwl_host_xdg_toplevel *host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_unset_maximized(host->proxy);
}
static void
xwl_xdg_toplevel_set_fullscreen(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *output_resource) {
struct xwl_host_xdg_toplevel *host = wl_resource_get_user_data(resource);
struct xwl_host_output *host_output =
output_resource ? wl_resource_get_user_data(resource) : NULL;
zxdg_toplevel_v6_set_fullscreen(host->proxy,
host_output ? host_output->proxy : NULL);
}
static void xwl_xdg_toplevel_unset_fullscreen(struct wl_client *client,
struct wl_resource *resource) {
struct xwl_host_xdg_toplevel *host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_unset_fullscreen(host->proxy);
}
static void xwl_xdg_toplevel_set_minimized(struct wl_client *client,
struct wl_resource *resource) {
struct xwl_host_xdg_toplevel *host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_set_minimized(host->proxy);
}
static const struct zxdg_toplevel_v6_interface xwl_xdg_toplevel_implementation =
{xwl_xdg_toplevel_destroy, xwl_xdg_toplevel_set_parent,
xwl_xdg_toplevel_set_title, xwl_xdg_toplevel_set_app_id,
xwl_xdg_toplevel_show_window_menu, xwl_xdg_toplevel_move,
xwl_xdg_toplevel_resize, xwl_xdg_toplevel_set_max_size,
xwl_xdg_toplevel_set_min_size, xwl_xdg_toplevel_set_maximized,
xwl_xdg_toplevel_unset_maximized, xwl_xdg_toplevel_set_fullscreen,
xwl_xdg_toplevel_unset_fullscreen, xwl_xdg_toplevel_set_minimized};
static void xwl_xdg_toplevel_configure(void *data,
struct zxdg_toplevel_v6 *xdg_toplevel,
int32_t width, int32_t height,
struct wl_array *states) {
struct xwl_host_xdg_toplevel *host =
zxdg_toplevel_v6_get_user_data(xdg_toplevel);
double scale = host->xwl->scale;
zxdg_toplevel_v6_send_configure(host->resource, width * scale, height * scale,
states);
}
static void xwl_xdg_toplevel_close(void *data,
struct zxdg_toplevel_v6 *xdg_toplevel) {
struct xwl_host_xdg_toplevel *host =
zxdg_toplevel_v6_get_user_data(xdg_toplevel);
zxdg_toplevel_v6_send_close(host->resource);
}
static const struct zxdg_toplevel_v6_listener xwl_xdg_toplevel_listener = {
xwl_xdg_toplevel_configure, xwl_xdg_toplevel_close};
static void xwl_destroy_host_xdg_toplevel(struct wl_resource *resource) {
struct xwl_host_xdg_toplevel *host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_xdg_surface_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void xwl_xdg_surface_get_toplevel(struct wl_client *client,
struct wl_resource *resource,
uint32_t id) {
struct xwl_host_xdg_surface *host = wl_resource_get_user_data(resource);
struct xwl_host_xdg_toplevel *host_xdg_toplevel;
host_xdg_toplevel = malloc(sizeof(*host_xdg_toplevel));
assert(host_xdg_toplevel);
host_xdg_toplevel->xwl = host->xwl;
host_xdg_toplevel->resource =
wl_resource_create(client, &zxdg_toplevel_v6_interface, 1, id);
wl_resource_set_implementation(
host_xdg_toplevel->resource, &xwl_xdg_toplevel_implementation,
host_xdg_toplevel, xwl_destroy_host_xdg_toplevel);
host_xdg_toplevel->proxy = zxdg_surface_v6_get_toplevel(host->proxy);
zxdg_toplevel_v6_set_user_data(host_xdg_toplevel->proxy, host_xdg_toplevel);
zxdg_toplevel_v6_add_listener(host_xdg_toplevel->proxy,
&xwl_xdg_toplevel_listener, host_xdg_toplevel);
}
static void xwl_xdg_surface_get_popup(struct wl_client *client,
struct wl_resource *resource, uint32_t id,
struct wl_resource *parent_resource,
struct wl_resource *positioner_resource) {
struct xwl_host_xdg_surface *host = wl_resource_get_user_data(resource);
struct xwl_host_xdg_surface *host_parent =
wl_resource_get_user_data(parent_resource);
struct xwl_host_xdg_positioner *host_positioner =
wl_resource_get_user_data(positioner_resource);
struct xwl_host_xdg_popup *host_xdg_popup;
host_xdg_popup = malloc(sizeof(*host_xdg_popup));
assert(host_xdg_popup);
host_xdg_popup->xwl = host->xwl;
host_xdg_popup->resource =
wl_resource_create(client, &zxdg_popup_v6_interface, 1, id);
wl_resource_set_implementation(host_xdg_popup->resource,
&xwl_xdg_popup_implementation, host_xdg_popup,
xwl_destroy_host_xdg_popup);
host_xdg_popup->proxy = zxdg_surface_v6_get_popup(
host->proxy, host_parent->proxy, host_positioner->proxy);
zxdg_popup_v6_set_user_data(host_xdg_popup->proxy, host_xdg_popup);
zxdg_popup_v6_add_listener(host_xdg_popup->proxy, &xwl_xdg_popup_listener,
host_xdg_popup);
}
static void xwl_xdg_surface_set_window_geometry(struct wl_client *client,
struct wl_resource *resource,
int32_t x, int32_t y,
int32_t width, int32_t height) {
struct xwl_host_xdg_surface *host = wl_resource_get_user_data(resource);
double scale = host->xwl->scale;
int32_t x1, y1, x2, y2;
x1 = x / scale;
y1 = y / scale;
x2 = (x + width) / scale;
y2 = (y + height) / scale;
zxdg_surface_v6_set_window_geometry(host->proxy, x1, y1, x2 - x1, y2 - y1);
}
static void xwl_xdg_surface_ack_configure(struct wl_client *client,
struct wl_resource *resource,
uint32_t serial) {
struct xwl_host_xdg_surface *host = wl_resource_get_user_data(resource);
zxdg_surface_v6_ack_configure(host->proxy, serial);
}
static const struct zxdg_surface_v6_interface xwl_xdg_surface_implementation = {
xwl_xdg_surface_destroy, xwl_xdg_surface_get_toplevel,
xwl_xdg_surface_get_popup, xwl_xdg_surface_set_window_geometry,
xwl_xdg_surface_ack_configure};
static void xwl_xdg_surface_configure(void *data,
struct zxdg_surface_v6 *xdg_surface,
uint32_t serial) {
struct xwl_host_xdg_surface *host =
zxdg_surface_v6_get_user_data(xdg_surface);
zxdg_surface_v6_send_configure(host->resource, serial);
}
static const struct zxdg_surface_v6_listener xwl_xdg_surface_listener = {
xwl_xdg_surface_configure};
static void xwl_destroy_host_xdg_surface(struct wl_resource *resource) {
struct xwl_host_xdg_surface *host = wl_resource_get_user_data(resource);
zxdg_surface_v6_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_xdg_shell_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void xwl_xdg_shell_create_positioner(struct wl_client *client,
struct wl_resource *resource,
uint32_t id) {
struct xwl_host_xdg_shell *host = wl_resource_get_user_data(resource);
struct xwl_host_xdg_positioner *host_xdg_positioner;
host_xdg_positioner = malloc(sizeof(*host_xdg_positioner));
assert(host_xdg_positioner);
host_xdg_positioner->xwl = host->xdg_shell->xwl;
host_xdg_positioner->resource =
wl_resource_create(client, &zxdg_positioner_v6_interface, 1, id);
wl_resource_set_implementation(
host_xdg_positioner->resource, &xwl_xdg_positioner_implementation,
host_xdg_positioner, xwl_destroy_host_xdg_positioner);
host_xdg_positioner->proxy = zxdg_shell_v6_create_positioner(host->proxy);
zxdg_positioner_v6_set_user_data(host_xdg_positioner->proxy,
host_xdg_positioner);
}
static void
xwl_xdg_shell_get_xdg_surface(struct wl_client *client,
struct wl_resource *resource, uint32_t id,
struct wl_resource *surface_resource) {
struct xwl_host_xdg_shell *host = wl_resource_get_user_data(resource);
struct xwl_host_surface *host_surface =
wl_resource_get_user_data(surface_resource);
struct xwl_host_xdg_surface *host_xdg_surface;
host_xdg_surface = malloc(sizeof(*host_xdg_surface));
assert(host_xdg_surface);
host_xdg_surface->xwl = host->xdg_shell->xwl;
host_xdg_surface->resource =
wl_resource_create(client, &zxdg_surface_v6_interface, 1, id);
wl_resource_set_implementation(
host_xdg_surface->resource, &xwl_xdg_surface_implementation,
host_xdg_surface, xwl_destroy_host_xdg_surface);
host_xdg_surface->proxy =
zxdg_shell_v6_get_xdg_surface(host->proxy, host_surface->proxy);
zxdg_surface_v6_set_user_data(host_xdg_surface->proxy, host_xdg_surface);
zxdg_surface_v6_add_listener(host_xdg_surface->proxy,
&xwl_xdg_surface_listener, host_xdg_surface);
}
static void xwl_xdg_shell_pong(struct wl_client *client,
struct wl_resource *resource, uint32_t serial) {
struct xwl_host_xdg_shell *host = wl_resource_get_user_data(resource);
zxdg_shell_v6_pong(host->proxy, serial);
}
static const struct zxdg_shell_v6_interface xwl_xdg_shell_implementation = {
xwl_xdg_shell_destroy, xwl_xdg_shell_create_positioner,
xwl_xdg_shell_get_xdg_surface, xwl_xdg_shell_pong};
static void xwl_xdg_shell_ping(void *data, struct zxdg_shell_v6 *xdg_shell,
uint32_t serial) {
struct xwl_host_xdg_shell *host = zxdg_shell_v6_get_user_data(xdg_shell);
zxdg_shell_v6_send_ping(host->resource, serial);
}
static const struct zxdg_shell_v6_listener xwl_xdg_shell_listener = {
xwl_xdg_shell_ping};
static void xwl_destroy_host_xdg_shell(struct wl_resource *resource) {
struct xwl_host_xdg_shell *host = wl_resource_get_user_data(resource);
zxdg_shell_v6_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_bind_host_xdg_shell(struct wl_client *client, void *data,
uint32_t version, uint32_t id) {
struct xwl_xdg_shell *xdg_shell = (struct xwl_xdg_shell *)data;
struct xwl_host_xdg_shell *host;
host = malloc(sizeof(*host));
assert(host);
host->xdg_shell = xdg_shell;
host->resource = wl_resource_create(client, &zxdg_shell_v6_interface, 1, id);
wl_resource_set_implementation(host->resource, &xwl_xdg_shell_implementation,
host, xwl_destroy_host_xdg_shell);
host->proxy =
wl_registry_bind(wl_display_get_registry(xdg_shell->xwl->display),
xdg_shell->id, &zxdg_shell_v6_interface, 1);
zxdg_shell_v6_set_user_data(host->proxy, host);
zxdg_shell_v6_add_listener(host->proxy, &xwl_xdg_shell_listener, host);
}
static void xwl_data_offer_accept(struct wl_client *client,
struct wl_resource *resource, uint32_t serial,
const char *mime_type) {
struct xwl_host_data_offer *host = wl_resource_get_user_data(resource);
wl_data_offer_accept(host->proxy, serial, mime_type);
}
static void xwl_data_offer_receive(struct wl_client *client,
struct wl_resource *resource,
const char *mime_type, int32_t fd) {
struct xwl_host_data_offer *host = wl_resource_get_user_data(resource);
wl_data_offer_receive(host->proxy, mime_type, fd);
close(fd);
}
static void xwl_data_offer_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void xwl_data_offer_finish(struct wl_client *client,
struct wl_resource *resource) {
struct xwl_host_data_offer *host = wl_resource_get_user_data(resource);
wl_data_offer_finish(host->proxy);
}
static void xwl_data_offer_set_actions(struct wl_client *client,
struct wl_resource *resource,
uint32_t dnd_actions,
uint32_t preferred_action) {
struct xwl_host_data_offer *host = wl_resource_get_user_data(resource);
wl_data_offer_set_actions(host->proxy, dnd_actions, preferred_action);
}
static const struct wl_data_offer_interface xwl_data_offer_implementation = {
xwl_data_offer_accept, xwl_data_offer_receive, xwl_data_offer_destroy,
xwl_data_offer_finish, xwl_data_offer_set_actions};
static void xwl_data_offer_offer(void *data, struct wl_data_offer *data_offer,
const char *mime_type) {
struct xwl_host_data_offer *host = wl_data_offer_get_user_data(data_offer);
wl_data_offer_send_offer(host->resource, mime_type);
}
static void xwl_data_offer_source_actions(void *data,
struct wl_data_offer *data_offer,
uint32_t source_actions) {
struct xwl_host_data_offer *host = wl_data_offer_get_user_data(data_offer);
wl_data_offer_send_source_actions(host->resource, source_actions);
}
static void xwl_data_offer_action(void *data, struct wl_data_offer *data_offer,
uint32_t dnd_action) {
struct xwl_host_data_offer *host = wl_data_offer_get_user_data(data_offer);
wl_data_offer_send_action(host->resource, dnd_action);
}
static const struct wl_data_offer_listener xwl_data_offer_listener = {
xwl_data_offer_offer, xwl_data_offer_source_actions, xwl_data_offer_action};
static void xwl_destroy_host_data_offer(struct wl_resource *resource) {
struct xwl_host_data_offer *host = wl_resource_get_user_data(resource);
wl_data_offer_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_data_source_offer(struct wl_client *client,
struct wl_resource *resource,
const char *mime_type) {
struct xwl_host_data_source *host = wl_resource_get_user_data(resource);
wl_data_source_offer(host->proxy, mime_type);
}
static void xwl_data_source_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void xwl_data_source_set_actions(struct wl_client *client,
struct wl_resource *resource,
uint32_t dnd_actions) {
struct xwl_host_data_source *host = wl_resource_get_user_data(resource);
wl_data_source_set_actions(host->proxy, dnd_actions);
}
static const struct wl_data_source_interface xwl_data_source_implementation = {
xwl_data_source_offer, xwl_data_source_destroy,
xwl_data_source_set_actions};
static void xwl_destroy_host_data_source(struct wl_resource *resource) {
struct xwl_host_data_source *host = wl_resource_get_user_data(resource);
wl_data_source_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_data_device_start_drag(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *source_resource,
struct wl_resource *origin_resource,
struct wl_resource *icon_resource,
uint32_t serial) {
struct xwl_host_data_device *host = wl_resource_get_user_data(resource);
struct xwl_host_data_source *host_source =
source_resource ? wl_resource_get_user_data(source_resource) : NULL;
struct xwl_host_surface *host_origin =
origin_resource ? wl_resource_get_user_data(origin_resource) : NULL;
struct xwl_host_surface *host_icon =
icon_resource ? wl_resource_get_user_data(icon_resource) : NULL;
wl_data_device_start_drag(host->proxy,
host_source ? host_source->proxy : NULL,
host_origin ? host_origin->proxy : NULL,
host_icon ? host_icon->proxy : NULL, serial);
}
static void xwl_data_device_set_selection(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *source_resource,
uint32_t serial) {
struct xwl_host_data_device *host = wl_resource_get_user_data(resource);
struct xwl_host_data_source *host_source =
source_resource ? wl_resource_get_user_data(source_resource) : NULL;
wl_data_device_set_selection(host->proxy,
host_source ? host_source->proxy : NULL, serial);
}
static void xwl_data_device_release(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static const struct wl_data_device_interface xwl_data_device_implementation = {
xwl_data_device_start_drag, xwl_data_device_set_selection,
xwl_data_device_release};
static void xwl_data_device_data_offer(void *data,
struct wl_data_device *data_device,
struct wl_data_offer *data_offer) {
struct xwl_host_data_device *host = wl_data_device_get_user_data(data_device);
struct xwl_host_data_offer *host_data_offer;
host_data_offer = malloc(sizeof(*host_data_offer));
assert(host_data_offer);
host_data_offer->resource = wl_resource_create(
wl_resource_get_client(host->resource), &wl_data_offer_interface,
wl_resource_get_version(host->resource), 0);
wl_resource_set_implementation(host_data_offer->resource,
&xwl_data_offer_implementation,
host_data_offer, xwl_destroy_host_data_offer);
host_data_offer->proxy = data_offer;
wl_data_offer_set_user_data(host_data_offer->proxy, host_data_offer);
wl_data_offer_add_listener(host_data_offer->proxy, &xwl_data_offer_listener,
host_data_offer);
wl_data_device_send_data_offer(host->resource, host_data_offer->resource);
}
static void xwl_data_device_enter(void *data,
struct wl_data_device *data_device,
uint32_t serial, struct wl_surface *surface,
wl_fixed_t x, wl_fixed_t y,
struct wl_data_offer *data_offer) {
struct xwl_host_data_device *host = wl_data_device_get_user_data(data_device);
struct xwl_host_surface *host_surface = wl_surface_get_user_data(surface);
struct xwl_host_data_offer *host_data_offer =
wl_data_offer_get_user_data(data_offer);
double scale = host->xwl->scale;
wl_data_device_send_enter(host->resource, serial, host_surface->resource,
wl_fixed_from_double(wl_fixed_to_double(x) * scale),
wl_fixed_from_double(wl_fixed_to_double(y) * scale),
host_data_offer->resource);
}
static void xwl_data_device_leave(void *data,
struct wl_data_device *data_device) {
struct xwl_host_data_device *host = wl_data_device_get_user_data(data_device);
wl_data_device_send_leave(host->resource);
}
static void xwl_data_device_motion(void *data,
struct wl_data_device *data_device,
uint32_t time, wl_fixed_t x, wl_fixed_t y) {
struct xwl_host_data_device *host = wl_data_device_get_user_data(data_device);
double scale = host->xwl->scale;
wl_data_device_send_motion(
host->resource, time, wl_fixed_from_double(wl_fixed_to_double(x) * scale),
wl_fixed_from_double(wl_fixed_to_double(y) * scale));
}
static void xwl_data_device_drop(void *data,
struct wl_data_device *data_device) {
struct xwl_host_data_device *host = wl_data_device_get_user_data(data_device);
wl_data_device_send_drop(host->resource);
}
static void xwl_data_device_selection(void *data,
struct wl_data_device *data_device,
struct wl_data_offer *data_offer) {
struct xwl_host_data_device *host = wl_data_device_get_user_data(data_device);
struct xwl_host_data_offer *host_data_offer =
wl_data_offer_get_user_data(data_offer);
wl_data_device_send_selection(host->resource, host_data_offer->resource);
}
static const struct wl_data_device_listener xwl_data_device_listener = {
xwl_data_device_data_offer, xwl_data_device_enter,
xwl_data_device_leave, xwl_data_device_motion,
xwl_data_device_drop, xwl_data_device_selection};
static void xwl_destroy_host_data_device(struct wl_resource *resource) {
struct xwl_host_data_device *host = wl_resource_get_user_data(resource);
if (wl_data_device_get_version(host->proxy) >=
WL_DATA_DEVICE_RELEASE_SINCE_VERSION) {
wl_data_device_release(host->proxy);
} else {
wl_data_device_destroy(host->proxy);
}
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_data_device_manager_create_data_source(
struct wl_client *client, struct wl_resource *resource, uint32_t id) {
struct xwl_host_data_device_manager *host =
wl_resource_get_user_data(resource);
struct xwl_host_data_source *host_data_source;
host_data_source = malloc(sizeof(*host_data_source));
assert(host_data_source);
host_data_source->resource = wl_resource_create(
client, &wl_data_source_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(
host_data_source->resource, &xwl_data_source_implementation,
host_data_source, xwl_destroy_host_data_source);
host_data_source->proxy =
wl_data_device_manager_create_data_source(host->proxy);
wl_data_source_set_user_data(host_data_source->proxy, host_data_source);
}
static void xwl_data_device_manager_get_data_device(
struct wl_client *client, struct wl_resource *resource, uint32_t id,
struct wl_resource *seat_resource) {
struct xwl_host_data_device_manager *host =
wl_resource_get_user_data(resource);
struct xwl_host_seat *host_seat = wl_resource_get_user_data(seat_resource);
struct xwl_host_data_device *host_data_device;
host_data_device = malloc(sizeof(*host_data_device));
assert(host_data_device);
host_data_device->xwl = host->xwl;
host_data_device->resource = wl_resource_create(
client, &wl_data_device_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(
host_data_device->resource, &xwl_data_device_implementation,
host_data_device, xwl_destroy_host_data_device);
host_data_device->proxy =
wl_data_device_manager_get_data_device(host->proxy, host_seat->proxy);
wl_data_device_set_user_data(host_data_device->proxy, host_data_device);
wl_data_device_add_listener(host_data_device->proxy,
&xwl_data_device_listener, host_data_device);
}
static const struct wl_data_device_manager_interface
xwl_data_device_manager_implementation = {
xwl_data_device_manager_create_data_source,
xwl_data_device_manager_get_data_device};
static void xwl_destroy_host_data_device_manager(struct wl_resource *resource) {
struct xwl_host_data_device_manager *host =
wl_resource_get_user_data(resource);
wl_data_device_manager_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_bind_host_data_device_manager(struct wl_client *client,
void *data, uint32_t version,
uint32_t id) {
struct xwl_data_device_manager *data_device_manager =
(struct xwl_data_device_manager *)data;
struct xwl_host_data_device_manager *host;
host = malloc(sizeof(*host));
assert(host);
host->xwl = data_device_manager->xwl;
host->resource =
wl_resource_create(client, &wl_data_device_manager_interface,
MIN(version, data_device_manager->version), id);
wl_resource_set_implementation(host->resource,
&xwl_data_device_manager_implementation, host,
xwl_destroy_host_data_device_manager);
host->proxy = wl_registry_bind(
wl_display_get_registry(data_device_manager->xwl->display),
data_device_manager->id, &wl_data_device_manager_interface,
data_device_manager->version);
wl_data_device_manager_set_user_data(host->proxy, host);
}
static void xwl_subsurface_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void xwl_subsurface_set_position(struct wl_client *client,
struct wl_resource *resource, int32_t x,
int32_t y) {
struct xwl_host_subsurface *host = wl_resource_get_user_data(resource);
double scale = host->xwl->scale;
wl_subsurface_set_position(host->proxy, x / scale, y / scale);
}
static void xwl_subsurface_place_above(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *sibling_resource) {
struct xwl_host_subsurface *host = wl_resource_get_user_data(resource);
struct xwl_host_surface *host_sibling =
wl_resource_get_user_data(sibling_resource);
wl_subsurface_place_above(host->proxy, host_sibling->proxy);
}
static void xwl_subsurface_place_below(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *sibling_resource) {
struct xwl_host_subsurface *host = wl_resource_get_user_data(resource);
struct xwl_host_surface *host_sibling =
wl_resource_get_user_data(sibling_resource);
wl_subsurface_place_below(host->proxy, host_sibling->proxy);
}
static void xwl_subsurface_set_sync(struct wl_client *client,
struct wl_resource *resource) {
struct xwl_host_subsurface *host = wl_resource_get_user_data(resource);
wl_subsurface_set_sync(host->proxy);
}
static void xwl_subsurface_set_desync(struct wl_client *client,
struct wl_resource *resource) {
struct xwl_host_subsurface *host = wl_resource_get_user_data(resource);
wl_subsurface_set_desync(host->proxy);
}
static const struct wl_subsurface_interface xwl_subsurface_implementation = {
xwl_subsurface_destroy, xwl_subsurface_set_position,
xwl_subsurface_place_above, xwl_subsurface_place_below,
xwl_subsurface_set_sync, xwl_subsurface_set_desync};
static void xwl_destroy_host_subsurface(struct wl_resource *resource) {
struct xwl_host_subsurface *host = wl_resource_get_user_data(resource);
wl_subsurface_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_subcompositor_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void xwl_subcompositor_get_subsurface(
struct wl_client *client, struct wl_resource *resource, uint32_t id,
struct wl_resource *surface_resource, struct wl_resource *parent_resource) {
struct xwl_host_subcompositor *host = wl_resource_get_user_data(resource);
struct xwl_host_surface *host_surface =
wl_resource_get_user_data(surface_resource);
struct xwl_host_surface *host_parent =
wl_resource_get_user_data(parent_resource);
struct xwl_host_subsurface *host_subsurface;
host_subsurface = malloc(sizeof(*host_subsurface));
assert(host_subsurface);
host_subsurface->xwl = host->xwl;
host_subsurface->resource =
wl_resource_create(client, &wl_subsurface_interface, 1, id);
wl_resource_set_implementation(host_subsurface->resource,
&xwl_subsurface_implementation,
host_subsurface, xwl_destroy_host_subsurface);
host_subsurface->proxy = wl_subcompositor_get_subsurface(
host->proxy, host_surface->proxy, host_parent->proxy);
wl_subsurface_set_user_data(host_subsurface->proxy, host_subsurface);
}
static const struct wl_subcompositor_interface
xwl_subcompositor_implementation = {xwl_subcompositor_destroy,
xwl_subcompositor_get_subsurface};
static void xwl_destroy_host_subcompositor(struct wl_resource *resource) {
struct xwl_host_subcompositor *host = wl_resource_get_user_data(resource);
wl_subcompositor_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void xwl_bind_host_subcompositor(struct wl_client *client, void *data,
uint32_t version, uint32_t id) {
struct xwl_subcompositor *subcompositor = (struct xwl_subcompositor *)data;
struct xwl_host_subcompositor *host;
host = malloc(sizeof(*host));
assert(host);
host->xwl = subcompositor->xwl;
host->resource =
wl_resource_create(client, &wl_subcompositor_interface, 1, id);
wl_resource_set_implementation(host->resource,
&xwl_subcompositor_implementation, host,
xwl_destroy_host_subcompositor);
host->proxy =
wl_registry_bind(wl_display_get_registry(subcompositor->xwl->display),
subcompositor->id, &wl_subcompositor_interface, 1);
wl_subcompositor_set_user_data(host->proxy, host);
}
static struct xwl_global *
xwl_global_create(struct xwl *xwl, const struct wl_interface *interface,
int version, void *data, wl_global_bind_func_t bind) {
struct xwl_host_registry *registry;
struct xwl_global *global;
assert(version > 0);
assert(version <= interface->version);
global = malloc(sizeof *global);
assert(global);
global->xwl = xwl;
global->name = xwl->next_global_id++;
global->interface = interface;
global->version = version;
global->data = data;
global->bind = bind;
wl_list_insert(xwl->globals.prev, &global->link);
wl_list_for_each(registry, &xwl->registries, link) {
wl_resource_post_event(registry->resource, WL_REGISTRY_GLOBAL, global->name,
global->interface->name, global->version);
}
return global;
}
static void xwl_global_destroy(struct xwl_global *global) {
struct xwl_host_registry *registry;
wl_list_for_each(registry, &global->xwl->registries, link)
wl_resource_post_event(registry->resource, WL_REGISTRY_GLOBAL_REMOVE,
global->name);
wl_list_remove(&global->link);
free(global);
}
static void xwl_registry_handler(void *data, struct wl_registry *registry,
uint32_t id, const char *interface,
uint32_t version) {
struct xwl *xwl = (struct xwl *)data;
if (strcmp(interface, "wl_compositor") == 0) {
struct xwl_compositor *compositor = malloc(sizeof(struct xwl_compositor));
assert(compositor);
compositor->xwl = xwl;
compositor->id = id;
assert(version >= 3);
compositor->version = 3;
compositor->host_global =
xwl_global_create(xwl, &wl_compositor_interface, compositor->version,
compositor, xwl_bind_host_compositor);
compositor->internal = wl_registry_bind(
registry, id, &wl_compositor_interface, compositor->version);
assert(!xwl->compositor);
xwl->compositor = compositor;
} else if (strcmp(interface, "wl_subcompositor") == 0) {
struct xwl_subcompositor *subcompositor =
malloc(sizeof(struct xwl_subcompositor));
assert(subcompositor);
subcompositor->xwl = xwl;
subcompositor->id = id;
subcompositor->host_global =
xwl_global_create(xwl, &wl_subcompositor_interface, 1, subcompositor,
xwl_bind_host_subcompositor);
xwl->subcompositor = subcompositor;
} else if (strcmp(interface, "wl_shm") == 0) {
struct xwl_shm *shm = malloc(sizeof(struct xwl_shm));
assert(shm);
shm->xwl = xwl;
shm->id = id;
shm->host_global =
xwl_global_create(xwl, &wl_shm_interface, 1, shm, xwl_bind_host_shm);
assert(!xwl->shm);
xwl->shm = shm;
} else if (strcmp(interface, "wl_shell") == 0) {
struct xwl_shell *shell = malloc(sizeof(struct xwl_shell));
assert(shell);
shell->xwl = xwl;
shell->id = id;
shell->host_global = xwl_global_create(xwl, &wl_shell_interface, 1, shell,
xwl_bind_host_shell);
assert(!xwl->shell);
xwl->shell = shell;
} else if (strcmp(interface, "wl_output") == 0) {
struct xwl_output *output = malloc(sizeof(struct xwl_output));
assert(output);
output->xwl = xwl;
output->id = id;
output->version = MIN(2, version);
output->host_global =
xwl_global_create(xwl, &wl_output_interface, output->version, output,
xwl_bind_host_output);
wl_list_insert(&xwl->outputs, &output->link);
} else if (strcmp(interface, "wl_seat") == 0) {
struct xwl_seat *seat = malloc(sizeof(struct xwl_seat));
assert(seat);
seat->xwl = xwl;
seat->id = id;
seat->version = MIN(5, version);
seat->host_global = xwl_global_create(
xwl, &wl_seat_interface, seat->version, seat, xwl_bind_host_seat);
seat->last_serial = 0;
wl_list_insert(&xwl->seats, &seat->link);
} else if (strcmp(interface, "wl_data_device_manager") == 0) {
struct xwl_data_device_manager *data_device_manager =
malloc(sizeof(struct xwl_data_device_manager));
assert(data_device_manager);
data_device_manager->xwl = xwl;
data_device_manager->id = id;
data_device_manager->version = MIN(3, version);
if (xwl->xwayland) {
data_device_manager->host_global = NULL;
data_device_manager->internal =
wl_registry_bind(registry, id, &wl_data_device_manager_interface,
data_device_manager->version);
} else {
data_device_manager->internal = NULL;
data_device_manager->host_global = xwl_global_create(
xwl, &wl_data_device_manager_interface, data_device_manager->version,
data_device_manager, xwl_bind_host_data_device_manager);
}
xwl->data_device_manager = data_device_manager;
} else if (strcmp(interface, "zxdg_shell_v6") == 0) {
struct xwl_xdg_shell *xdg_shell = malloc(sizeof(struct xwl_xdg_shell));
assert(xdg_shell);
xdg_shell->xwl = xwl;
xdg_shell->id = id;
if (xwl->xwayland) {
xdg_shell->host_global = NULL;
xdg_shell->internal =
wl_registry_bind(registry, id, &zxdg_shell_v6_interface, 1);
zxdg_shell_v6_add_listener(xdg_shell->internal,
&xwl_internal_xdg_shell_listener, NULL);
} else {
xdg_shell->internal = NULL;
xdg_shell->host_global = xwl_global_create(
xwl, &zxdg_shell_v6_interface, 1, xdg_shell, xwl_bind_host_xdg_shell);
}
assert(!xwl->xdg_shell);
xwl->xdg_shell = xdg_shell;
} else if (strcmp(interface, "zaura_shell") == 0) {
struct xwl_aura_shell *aura_shell = malloc(sizeof(struct xwl_aura_shell));
assert(aura_shell);
aura_shell->xwl = xwl;
aura_shell->id = id;
aura_shell->version = MIN(3, version);
aura_shell->internal = wl_registry_bind(
registry, id, &zaura_shell_interface, aura_shell->version);
assert(!xwl->aura_shell);
xwl->aura_shell = aura_shell;
} else if (strcmp(interface, "wp_viewporter") == 0) {
struct xwl_viewporter *viewporter = malloc(sizeof(struct xwl_viewporter));
assert(viewporter);
viewporter->xwl = xwl;
viewporter->id = id;
viewporter->internal =
wl_registry_bind(registry, id, &wp_viewporter_interface, 1);
assert(!xwl->viewporter);
xwl->viewporter = viewporter;
} else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) {
struct xwl_linux_dmabuf *linux_dmabuf =
malloc(sizeof(struct xwl_linux_dmabuf));
assert(linux_dmabuf);
linux_dmabuf->xwl = xwl;
linux_dmabuf->id = id;
linux_dmabuf->version = MIN(2, version);
linux_dmabuf->host_drm_global = NULL;
linux_dmabuf->internal = wl_registry_bind(
registry, id, &zwp_linux_dmabuf_v1_interface, linux_dmabuf->version);
assert(!xwl->linux_dmabuf);
xwl->linux_dmabuf = linux_dmabuf;
// Register wl_drm global if DRM device is available and DMABuf interface
// version is sufficient.
if (xwl->drm_device && linux_dmabuf->version >= 2) {
linux_dmabuf->host_drm_global = xwl_global_create(
xwl, &wl_drm_interface, 2, linux_dmabuf, xwl_bind_host_drm);
}
}
}
static void xwl_registry_remover(void *data, struct wl_registry *registry,
uint32_t id) {
struct xwl *xwl = (struct xwl *)data;
struct xwl_output *output;
struct xwl_seat *seat;
if (xwl->compositor && xwl->compositor->id == id) {
xwl_global_destroy(xwl->compositor->host_global);
wl_compositor_destroy(xwl->compositor->internal);
free(xwl->compositor);
xwl->compositor = NULL;
return;
}
if (xwl->subcompositor && xwl->subcompositor->id == id) {
xwl_global_destroy(xwl->subcompositor->host_global);
free(xwl->subcompositor);
xwl->subcompositor = NULL;
return;
}
if (xwl->shm && xwl->shm->id == id) {
xwl_global_destroy(xwl->shm->host_global);
free(xwl->shm);
xwl->shm = NULL;
return;
}
if (xwl->shell && xwl->shell->id == id) {
xwl_global_destroy(xwl->shell->host_global);
free(xwl->shell);
xwl->shell = NULL;
return;
}
if (xwl->data_device_manager && xwl->data_device_manager->id == id) {
if (xwl->data_device_manager->host_global)
xwl_global_destroy(xwl->data_device_manager->host_global);
if (xwl->data_device_manager->internal)
wl_data_device_manager_destroy(xwl->data_device_manager->internal);
free(xwl->data_device_manager);
xwl->data_device_manager = NULL;
return;
}
if (xwl->xdg_shell && xwl->xdg_shell->id == id) {
if (xwl->xdg_shell->host_global)
xwl_global_destroy(xwl->xdg_shell->host_global);
if (xwl->xdg_shell->internal)
zxdg_shell_v6_destroy(xwl->xdg_shell->internal);
free(xwl->xdg_shell);
xwl->xdg_shell = NULL;
return;
}
if (xwl->aura_shell && xwl->aura_shell->id == id) {
zaura_shell_destroy(xwl->aura_shell->internal);
free(xwl->aura_shell);
xwl->aura_shell = NULL;
return;
}
if (xwl->viewporter && xwl->viewporter->id == id) {
wp_viewporter_destroy(xwl->viewporter->internal);
free(xwl->viewporter);
xwl->viewporter = NULL;
return;
}
if (xwl->linux_dmabuf && xwl->linux_dmabuf->id == id) {
if (xwl->linux_dmabuf->host_drm_global)
xwl_global_destroy(xwl->linux_dmabuf->host_drm_global);
zwp_linux_dmabuf_v1_destroy(xwl->linux_dmabuf->internal);
free(xwl->linux_dmabuf);
xwl->linux_dmabuf = NULL;
return;
}
wl_list_for_each(output, &xwl->outputs, link) {
if (output->id == id) {
xwl_global_destroy(output->host_global);
wl_list_remove(&output->link);
free(output);
return;
}
}
wl_list_for_each(seat, &xwl->seats, link) {
if (seat->id == id) {
xwl_global_destroy(seat->host_global);
wl_list_remove(&seat->link);
free(seat);
return;
}
}
// Not reached.
assert(0);
}
static const struct wl_registry_listener xwl_registry_listener = {
xwl_registry_handler, xwl_registry_remover};
static int xwl_handle_event(int fd, uint32_t mask, void *data) {
struct xwl *xwl = (struct xwl *)data;
int count = 0;
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR))
return 0;
if (mask & WL_EVENT_READABLE)
count = wl_display_dispatch(xwl->display);
if (mask & WL_EVENT_WRITABLE)
wl_display_flush(xwl->display);
if (mask == 0) {
count = wl_display_dispatch_pending(xwl->display);
wl_display_flush(xwl->display);
}
return count;
}
static void xwl_create_window(struct xwl *xwl, xcb_window_t id, int x, int y,
int width, int height, int border_width) {
struct xwl_window *window = malloc(sizeof(struct xwl_window));
uint32_t values[1];
assert(window);
window->xwl = xwl;
window->id = id;
window->frame_id = XCB_WINDOW_NONE;
window->host_surface_id = 0;
window->unpaired = 1;
window->x = x;
window->y = y;
window->width = width;
window->height = height;
window->border_width = border_width;
window->depth = 0;
window->managed = 0;
window->realized = 0;
window->activated = 0;
window->transient_for = XCB_WINDOW_NONE;
window->decorated = 0;
window->name = NULL;
window->clazz = NULL;
window->size_flags = P_POSITION;
window->xdg_surface = NULL;
window->xdg_toplevel = NULL;
window->xdg_popup = NULL;
window->aura_surface = NULL;
window->next_config.serial = 0;
window->next_config.mask = 0;
window->next_config.states_length = 0;
window->pending_config.serial = 0;
window->pending_config.mask = 0;
window->pending_config.states_length = 0;
wl_list_insert(&xwl->unpaired_windows, &window->link);
values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE;
xcb_change_window_attributes(xwl->connection, window->id, XCB_CW_EVENT_MASK,
values);
}
static void xwl_destroy_window(struct xwl_window *window) {
if (window->frame_id != XCB_WINDOW_NONE)
xcb_destroy_window(window->xwl->connection, window->frame_id);
if (window->xwl->host_focus_window == window) {
window->xwl->host_focus_window = NULL;
window->xwl->needs_set_input_focus = 1;
}
if (window->xdg_popup)
zxdg_popup_v6_destroy(window->xdg_popup);
if (window->xdg_toplevel)
zxdg_toplevel_v6_destroy(window->xdg_toplevel);
if (window->xdg_surface)
zxdg_surface_v6_destroy(window->xdg_surface);
if (window->aura_surface)
zaura_surface_destroy(window->aura_surface);
if (window->name)
free(window->name);
if (window->clazz)
free(window->clazz);
wl_list_remove(&window->link);
free(window);
}
static int xwl_is_window(struct xwl_window *window, xcb_window_t id) {
if (window->id == id)
return 1;
if (window->frame_id != XCB_WINDOW_NONE) {
if (window->frame_id == id)
return 1;
}
return 0;
}
static struct xwl_window *xwl_lookup_window(struct xwl *xwl, xcb_window_t id) {
struct xwl_window *window;
wl_list_for_each(window, &xwl->windows, link) {
if (xwl_is_window(window, id))
return window;
}
wl_list_for_each(window, &xwl->unpaired_windows, link) {
if (xwl_is_window(window, id))
return window;
}
return NULL;
}
static int xwl_is_our_window(struct xwl *xwl, xcb_window_t id) {
const xcb_setup_t *setup = xcb_get_setup(xwl->connection);
return (id & ~setup->resource_id_mask) == setup->resource_id_base;
}
static void xwl_handle_create_notify(struct xwl *xwl,
xcb_create_notify_event_t *event) {
if (xwl_is_our_window(xwl, event->window))
return;
xwl_create_window(xwl, event->window, event->x, event->y, event->width,
event->height, event->border_width);
}
static void xwl_handle_destroy_notify(struct xwl *xwl,
xcb_destroy_notify_event_t *event) {
struct xwl_window *window;
if (xwl_is_our_window(xwl, event->window))
return;
window = xwl_lookup_window(xwl, event->window);
if (!window)
return;
xwl_destroy_window(window);
}
static void xwl_handle_reparent_notify(struct xwl *xwl,
xcb_reparent_notify_event_t *event) {
struct xwl_window *window;
if (event->parent == xwl->screen->root) {
int width = 1;
int height = 1;
int border_width = 0;
xcb_get_geometry_reply_t *geometry_reply = xcb_get_geometry_reply(
xwl->connection, xcb_get_geometry(xwl->connection, event->window),
NULL);
if (geometry_reply) {
width = geometry_reply->width;
height = geometry_reply->height;
border_width = geometry_reply->border_width;
free(geometry_reply);
}
xwl_create_window(xwl, event->window, event->x, event->y, width, height,
border_width);
return;
}
if (xwl_is_our_window(xwl, event->parent))
return;
window = xwl_lookup_window(xwl, event->window);
if (!window)
return;
xwl_destroy_window(window);
}
static void xwl_handle_map_request(struct xwl *xwl,
xcb_map_request_event_t *event) {
struct xwl_window *window = xwl_lookup_window(xwl, event->window);
struct {
int type;
xcb_atom_t atom;
} properties[] = {
{PROPERTY_WM_NAME, XCB_ATOM_WM_NAME},
{PROPERTY_WM_CLASS, XCB_ATOM_WM_CLASS},
{PROPERTY_WM_TRANSIENT_FOR, XCB_ATOM_WM_TRANSIENT_FOR},
{PROPERTY_WM_NORMAL_HINTS, XCB_ATOM_WM_NORMAL_HINTS},
{PROPERTY_MOTIF_WM_HINTS, xwl->atoms[ATOM_MOTIF_WM_HINTS].value},
};
xcb_get_geometry_cookie_t geometry_cookie;
xcb_get_property_cookie_t property_cookies[ARRAY_SIZE(properties)];
struct xwl_wm_size_hints {
uint32_t flags;
int32_t x, y;
int32_t width, height;
int32_t min_width, min_height;
int32_t max_width, max_height;
int32_t width_inc, height_inc;
struct {
int32_t x;
int32_t y;
} min_aspect, max_aspect;
int32_t base_width, base_height;
int32_t win_gravity;
} size_hints = {0};
struct xwl_mwm_hints {
uint32_t flags;
uint32_t functions;
uint32_t decorations;
int32_t input_mode;
uint32_t status;
} mwm_hints = {0};
uint32_t values[5];
int i;
if (!window)
return;
assert(!xwl_is_our_window(xwl, event->window));
window->managed = 1;
if (window->frame_id == XCB_WINDOW_NONE)
geometry_cookie = xcb_get_geometry(xwl->connection, window->id);
for (i = 0; i < ARRAY_SIZE(properties); ++i) {
property_cookies[i] =
xcb_get_property(xwl->connection, 0, window->id, properties[i].atom,
XCB_ATOM_ANY, 0, 2048);
}
if (window->frame_id == XCB_WINDOW_NONE) {
xcb_get_geometry_reply_t *geometry_reply =
xcb_get_geometry_reply(xwl->connection, geometry_cookie, NULL);
if (geometry_reply) {
window->x = geometry_reply->x;
window->y = geometry_reply->y;
window->width = geometry_reply->width;
window->height = geometry_reply->height;
window->depth = geometry_reply->depth;
free(geometry_reply);
}
}
if (window->name) {
free(window->name);
window->name = NULL;
}
if (window->clazz) {
free(window->clazz);
window->clazz = NULL;
}
window->transient_for = XCB_WINDOW_NONE;
window->decorated = 1;
window->size_flags = 0;
for (i = 0; i < ARRAY_SIZE(properties); ++i) {
xcb_get_property_reply_t *reply =
xcb_get_property_reply(xwl->connection, property_cookies[i], NULL);
if (!reply)
continue;
if (reply->type == XCB_ATOM_NONE) {
free(reply);
continue;
}
switch (properties[i].type) {
case PROPERTY_WM_NAME:
window->name = strndup(xcb_get_property_value(reply),
xcb_get_property_value_length(reply));
break;
case PROPERTY_WM_CLASS: {
// WM_CLASS property contains two consecutive null-terminated strings.
// These specify the Instance and Class names. If a global app ID is
// not set then use Class name for app ID.
const char *value = xcb_get_property_value(reply);
int value_length = xcb_get_property_value_length(reply);
int instance_length = strnlen(value, value_length);
if (value_length > instance_length) {
window->clazz = strndup(value + instance_length + 1,
value_length - instance_length - 1);
}
} break;
case PROPERTY_WM_TRANSIENT_FOR:
if (xcb_get_property_value_length(reply) >= 4)
window->transient_for = *((uint32_t *)xcb_get_property_value(reply));
break;
case PROPERTY_WM_NORMAL_HINTS:
if (xcb_get_property_value_length(reply) >= sizeof(size_hints))
memcpy(&size_hints, xcb_get_property_value(reply), sizeof(size_hints));
break;
case PROPERTY_MOTIF_WM_HINTS:
if (xcb_get_property_value_length(reply) >= sizeof(mwm_hints))
memcpy(&mwm_hints, xcb_get_property_value(reply), sizeof(mwm_hints));
break;
default:
break;
}
free(reply);
}
if (mwm_hints.flags & MWM_HINTS_DECORATIONS) {
if (mwm_hints.decorations & MWM_DECOR_ALL)
window->decorated = ~mwm_hints.decorations & MWM_DECOR_TITLE;
else
window->decorated = mwm_hints.decorations & MWM_DECOR_TITLE;
}
// Allow user/program controlled position for transients.
if (window->transient_for)
window->size_flags |= size_hints.flags & (US_POSITION | P_POSITION);
window->border_width = 0;
xwl_adjust_window_size_for_screen_size(window);
if (window->size_flags & (US_POSITION | P_POSITION)) {
// x/y fields are obsolete but some clients still expect them to be
// honored so use them if greater than zero.
if (size_hints.x > 0)
window->x = size_hints.x;
if (size_hints.y > 0)
window->y = size_hints.y;
} else {
xwl_adjust_window_position_for_screen_size(window);
}
values[0] = window->width;
values[1] = window->height;
values[2] = 0;
xcb_configure_window(xwl->connection, window->id,
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
XCB_CONFIG_WINDOW_BORDER_WIDTH,
values);
values[0] = 0;
values[1] = 0;
values[2] = window->decorated ? CAPTION_HEIGHT * xwl->scale : 0;
values[3] = 0;
xcb_change_property(xwl->connection, XCB_PROP_MODE_REPLACE, window->id,
xwl->atoms[ATOM_NET_FRAME_EXTENTS].value,
XCB_ATOM_CARDINAL, 32, 4, values);
if (window->frame_id == XCB_WINDOW_NONE) {
int depth = window->depth ? window->depth : xwl->screen->root_depth;
values[0] = xwl->screen->black_pixel;
values[1] = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
values[2] = xwl->colormaps[depth];
window->frame_id = xcb_generate_id(xwl->connection);
xcb_create_window(
xwl->connection, depth, window->frame_id, xwl->screen->root, window->x,
window->y, window->width, window->height, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, xwl->visual_ids[depth],
XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP, values);
values[0] = XCB_STACK_MODE_BELOW;
xcb_configure_window(xwl->connection, window->frame_id,
XCB_CONFIG_WINDOW_STACK_MODE, values);
xcb_reparent_window(xwl->connection, window->id, window->frame_id, 0, 0);
} else {
values[0] = window->x;
values[1] = window->y;
values[2] = window->width;
values[3] = window->height;
values[4] = XCB_STACK_MODE_BELOW;
xcb_configure_window(
xwl->connection, window->frame_id,
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH |
XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_STACK_MODE,
values);
}
xwl_window_set_wm_state(window, WM_STATE_NORMAL);
xwl_send_configure_notify(window);
xcb_map_window(xwl->connection, window->id);
xcb_map_window(xwl->connection, window->frame_id);
}
static void xwl_handle_map_notify(struct xwl *xwl,
xcb_map_notify_event_t *event) {}
static void xwl_handle_unmap_notify(struct xwl *xwl,
xcb_unmap_notify_event_t *event) {
struct xwl_window *window;
if (xwl_is_our_window(xwl, event->window))
return;
if (event->response_type & SEND_EVENT_MASK)
return;
window = xwl_lookup_window(xwl, event->window);
if (!window)
return;
if (xwl->host_focus_window == window) {
xwl->host_focus_window = NULL;
xwl->needs_set_input_focus = 1;
}
if (window->host_surface_id) {
window->host_surface_id = 0;
xwl_window_update(window);
}
xwl_window_set_wm_state(window, WM_STATE_WITHDRAWN);
if (window->frame_id != XCB_WINDOW_NONE)
xcb_unmap_window(xwl->connection, window->frame_id);
}
static void xwl_handle_configure_request(struct xwl *xwl,
xcb_configure_request_event_t *event) {
struct xwl_window *window = xwl_lookup_window(xwl, event->window);
int width = window->width;
int height = window->height;
uint32_t values[7];
assert(!xwl_is_our_window(xwl, event->window));
if (!window->managed) {
int i = 0;
if (event->value_mask & XCB_CONFIG_WINDOW_X)
values[i++] = event->x;
if (event->value_mask & XCB_CONFIG_WINDOW_Y)
values[i++] = event->y;
if (event->value_mask & XCB_CONFIG_WINDOW_WIDTH)
values[i++] = event->width;
if (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
values[i++] = event->height;
if (event->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
values[i++] = event->border_width;
if (event->value_mask & XCB_CONFIG_WINDOW_SIBLING)
values[i++] = event->sibling;
if (event->value_mask & XCB_CONFIG_WINDOW_STACK_MODE)
values[i++] = event->stack_mode;
xcb_configure_window(xwl->connection, window->id, event->value_mask,
values);
return;
}
// Ack configure events as satisfying request removes the guarantee
// that matching contents will arrive.
if (window->xdg_toplevel) {
if (window->pending_config.serial) {
zxdg_surface_v6_ack_configure(window->xdg_surface,
window->pending_config.serial);
window->pending_config.serial = 0;
window->pending_config.mask = 0;
window->pending_config.states_length = 0;
}
if (window->next_config.serial) {
zxdg_surface_v6_ack_configure(window->xdg_surface,
window->next_config.serial);
window->next_config.serial = 0;
window->next_config.mask = 0;
window->next_config.states_length = 0;
}
}
if (event->value_mask & XCB_CONFIG_WINDOW_X)
window->x = event->x;
if (event->value_mask & XCB_CONFIG_WINDOW_Y)
window->y = event->y;
if (event->value_mask & XCB_CONFIG_WINDOW_WIDTH)
window->width = event->width;
if (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
window->height = event->height;
xwl_adjust_window_size_for_screen_size(window);
if (window->size_flags & (US_POSITION | P_POSITION))
xwl_window_update(window);
else
xwl_adjust_window_position_for_screen_size(window);
values[0] = window->x;
values[1] = window->y;
values[2] = window->width;
values[3] = window->height;
values[4] = 0;
xcb_configure_window(xwl->connection, window->frame_id,
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
values);
// We need to send a synthetic configure notify if:
// - Not changing the size, location, border width.
// - Moving the window without resizing it or changing its border width.
if (width != window->width || height != window->height ||
window->border_width) {
xcb_configure_window(xwl->connection, window->id,
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
XCB_CONFIG_WINDOW_BORDER_WIDTH,
&values[2]);
window->border_width = 0;
} else {
xwl_send_configure_notify(window);
}
}
static void xwl_handle_configure_notify(struct xwl *xwl,
xcb_configure_notify_event_t *event) {
struct xwl_window *window;
if (xwl_is_our_window(xwl, event->window))
return;
if (event->window == xwl->screen->root) {
xcb_get_geometry_reply_t *geometry_reply = xcb_get_geometry_reply(
xwl->connection, xcb_get_geometry(xwl->connection, event->window),
NULL);
int width = xwl->screen->width_in_pixels;
int height = xwl->screen->height_in_pixels;
if (geometry_reply) {
width = geometry_reply->width;
height = geometry_reply->height;
free(geometry_reply);
}
if (width == xwl->screen->width_in_pixels ||
height == xwl->screen->height_in_pixels) {
return;
}
xwl->screen->width_in_pixels = width;
xwl->screen->height_in_pixels = height;
// Re-center managed windows.
wl_list_for_each(window, &xwl->windows, link) {
int x, y;
if (window->size_flags & (US_POSITION | P_POSITION))
continue;
x = window->x;
y = window->y;
xwl_adjust_window_position_for_screen_size(window);
if (window->x != x || window->y != y) {
uint32_t values[2];
values[0] = window->x;
values[1] = window->y;
xcb_configure_window(xwl->connection, window->frame_id,
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values);
xwl_send_configure_notify(window);
}
}
return;
}
window = xwl_lookup_window(xwl, event->window);
if (!window)
return;
if (window->managed)
return;
window->width = event->width;
window->height = event->height;
window->border_width = event->border_width;
if (event->x != window->x || event->y != window->y) {
window->x = event->x;
window->y = event->y;
xwl_window_update(window);
}
}
static uint32_t xwl_resize_edge(int net_wm_moveresize_size) {
switch (net_wm_moveresize_size) {
case NET_WM_MOVERESIZE_SIZE_TOPLEFT:
return ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT;
case NET_WM_MOVERESIZE_SIZE_TOP:
return ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP;
case NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
return ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT;
case NET_WM_MOVERESIZE_SIZE_RIGHT:
return ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT;
case NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
return ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT;
case NET_WM_MOVERESIZE_SIZE_BOTTOM:
return ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM;
case NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
return ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT;
case NET_WM_MOVERESIZE_SIZE_LEFT:
return ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT;
default:
return ZXDG_TOPLEVEL_V6_RESIZE_EDGE_NONE;
}
}
static void xwl_handle_client_message(struct xwl *xwl,
xcb_client_message_event_t *event) {
if (event->type == xwl->atoms[ATOM_WL_SURFACE_ID].value) {
struct xwl_window *window, *unpaired_window = NULL;
wl_list_for_each(window, &xwl->unpaired_windows, link) {
if (xwl_is_window(window, event->window)) {
unpaired_window = window;
break;
}
}
if (unpaired_window) {
unpaired_window->host_surface_id = event->data.data32[0];
xwl_window_update(unpaired_window);
}
} else if (event->type == xwl->atoms[ATOM_NET_WM_MOVERESIZE].value) {
struct xwl_window *window = xwl_lookup_window(xwl, event->window);
if (window && window->xdg_toplevel) {
struct xwl_host_seat *seat = window->xwl->default_seat;
if (!seat)
return;
if (event->data.data32[2] == NET_WM_MOVERESIZE_MOVE) {
zxdg_toplevel_v6_move(window->xdg_toplevel, seat->proxy,
seat->seat->last_serial);
} else {
uint32_t edge = xwl_resize_edge(event->data.data32[2]);
if (edge == ZXDG_TOPLEVEL_V6_RESIZE_EDGE_NONE)
return;
zxdg_toplevel_v6_resize(window->xdg_toplevel, seat->proxy,
seat->seat->last_serial, edge);
}
}
} else if (event->type == xwl->atoms[ATOM_NET_WM_STATE].value) {
struct xwl_window *window = xwl_lookup_window(xwl, event->window);
if (window && window->xdg_toplevel) {
int changed[ATOM_LAST + 1];
uint32_t action = event->data.data32[0];
int i;
for (i = 0; i < ARRAY_SIZE(xwl->atoms); ++i) {
changed[i] = event->data.data32[1] == xwl->atoms[i].value ||
event->data.data32[2] == xwl->atoms[i].value;
}
if (changed[ATOM_NET_WM_STATE_FULLSCREEN]) {
if (action == NET_WM_STATE_ADD)
zxdg_toplevel_v6_set_fullscreen(window->xdg_toplevel, NULL);
else if (action == NET_WM_STATE_REMOVE)
zxdg_toplevel_v6_unset_fullscreen(window->xdg_toplevel);
}
if (changed[ATOM_NET_WM_STATE_MAXIMIZED_VERT] &&
changed[ATOM_NET_WM_STATE_MAXIMIZED_HORZ]) {
if (action == NET_WM_STATE_ADD)
zxdg_toplevel_v6_set_maximized(window->xdg_toplevel);
else if (action == NET_WM_STATE_REMOVE)
zxdg_toplevel_v6_unset_maximized(window->xdg_toplevel);
}
}
}
}
static void xwl_handle_focus_in(struct xwl *xwl, xcb_focus_in_event_t *event) {}
static void xwl_handle_focus_out(struct xwl *xwl,
xcb_focus_out_event_t *event) {}
static int xwl_handle_selection_fd_writable(int fd, uint32_t mask, void *data) {
struct xwl *xwl = data;
uint8_t *value;
int bytes, bytes_left;
value = xcb_get_property_value(xwl->selection_property_reply);
bytes_left = xcb_get_property_value_length(xwl->selection_property_reply) -
xwl->selection_property_offset;
bytes = write(fd, value + xwl->selection_property_offset, bytes_left);
if (bytes == -1) {
fprintf(stderr, "write error to target fd: %m\n");
close(fd);
} else if (bytes == bytes_left) {
if (xwl->selection_incremental_transfer) {
xcb_delete_property(xwl->connection, xwl->selection_window,
xwl->atoms[ATOM_WL_SELECTION].value);
} else {
close(fd);
}
} else {
xwl->selection_property_offset += bytes;
return 1;
}
free(xwl->selection_property_reply);
xwl->selection_property_reply = NULL;
if (xwl->selection_send_event_source) {
wl_event_source_remove(xwl->selection_send_event_source);
xwl->selection_send_event_source = NULL;
}
return 1;
}
static void xwl_write_selection_property(struct xwl *xwl,
xcb_get_property_reply_t *reply) {
xwl->selection_property_offset = 0;
xwl->selection_property_reply = reply;
xwl_handle_selection_fd_writable(xwl->selection_data_source_send_fd,
WL_EVENT_WRITABLE, xwl);
if (!xwl->selection_property_reply)
return;
assert(!xwl->selection_send_event_source);
xwl->selection_send_event_source = wl_event_loop_add_fd(
wl_display_get_event_loop(xwl->host_display),
xwl->selection_data_source_send_fd, WL_EVENT_WRITABLE,
xwl_handle_selection_fd_writable, xwl);
}
static void xwl_send_selection_notify(struct xwl *xwl, xcb_atom_t property) {
xcb_selection_notify_event_t event = {
.response_type = XCB_SELECTION_NOTIFY,
.sequence = 0,
.time = xwl->selection_request.time,
.requestor = xwl->selection_request.requestor,
.selection = xwl->selection_request.selection,
.target = xwl->selection_request.target,
.property = property,
.pad0 = 0};
xcb_send_event(xwl->connection, 0, xwl->selection_request.requestor,
XCB_EVENT_MASK_NO_EVENT, (char *)&event);
}
static void xwl_send_selection_data(struct xwl *xwl) {
assert(!xwl->selection_data_ack_pending);
xcb_change_property(
xwl->connection, XCB_PROP_MODE_REPLACE, xwl->selection_request.requestor,
xwl->selection_request.property, xwl->atoms[ATOM_UTF8_STRING].value, 8,
xwl->selection_data.size, xwl->selection_data.data);
xwl->selection_data_ack_pending = 1;
xwl->selection_data.size = 0;
}
static const uint32_t xwl_incr_chunk_size = 64 * 1024;
static int xwl_handle_selection_fd_readable(int fd, uint32_t mask, void *data) {
struct xwl *xwl = data;
int bytes, offset, bytes_left;
void *p;
offset = xwl->selection_data.size;
if (xwl->selection_data.size < xwl_incr_chunk_size)
p = wl_array_add(&xwl->selection_data, xwl_incr_chunk_size);
else
p = (char *)xwl->selection_data.data + xwl->selection_data.size;
bytes_left = xwl->selection_data.alloc - offset;
bytes = read(fd, p, bytes_left);
if (bytes == -1) {
fprintf(stderr, "read error from data source: %m\n");
xwl_send_selection_notify(xwl, XCB_ATOM_NONE);
xwl->selection_data_offer_receive_fd = -1;
close(fd);
} else {
xwl->selection_data.size = offset + bytes;
if (xwl->selection_data.size >= xwl_incr_chunk_size) {
if (!xwl->selection_incremental_transfer) {
xwl->selection_incremental_transfer = 1;
xcb_change_property(
xwl->connection, XCB_PROP_MODE_REPLACE,
xwl->selection_request.requestor, xwl->selection_request.property,
xwl->atoms[ATOM_INCR].value, 32, 1, &xwl_incr_chunk_size);
xwl->selection_data_ack_pending = 1;
xwl_send_selection_notify(xwl, xwl->selection_request.property);
} else if (!xwl->selection_data_ack_pending) {
xwl_send_selection_data(xwl);
}
} else if (bytes == 0) {
if (!xwl->selection_data_ack_pending)
xwl_send_selection_data(xwl);
if (!xwl->selection_incremental_transfer) {
xwl_send_selection_notify(xwl, xwl->selection_request.property);
xwl->selection_request.requestor = XCB_NONE;
wl_array_release(&xwl->selection_data);
}
xcb_flush(xwl->connection);
xwl->selection_data_offer_receive_fd = -1;
close(fd);
} else {
xwl->selection_data.size = offset + bytes;
return 1;
}
}
wl_event_source_remove(xwl->selection_event_source);
xwl->selection_event_source = NULL;
return 1;
}
static void xwl_handle_property_notify(struct xwl *xwl,
xcb_property_notify_event_t *event) {
if (event->atom == XCB_ATOM_WM_NAME) {
struct xwl_window *window = xwl_lookup_window(xwl, event->window);
if (!window)
return;
if (window->name) {
free(window->name);
window->name = NULL;
}
if (event->state != XCB_PROPERTY_DELETE) {
xcb_get_property_reply_t *reply = xcb_get_property_reply(
xwl->connection,
xcb_get_property(xwl->connection, 0, window->id, XCB_ATOM_WM_NAME,
XCB_ATOM_ANY, 0, 2048),
NULL);
if (reply) {
window->name = strndup(xcb_get_property_value(reply),
xcb_get_property_value_length(reply));
free(reply);
}
}
if (!window->xdg_toplevel || !xwl->show_window_title)
return;
if (window->name) {
zxdg_toplevel_v6_set_title(window->xdg_toplevel, window->name);
} else {
zxdg_toplevel_v6_set_title(window->xdg_toplevel, "");
}
} else if (event->atom == xwl->atoms[ATOM_WL_SELECTION].value) {
if (event->window == xwl->selection_window &&
event->state == XCB_PROPERTY_NEW_VALUE &&
xwl->selection_incremental_transfer) {
xcb_get_property_reply_t *reply = xcb_get_property_reply(
xwl->connection,
xcb_get_property(xwl->connection, 0, xwl->selection_window,
xwl->atoms[ATOM_WL_SELECTION].value,
XCB_GET_PROPERTY_TYPE_ANY, 0, 0x1fffffff),
NULL);
if (!reply)
return;
if (xcb_get_property_value_length(reply) > 0) {
xwl_write_selection_property(xwl, reply);
} else {
assert(!xwl->selection_send_event_source);
close(xwl->selection_data_source_send_fd);
free(reply);
}
}
} else if (event->atom == xwl->selection_request.property) {
if (event->window == xwl->selection_request.requestor &&
event->state == XCB_PROPERTY_DELETE &&
xwl->selection_incremental_transfer) {
int data_size = xwl->selection_data.size;
xwl->selection_data_ack_pending = 0;
// Handle the case when there's more data to be received.
if (xwl->selection_data_offer_receive_fd >= 0) {
// Avoid sending empty data until transfer is complete.
if (data_size)
xwl_send_selection_data(xwl);
if (!xwl->selection_event_source) {
xwl->selection_event_source = wl_event_loop_add_fd(
wl_display_get_event_loop(xwl->host_display),
xwl->selection_data_offer_receive_fd, WL_EVENT_READABLE,
xwl_handle_selection_fd_readable, xwl);
}
return;
}
xwl_send_selection_data(xwl);
// Release data if transfer is complete.
if (!data_size) {
xwl->selection_request.requestor = XCB_NONE;
wl_array_release(&xwl->selection_data);
}
}
}
}
static void xwl_data_source_target(void *data,
struct wl_data_source *data_source,
const char *mime_type) {}
static void xwl_data_source_send(void *data, struct wl_data_source *data_source,
const char *mime_type, int32_t fd) {
struct xwl_data_source *host = data;
struct xwl *xwl = host->xwl;
if (strcmp(mime_type, xwl_utf8_mime_type) == 0) {
xcb_convert_selection(
xwl->connection, xwl->selection_window,
xwl->atoms[ATOM_CLIPBOARD].value, xwl->atoms[ATOM_UTF8_STRING].value,
xwl->atoms[ATOM_WL_SELECTION].value, XCB_CURRENT_TIME);
fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
xwl->selection_data_source_send_fd = fd;
} else {
close(fd);
}
}
static void xwl_data_source_cancelled(void *data,
struct wl_data_source *data_source) {
struct xwl_data_source *host = data;
if (host->xwl->selection_data_source == host)
host->xwl->selection_data_source = NULL;
wl_data_source_destroy(data_source);
}
static const struct wl_data_source_listener xwl_data_source_listener = {
xwl_data_source_target, xwl_data_source_send, xwl_data_source_cancelled};
static void xwl_get_selection_targets(struct xwl *xwl) {
struct xwl_data_source *data_source = NULL;
xcb_get_property_reply_t *reply;
xcb_atom_t *value;
uint32_t i;
reply = xcb_get_property_reply(
xwl->connection,
xcb_get_property(xwl->connection, 1, xwl->selection_window,
xwl->atoms[ATOM_WL_SELECTION].value,
XCB_GET_PROPERTY_TYPE_ANY, 0, 4096),
NULL);
if (!reply)
return;
if (reply->type != XCB_ATOM_ATOM) {
free(reply);
return;
}
if (xwl->data_device_manager) {
data_source = malloc(sizeof(*data_source));
assert(data_source);
data_source->xwl = xwl;
data_source->internal = wl_data_device_manager_create_data_source(
xwl->data_device_manager->internal);
wl_data_source_add_listener(data_source->internal,
&xwl_data_source_listener, data_source);
value = xcb_get_property_value(reply);
for (i = 0; i < reply->value_len; i++) {
if (value[i] == xwl->atoms[ATOM_UTF8_STRING].value)
wl_data_source_offer(data_source->internal, "text/plain;charset=utf-8");
}
if (xwl->selection_data_device && xwl->default_seat) {
wl_data_device_set_selection(xwl->selection_data_device,
data_source->internal,
xwl->default_seat->seat->last_serial);
}
if (xwl->selection_data_source) {
wl_data_source_destroy(xwl->selection_data_source->internal);
free(xwl->selection_data_source);
}
xwl->selection_data_source = data_source;
}
free(reply);
}
static void xwl_get_selection_data(struct xwl *xwl) {
xcb_get_property_reply_t *reply = xcb_get_property_reply(
xwl->connection,
xcb_get_property(xwl->connection, 1, xwl->selection_window,
xwl->atoms[ATOM_WL_SELECTION].value,
XCB_GET_PROPERTY_TYPE_ANY, 0, 0x1fffffff),
NULL);
if (!reply)
return;
if (reply->type == xwl->atoms[ATOM_INCR].value) {
xwl->selection_incremental_transfer = 1;
free(reply);
} else {
xwl->selection_incremental_transfer = 0;
xwl_write_selection_property(xwl, reply);
}
}
static void xwl_handle_selection_notify(struct xwl *xwl,
xcb_selection_notify_event_t *event) {
if (event->property == XCB_ATOM_NONE)
return;
if (event->target == xwl->atoms[ATOM_TARGETS].value)
xwl_get_selection_targets(xwl);
else
xwl_get_selection_data(xwl);
}
static void xwl_send_targets(struct xwl *xwl) {
xcb_atom_t targets[] = {
xwl->atoms[ATOM_TIMESTAMP].value, xwl->atoms[ATOM_TARGETS].value,
xwl->atoms[ATOM_UTF8_STRING].value, xwl->atoms[ATOM_TEXT].value,
};
xcb_change_property(xwl->connection, XCB_PROP_MODE_REPLACE,
xwl->selection_request.requestor,
xwl->selection_request.property, XCB_ATOM_ATOM, 32,
ARRAY_SIZE(targets), targets);
xwl_send_selection_notify(xwl, xwl->selection_request.property);
}
static void xwl_send_timestamp(struct xwl *xwl) {
xcb_change_property(xwl->connection, XCB_PROP_MODE_REPLACE,
xwl->selection_request.requestor,
xwl->selection_request.property, XCB_ATOM_INTEGER, 32, 1,
&xwl->selection_timestamp);
xwl_send_selection_notify(xwl, xwl->selection_request.property);
}
static void xwl_send_data(struct xwl *xwl) {
int p[2];
if (!xwl->selection_data_offer || !xwl->selection_data_offer->utf8_text) {
xwl_send_selection_notify(xwl, XCB_ATOM_NONE);
return;
}
if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
fprintf(stderr, "pipe2 failed: %m\n");
xwl_send_selection_notify(xwl, XCB_ATOM_NONE);
return;
}
wl_array_init(&xwl->selection_data);
xwl->selection_data_offer_receive_fd = p[0];
xwl->selection_data_ack_pending = 0;
assert(!xwl->selection_event_source);
xwl->selection_event_source = wl_event_loop_add_fd(
wl_display_get_event_loop(xwl->host_display),
xwl->selection_data_offer_receive_fd, WL_EVENT_READABLE,
xwl_handle_selection_fd_readable, xwl);
wl_data_offer_receive(xwl->selection_data_offer->internal,
"text/plain;charset=utf-8", p[1]);
close(p[1]);
}
static void xwl_handle_selection_request(struct xwl *xwl,
xcb_selection_request_event_t *event) {
xwl->selection_request = *event;
xwl->selection_incremental_transfer = 0;
if (event->selection == xwl->atoms[ATOM_CLIPBOARD_MANAGER].value) {
xwl_send_selection_notify(xwl, xwl->selection_request.property);
return;
}
if (event->target == xwl->atoms[ATOM_TARGETS].value) {
xwl_send_targets(xwl);
} else if (event->target == xwl->atoms[ATOM_TIMESTAMP].value) {
xwl_send_timestamp(xwl);
} else if (event->target == xwl->atoms[ATOM_UTF8_STRING].value ||
event->target == xwl->atoms[ATOM_TEXT].value) {
xwl_send_data(xwl);
} else {
xwl_send_selection_notify(xwl, XCB_ATOM_NONE);
}
}
static void
xwl_handle_xfixes_selection_notify(struct xwl *xwl,
xcb_xfixes_selection_notify_event_t *event) {
if (event->selection != xwl->atoms[ATOM_CLIPBOARD].value)
return;
if (event->owner == XCB_WINDOW_NONE) {
// If client selection is gone. Set NULL selection for each seat.
if (xwl->selection_owner != xwl->selection_window) {
if (xwl->selection_data_device && xwl->default_seat) {
wl_data_device_set_selection(xwl->selection_data_device, NULL,
xwl->default_seat->seat->last_serial);
}
}
xwl->selection_owner = XCB_WINDOW_NONE;
return;
}
xwl->selection_owner = event->owner;
// Save timestamp if it's our selection.
if (event->owner == xwl->selection_window) {
xwl->selection_timestamp = event->timestamp;
return;
}
xwl->selection_incremental_transfer = 0;
xcb_convert_selection(xwl->connection, xwl->selection_window,
xwl->atoms[ATOM_CLIPBOARD].value,
xwl->atoms[ATOM_TARGETS].value,
xwl->atoms[ATOM_WL_SELECTION].value, event->timestamp);
}
static int xwl_handle_x_connection_event(int fd, uint32_t mask, void *data) {
struct xwl *xwl = (struct xwl *)data;
xcb_generic_event_t *event;
uint32_t count = 0;
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR))
return 0;
while ((event = xcb_poll_for_event(xwl->connection))) {
switch (event->response_type & ~SEND_EVENT_MASK) {
case XCB_CREATE_NOTIFY:
xwl_handle_create_notify(xwl, (xcb_create_notify_event_t *)event);
break;
case XCB_DESTROY_NOTIFY:
xwl_handle_destroy_notify(xwl, (xcb_destroy_notify_event_t *)event);
break;
case XCB_REPARENT_NOTIFY:
xwl_handle_reparent_notify(xwl, (xcb_reparent_notify_event_t *)event);
break;
case XCB_MAP_REQUEST:
xwl_handle_map_request(xwl, (xcb_map_request_event_t *)event);
break;
case XCB_MAP_NOTIFY:
xwl_handle_map_notify(xwl, (xcb_map_notify_event_t *)event);
break;
case XCB_UNMAP_NOTIFY:
xwl_handle_unmap_notify(xwl, (xcb_unmap_notify_event_t *)event);
break;
case XCB_CONFIGURE_REQUEST:
xwl_handle_configure_request(xwl, (xcb_configure_request_event_t *)event);
break;
case XCB_CONFIGURE_NOTIFY:
xwl_handle_configure_notify(xwl, (xcb_configure_notify_event_t *)event);
break;
case XCB_CLIENT_MESSAGE:
xwl_handle_client_message(xwl, (xcb_client_message_event_t *)event);
break;
case XCB_FOCUS_IN:
xwl_handle_focus_in(xwl, (xcb_focus_in_event_t *)event);
break;
case XCB_FOCUS_OUT:
xwl_handle_focus_out(xwl, (xcb_focus_out_event_t *)event);
break;
case XCB_PROPERTY_NOTIFY:
xwl_handle_property_notify(xwl, (xcb_property_notify_event_t *)event);
break;
case XCB_SELECTION_NOTIFY:
xwl_handle_selection_notify(xwl, (xcb_selection_notify_event_t *)event);
break;
case XCB_SELECTION_REQUEST:
xwl_handle_selection_request(xwl, (xcb_selection_request_event_t *)event);
break;
}
switch (event->response_type - xwl->xfixes_extension->first_event) {
case XCB_XFIXES_SELECTION_NOTIFY:
xwl_handle_xfixes_selection_notify(
xwl, (xcb_xfixes_selection_notify_event_t *)event);
break;
}
free(event);
++count;
}
if ((mask & ~WL_EVENT_WRITABLE) == 0)
xcb_flush(xwl->connection);
return count;
}
static void xwl_connect(struct xwl *xwl) {
const char wm_name[] = "WLWM";
const xcb_setup_t *setup;
xcb_screen_iterator_t screen_iterator;
uint32_t values[1];
xcb_void_cookie_t change_attributes_cookie, redirect_subwindows_cookie;
xcb_generic_error_t *error;
xcb_intern_atom_reply_t *atom_reply;
xcb_depth_iterator_t depth_iterator;
xcb_xfixes_query_version_reply_t *xfixes_query_version_reply;
const xcb_query_extension_reply_t *composite_extension;
unsigned i;
xwl->connection = xcb_connect_to_fd(xwl->wm_fd, NULL);
assert(!xcb_connection_has_error(xwl->connection));
xcb_prefetch_extension_data(xwl->connection, &xcb_xfixes_id);
xcb_prefetch_extension_data(xwl->connection, &xcb_composite_id);
for (i = 0; i < ARRAY_SIZE(xwl->atoms); ++i) {
const char *name = xwl->atoms[i].name;
xwl->atoms[i].cookie =
xcb_intern_atom(xwl->connection, 0, strlen(name), name);
}
setup = xcb_get_setup(xwl->connection);
screen_iterator = xcb_setup_roots_iterator(setup);
xwl->screen = screen_iterator.data;
// Select for substructure redirect.
values[0] = XCB_EVENT_MASK_STRUCTURE_NOTIFY |
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
change_attributes_cookie = xcb_change_window_attributes(
xwl->connection, xwl->screen->root, XCB_CW_EVENT_MASK, values);
xwl->connection_event_source = wl_event_loop_add_fd(
wl_display_get_event_loop(xwl->host_display),
xcb_get_file_descriptor(xwl->connection), WL_EVENT_READABLE,
&xwl_handle_x_connection_event, xwl);
xwl->xfixes_extension =
xcb_get_extension_data(xwl->connection, &xcb_xfixes_id);
assert(xwl->xfixes_extension->present);
xfixes_query_version_reply = xcb_xfixes_query_version_reply(
xwl->connection,
xcb_xfixes_query_version(xwl->connection, XCB_XFIXES_MAJOR_VERSION,
XCB_XFIXES_MINOR_VERSION),
NULL);
assert(xfixes_query_version_reply);
assert(xfixes_query_version_reply->major_version >= 5);
free(xfixes_query_version_reply);
composite_extension =
xcb_get_extension_data(xwl->connection, &xcb_composite_id);
assert(composite_extension->present);
redirect_subwindows_cookie = xcb_composite_redirect_subwindows_checked(
xwl->connection, xwl->screen->root, XCB_COMPOSITE_REDIRECT_MANUAL);
// Another window manager should not be running.
error = xcb_request_check(xwl->connection, change_attributes_cookie);
assert(!error);
// Redirecting subwindows of root for compositing should have succeeded.
error = xcb_request_check(xwl->connection, redirect_subwindows_cookie);
assert(!error);
xwl->window = xcb_generate_id(xwl->connection);
xcb_create_window(xwl->connection, 0, xwl->window, xwl->screen->root, 0, 0, 1,
1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, 0,
NULL);
for (i = 0; i < ARRAY_SIZE(xwl->atoms); ++i) {
atom_reply =
xcb_intern_atom_reply(xwl->connection, xwl->atoms[i].cookie, &error);
assert(!error);
xwl->atoms[i].value = atom_reply->atom;
free(atom_reply);
}
depth_iterator = xcb_screen_allowed_depths_iterator(xwl->screen);
while (depth_iterator.rem > 0) {
int depth = depth_iterator.data->depth;
if (depth == xwl->screen->root_depth) {
xwl->visual_ids[depth] = xwl->screen->root_visual;
xwl->colormaps[depth] = xwl->screen->default_colormap;
} else {
xcb_visualtype_iterator_t visualtype_iterator =
xcb_depth_visuals_iterator(depth_iterator.data);
xwl->visual_ids[depth] = visualtype_iterator.data->visual_id;
xwl->colormaps[depth] = xcb_generate_id(xwl->connection);
xcb_create_colormap(xwl->connection, XCB_COLORMAP_ALLOC_NONE,
xwl->colormaps[depth], xwl->screen->root,
xwl->visual_ids[depth]);
}
xcb_depth_next(&depth_iterator);
}
assert(xwl->visual_ids[xwl->screen->root_depth]);
if (xwl->clipboard_manager) {
values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
xwl->selection_window = xcb_generate_id(xwl->connection);
xcb_create_window(xwl->connection, XCB_COPY_FROM_PARENT,
xwl->selection_window, xwl->screen->root, 0, 0, 1, 1, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, xwl->screen->root_visual,
XCB_CW_EVENT_MASK, values);
xcb_set_selection_owner(xwl->connection, xwl->selection_window,
xwl->atoms[ATOM_CLIPBOARD_MANAGER].value,
XCB_CURRENT_TIME);
xcb_xfixes_select_selection_input(
xwl->connection, xwl->selection_window,
xwl->atoms[ATOM_CLIPBOARD].value,
XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE);
xwl_set_selection(xwl, NULL);
}
xcb_change_property(xwl->connection, XCB_PROP_MODE_REPLACE, xwl->window,
xwl->atoms[ATOM_NET_SUPPORTING_WM_CHECK].value,
XCB_ATOM_WINDOW, 32, 1, &xwl->window);
xcb_change_property(xwl->connection, XCB_PROP_MODE_REPLACE, xwl->window,
xwl->atoms[ATOM_NET_WM_NAME].value,
xwl->atoms[ATOM_UTF8_STRING].value, 8, strlen(wm_name),
wm_name);
xcb_change_property(xwl->connection, XCB_PROP_MODE_REPLACE, xwl->screen->root,
xwl->atoms[ATOM_NET_SUPPORTING_WM_CHECK].value,
XCB_ATOM_WINDOW, 32, 1, &xwl->window);
xcb_set_selection_owner(xwl->connection, xwl->window,
xwl->atoms[ATOM_WM_S0].value, XCB_CURRENT_TIME);
xcb_set_input_focus(xwl->connection, XCB_INPUT_FOCUS_NONE, XCB_NONE,
XCB_CURRENT_TIME);
xcb_flush(xwl->connection);
}
static int xwl_handle_sigchld(int signal_number, void *data) {
struct xwl *xwl = (struct xwl *)data;
int status;
pid_t pid;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
if (pid == xwl->child_pid) {
xwl->child_pid = -1;
if (WIFEXITED(status) && WEXITSTATUS(status)) {
fprintf(stderr, "Child exited with status: %d\n", WEXITSTATUS(status));
}
if (xwl->exit_with_child && xwl->xwayland_pid >= 0)
kill(xwl->xwayland_pid, SIGTERM);
} else if (pid == xwl->xwayland_pid) {
xwl->xwayland_pid = -1;
if (WIFEXITED(status) && WEXITSTATUS(status)) {
fprintf(stderr, "Xwayland exited with status: %d\n",
WEXITSTATUS(status));
exit(WEXITSTATUS(status));
}
}
}
return 1;
}
static void xwl_execvp(const char *file, char *const argv[],
int wayland_socked_fd) {
if (wayland_socked_fd >= 0) {
char fd_str[8];
int fd;
fd = dup(wayland_socked_fd);
snprintf(fd_str, sizeof(fd_str), "%d", fd);
setenv("WAYLAND_SOCKET", fd_str, 1);
}
setenv("XWL_VERSION", VERSION, 1);
execvp(file, argv);
perror(file);
}
static int xwl_handle_display_ready_event(int fd, uint32_t mask, void *data) {
struct xwl *xwl = (struct xwl *)data;
char display_name[9];
int bytes_read = 0;
pid_t pid;
if (!(mask & WL_EVENT_READABLE))
return 0;
display_name[0] = ':';
do {
int bytes_left = sizeof(display_name) - bytes_read - 1;
int bytes;
if (!bytes_left)
break;
bytes = read(fd, &display_name[bytes_read + 1], bytes_left);
if (!bytes)
break;
bytes_read += bytes;
} while (display_name[bytes_read] != '\n');
display_name[bytes_read] = '\0';
setenv("DISPLAY", display_name, 1);
xwl_connect(xwl);
wl_event_source_remove(xwl->display_ready_event_source);
xwl->display_ready_event_source = NULL;
close(fd);
sd_notify(0, "READY=1");
pid = fork();
assert(pid >= 0);
if (pid == 0) {
xwl_execvp(xwl->runprog[0], xwl->runprog, -1);
_exit(EXIT_FAILURE);
}
xwl->child_pid = pid;
return 1;
}
static void xwl_sigchld_handler(int signal) {
while (waitpid(-1, NULL, WNOHANG) > 0)
continue;
}
static void xwl_client_destroy_notify(struct wl_listener *listener,
void *data) {
exit(0);
}
static void xwl_registry_bind(struct wl_client *client,
struct wl_resource *resource, uint32_t name,
const char *interface, uint32_t version,
uint32_t id) {
struct xwl_host_registry *host = wl_resource_get_user_data(resource);
struct xwl_global *global;
wl_list_for_each(global, &host->xwl->globals, link) {
if (global->name == name)
break;
}
assert(&global->link != &host->xwl->globals);
assert(version != 0);
assert(global->version >= version);
global->bind(client, global->data, version, id);
}
static const struct wl_registry_interface xwl_registry_implementation = {
xwl_registry_bind};
static void xwl_sync_callback_done(void *data, struct wl_callback *callback,
uint32_t serial) {
struct xwl_host_callback *host = wl_callback_get_user_data(callback);
wl_callback_send_done(host->resource, serial);
wl_resource_destroy(host->resource);
}
static const struct wl_callback_listener xwl_sync_callback_listener = {
xwl_sync_callback_done};
static void xwl_display_sync(struct wl_client *client,
struct wl_resource *resource, uint32_t id) {
struct xwl *xwl = wl_resource_get_user_data(resource);
struct xwl_host_callback *host_callback;
host_callback = malloc(sizeof(*host_callback));
assert(host_callback);
host_callback->resource =
wl_resource_create(client, &wl_callback_interface, 1, id);
wl_resource_set_implementation(host_callback->resource, NULL, host_callback,
xwl_host_callback_destroy);
host_callback->proxy = wl_display_sync(xwl->display);
wl_callback_set_user_data(host_callback->proxy, host_callback);
wl_callback_add_listener(host_callback->proxy, &xwl_sync_callback_listener,
host_callback);
}
static void xwl_destroy_host_registry(struct wl_resource *resource) {
struct xwl_host_registry *host = wl_resource_get_user_data(resource);
wl_list_remove(&host->link);
free(host);
}
static void xwl_display_get_registry(struct wl_client *client,
struct wl_resource *resource,
uint32_t id) {
struct xwl *xwl = wl_resource_get_user_data(resource);
struct xwl_host_registry *host_registry;
struct xwl_global *global;
host_registry = malloc(sizeof(*host_registry));
assert(host_registry);
host_registry->xwl = xwl;
host_registry->resource =
wl_resource_create(client, &wl_registry_interface, 1, id);
wl_list_insert(&xwl->registries, &host_registry->link);
wl_resource_set_implementation(host_registry->resource,
&xwl_registry_implementation, host_registry,
xwl_destroy_host_registry);
wl_list_for_each(global, &xwl->globals, link) {
wl_resource_post_event(host_registry->resource, WL_REGISTRY_GLOBAL,
global->name, global->interface->name,
global->version);
}
}
static const struct wl_display_interface xwl_display_implementation = {
xwl_display_sync, xwl_display_get_registry};
static enum wl_iterator_result
xwl_set_display_implementation(struct wl_resource *resource, void *user_data) {
struct xwl *xwl = (struct xwl *)user_data;
if (strcmp(wl_resource_get_class(resource), "wl_display") == 0) {
wl_resource_set_implementation(resource, &xwl_display_implementation, xwl,
NULL);
return WL_ITERATOR_STOP;
}
return WL_ITERATOR_CONTINUE;
}
static void xwl_usage() {
printf("xwl-run "
"[-X] "
"[--master] "
"[--socket=SOCKET] "
"[--scale=SCALE] "
"[--app-id=ID] "
"[--x-display=DISPLAY] "
"[--no-exit-with-child] "
"[--no-clipboard-manager] "
"[--frame-color=COLOR] "
"[--drm-device=DEVICE] "
"[--glamor] "
"[PROGRAM] [ARGS...]\n");
}
int main(int argc, char **argv) {
struct xwl xwl = {
.runprog = NULL,
.display = NULL,
.host_display = NULL,
.client = NULL,
.compositor = NULL,
.subcompositor = NULL,
.shm = NULL,
.shell = NULL,
.data_device_manager = NULL,
.xdg_shell = NULL,
.aura_shell = NULL,
.viewporter = NULL,
.linux_dmabuf = NULL,
.display_event_source = NULL,
.display_ready_event_source = NULL,
.sigchld_event_source = NULL,
.wm_fd = -1,
.drm_device = NULL,
.xwayland = 0,
.xwayland_pid = -1,
.child_pid = -1,
.peer_pid = -1,
.next_global_id = 1,
.connection = NULL,
.connection_event_source = NULL,
.xfixes_extension = NULL,
.screen = NULL,
.window = 0,
.host_focus_window = NULL,
.needs_set_input_focus = 0,
.scale = 1.0,
.app_id = NULL,
.exit_with_child = 1,
.clipboard_manager = 0,
.frame_color = 0,
.has_frame_color = 0,
.show_window_title = 0,
.default_seat = NULL,
.selection_window = XCB_WINDOW_NONE,
.selection_owner = XCB_WINDOW_NONE,
.selection_incremental_transfer = 0,
.selection_request = {.requestor = XCB_NONE, .property = XCB_ATOM_NONE},
.selection_timestamp = XCB_CURRENT_TIME,
.selection_data_device = NULL,
.selection_data_offer = NULL,
.selection_data_source = NULL,
.selection_data_source_send_fd = -1,
.selection_send_event_source = NULL,
.selection_property_reply = NULL,
.selection_property_offset = 0,
.selection_event_source = NULL,
.selection_data_offer_receive_fd = -1,
.selection_data_ack_pending = 0,
.atoms =
{
[ATOM_WM_S0] = {"WM_S0"},
[ATOM_WM_PROTOCOLS] = {"WM_PROTOCOLS"},
[ATOM_WM_STATE] = {"WM_STATE"},
[ATOM_WM_DELETE_WINDOW] = {"WM_DELETE_WINDOW"},
[ATOM_WM_TAKE_FOCUS] = {"WM_TAKE_FOCUS"},
[ATOM_WL_SURFACE_ID] = {"WL_SURFACE_ID"},
[ATOM_UTF8_STRING] = {"UTF8_STRING"},
[ATOM_MOTIF_WM_HINTS] = {"_MOTIF_WM_HINTS"},
[ATOM_NET_FRAME_EXTENTS] = {"_NET_FRAME_EXTENTS"},
[ATOM_NET_SUPPORTING_WM_CHECK] = {"_NET_SUPPORTING_WM_CHECK"},
[ATOM_NET_WM_NAME] = {"_NET_WM_NAME"},
[ATOM_NET_WM_MOVERESIZE] = {"_NET_WM_MOVERESIZE"},
[ATOM_NET_WM_STATE] = {"_NET_WM_STATE"},
[ATOM_NET_WM_STATE_FULLSCREEN] = {"_NET_WM_STATE_FULLSCREEN"},
[ATOM_NET_WM_STATE_MAXIMIZED_VERT] =
{"_NET_WM_STATE_MAXIMIZED_VERT"},
[ATOM_NET_WM_STATE_MAXIMIZED_HORZ] =
{"_NET_WM_STATE_MAXIMIZED_HORZ"},
[ATOM_CLIPBOARD] = {"CLIPBOARD"},
[ATOM_CLIPBOARD_MANAGER] = {"CLIPBOARD_MANAGER"},
[ATOM_TARGETS] = {"TARGETS"},
[ATOM_TIMESTAMP] = {"TIMESTAMP"}, [ATOM_TEXT] = {"TEXT"},
[ATOM_INCR] = {"INCR"},
[ATOM_WL_SELECTION] = {"_WL_SELECTION"},
},
.visual_ids = {0},
.colormaps = {0}};
const char *scale = getenv("XWL_SCALE");
const char *clipboard_manager = getenv("XWL_CLIPBOARD_MANAGER");
const char *frame_color = getenv("XWL_FRAME_COLOR");
const char *show_window_title = getenv("XWL_SHOW_WINDOW_TITLE");
const char *drm_device = getenv("XWL_DRM_DEVICE");
const char *glamor = getenv("XWL_GLAMOR");
const char *socket_name = "wayland-0";
struct wl_event_loop *event_loop;
struct wl_listener client_destroy_listener = {.notify =
xwl_client_destroy_notify};
int sv[2];
pid_t pid;
int xdisplay = -1;
int master = 0;
int client_fd = -1;
int rv;
int i;
for (i = 1; i < argc; ++i) {
const char *arg = argv[i];
if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0 ||
strcmp(arg, "-?") == 0) {
xwl_usage();
return 0;
}
if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) {
printf("Version: %s\n", VERSION);
return 0;
}
if (strstr(arg, "--master") == arg) {
master = 1;
} else if (strstr(arg, "--socket") == arg) {
const char *s = strchr(arg, '=');
++s;
socket_name = s;
} else if (strstr(arg, "--peer-pid") == arg) {
const char *s = strchr(arg, '=');
++s;
xwl.peer_pid = atoi(s);
} else if (strstr(arg, "--client-fd") == arg) {
const char *s = strchr(arg, '=');
++s;
client_fd = atoi(s);
} else if (strstr(arg, "--scale") == arg) {
const char *s = strchr(arg, '=');
++s;
scale = s;
} else if (strstr(arg, "--app-id") == arg) {
const char *s = strchr(arg, '=');
++s;
xwl.app_id = s;
} else if (strstr(arg, "-X") == arg) {
xwl.xwayland = 1;
} else if (strstr(arg, "--x-display") == arg) {
const char *s = strchr(arg, '=');
++s;
xdisplay = atoi(s);
// Automatically enable X forwarding if X display is specified.
xwl.xwayland = 1;
} else if (strstr(arg, "--no-exit-with-child") == arg) {
xwl.exit_with_child = 0;
} else if (strstr(arg, "--no-clipboard-manager") == arg) {
clipboard_manager = "0";
} else if (strstr(arg, "--frame-color") == arg) {
const char *s = strchr(arg, '=');
++s;
frame_color = s;
} else if (strstr(arg, "--show-window-title") == arg) {
show_window_title = "1";
} else if (strstr(arg, "--drm-device") == arg) {
const char *s = strchr(arg, '=');
++s;
drm_device = s;
} else if (strstr(arg, "--glamor") == arg) {
glamor = "1";
} else if (arg[0] == '-') {
if (strcmp(arg, "--") != 0) {
fprintf(stderr, "Option `%s' is unknown.\n", arg);
return 1;
}
xwl.runprog = &argv[i + 1];
break;
} else {
xwl.runprog = &argv[i];
break;
}
}
if (master) {
const char *runtime_dir = getenv("XDG_RUNTIME_DIR");
char lock_addr[UNIX_PATH_MAX + LOCK_SUFFIXLEN];
struct sockaddr_un addr;
struct sigaction sa;
struct stat sock_stat;
int lock_fd;
int sock_fd;
if (!runtime_dir) {
fprintf(stderr, "XDG_RUNTIME_DIR not set in the environment\n");
exit(1);
}
addr.sun_family = AF_LOCAL;
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", runtime_dir,
socket_name);
snprintf(lock_addr, sizeof(lock_addr), "%s%s", addr.sun_path, LOCK_SUFFIX);
lock_fd = open(lock_addr, O_CREAT | O_CLOEXEC,
(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));
assert(lock_fd >= 0);
rv = flock(lock_fd, LOCK_EX | LOCK_NB);
if (rv < 0) {
fprintf(stderr, "unable to lock %s, is another compositor running?\n",
lock_addr);
exit(1);
}
rv = stat(addr.sun_path, &sock_stat);
if (rv >= 0) {
if (sock_stat.st_mode & (S_IWUSR | S_IWGRP))
unlink(addr.sun_path);
} else {
assert(errno == ENOENT);
}
sock_fd = socket(PF_LOCAL, SOCK_STREAM, 0);
assert(sock_fd >= 0);
rv = bind(sock_fd, (struct sockaddr *)&addr,
offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path));
assert(rv >= 0);
rv = listen(sock_fd, 128);
assert(rv >= 0);
sa.sa_handler = xwl_sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
rv = sigaction(SIGCHLD, &sa, NULL);
assert(rv >= 0);
sd_notify(0, "READY=1");
do {
struct ucred ucred;
socklen_t length = sizeof(addr);
client_fd = accept(sock_fd, (struct sockaddr *)&addr, &length);
if (client_fd < 0) {
fprintf(stderr, "failed to accept: %m\n");
continue;
}
ucred.pid = -1;
length = sizeof(ucred);
rv = getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &length);
pid = fork();
assert(pid != -1);
if (pid == 0) {
char client_fd_str[64], peer_pid_str[64];
char *args[32];
int i = 0, j;
close(sock_fd);
close(lock_fd);
args[i++] = argv[0];
snprintf(peer_pid_str, sizeof(peer_pid_str), "--peer-pid=%d",
ucred.pid);
args[i++] = peer_pid_str;
snprintf(client_fd_str, sizeof(client_fd_str), "--client-fd=%d",
client_fd);
args[i++] = client_fd_str;
// forward some flags.
for (j = 1; j < argc; ++j) {
char *arg = argv[j];
if (strstr(arg, "--scale") == arg ||
strstr(arg, "--drm-device") == arg) {
args[i++] = arg;
}
}
args[i++] = NULL;
execvp(argv[0], args);
_exit(EXIT_FAILURE);
}
close(client_fd);
} while (1);
_exit(EXIT_FAILURE);
}
if (client_fd == -1) {
if (!xwl.runprog || !xwl.runprog[0]) {
xwl_usage();
return 1;
}
}
if (xwl.xwayland) {
assert(client_fd == -1);
xwl.clipboard_manager = 1;
if (clipboard_manager)
xwl.clipboard_manager = !!strcmp(clipboard_manager, "0");
}
if (scale)
xwl.scale = MIN(MAX_SCALE, MAX(MIN_SCALE, atof(scale)));
if (frame_color) {
int r, g, b;
if (sscanf(frame_color, "#%02x%02x%02x", &r, &g, &b) == 3) {
xwl.frame_color = 0xff000000 | (r << 16) | (g << 8) | (b << 0);
xwl.has_frame_color = 1;
}
}
if (show_window_title)
xwl.show_window_title = !!strcmp(show_window_title, "0");
if (drm_device && !access(drm_device, 0))
xwl.drm_device = drm_device;
if (xwl.runprog || xwl.xwayland) {
// Wayland connection from client.
rv = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv);
assert(!rv);
client_fd = sv[0];
}
xwl.display = wl_display_connect(NULL);
assert(xwl.display);
wl_list_init(&xwl.registries);
wl_list_init(&xwl.globals);
wl_list_init(&xwl.outputs);
wl_list_init(&xwl.seats);
wl_list_init(&xwl.windows);
wl_list_init(&xwl.unpaired_windows);
xwl.host_display = wl_display_create();
assert(xwl.host_display);
event_loop = wl_display_get_event_loop(xwl.host_display);
xwl.display_event_source =
wl_event_loop_add_fd(event_loop, wl_display_get_fd(xwl.display),
WL_EVENT_READABLE, xwl_handle_event, &xwl);
wl_registry_add_listener(wl_display_get_registry(xwl.display),
&xwl_registry_listener, &xwl);
wl_display_roundtrip(xwl.display);
if (!xwl.viewporter)
xwl.scale = ceil(xwl.scale);
xwl.client = wl_client_create(xwl.host_display, client_fd);
// Replace the core display implementation. This is needed in order to
// implement sync handler properly.
wl_client_for_each_resource(xwl.client, xwl_set_display_implementation, &xwl);
if (xwl.runprog || xwl.xwayland) {
xwl.sigchld_event_source =
wl_event_loop_add_signal(event_loop, SIGCHLD, xwl_handle_sigchld, &xwl);
if (xwl.xwayland) {
int ds[2], wm[2];
// Xwayland display ready socket.
rv = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ds);
assert(!rv);
xwl.display_ready_event_source =
wl_event_loop_add_fd(event_loop, ds[0], WL_EVENT_READABLE,
xwl_handle_display_ready_event, &xwl);
// X connection to Xwayland.
rv = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wm);
assert(!rv);
xwl.wm_fd = wm[0];
pid = fork();
assert(pid != -1);
if (pid == 0) {
char display_str[8], display_fd_str[8], wm_fd_str[8];
char *args[32];
int i = 0;
int fd;
fd = dup(ds[1]);
snprintf(display_fd_str, sizeof(display_fd_str), "%d", fd);
fd = dup(wm[1]);
snprintf(wm_fd_str, sizeof(wm_fd_str), "%d", fd);
args[i++] = XWAYLAND_PATH "/Xwayland";
if (xdisplay > 0) {
snprintf(display_str, sizeof(display_str), ":%d", xdisplay);
args[i++] = display_str;
}
args[i++] = "-nolisten";
args[i++] = "tcp";
args[i++] = "-rootless";
if (xwl.drm_device) {
// Use DRM and software rendering unless glamor is enabled.
if (!glamor || !strcmp(glamor, "0"))
args[i++] = "-drm";
} else {
args[i++] = "-shm";
}
args[i++] = "-displayfd";
args[i++] = display_fd_str;
args[i++] = "-wm";
args[i++] = wm_fd_str;
args[i++] = NULL;
xwl_execvp(XWAYLAND_PATH "/Xwayland", args, sv[1]);
_exit(EXIT_FAILURE);
}
close(wm[1]);
xwl.xwayland_pid = pid;
} else {
pid = fork();
assert(pid != -1);
if (pid == 0) {
xwl_execvp(xwl.runprog[0], xwl.runprog, sv[1]);
_exit(EXIT_FAILURE);
}
xwl.child_pid = pid;
}
close(sv[1]);
}
wl_client_add_destroy_listener(xwl.client, &client_destroy_listener);
do {
wl_display_flush_clients(xwl.host_display);
if (xwl.connection) {
if (xwl.needs_set_input_focus) {
xwl_set_input_focus(&xwl, xwl.host_focus_window);
xwl.needs_set_input_focus = 0;
}
xcb_flush(xwl.connection);
}
wl_display_flush(xwl.display);
} while (wl_event_loop_dispatch(event_loop, -1) != -1);
return 0;
}