blob: 317357ee8b677786cb4f5436bc7c07feb3c4e402 [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 <gbm.h>
#include <libgen.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/mman.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 "virtwl.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_damage_tracker {
struct wl_list link;
int x1;
int y1;
int x2;
int y2;
};
typedef void (*xwl_begin_end_access_func_t)(int fd);
struct xwl_mmap {
struct xwl_damage_tracker damage;
int refcount;
int fd;
void *addr;
size_t size;
size_t offset;
size_t stride;
size_t bpp;
xwl_begin_end_access_func_t begin_access;
xwl_begin_end_access_func_t end_access;
};
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_mmap *client_mmap;
struct xwl_mmap *host_mmap;
struct wl_list damage_trackers;
};
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;
uint32_t stride;
uint32_t size;
int32_t offset;
struct xwl_mmap *client_mmap;
struct xwl_mmap *host_mmap;
};
struct xwl_host_shm_pool {
struct xwl_shm *shm;
struct wl_resource *resource;
struct wl_shm_pool *proxy;
int fd;
};
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 wl_shm *internal;
};
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 shm_driver;
int wm_fd;
int virtwl_fd;
const char *drm_device;
struct gbm_device *gbm;
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,
};
enum {
SHM_DRIVER_NOOP,
SHM_DRIVER_DMABUF,
SHM_DRIVER_VIRTWL,
};
#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
struct dma_buf_sync {
__u64 flags;
};
#define DMA_BUF_SYNC_READ (1 << 0)
#define DMA_BUF_SYNC_WRITE (2 << 0)
#define DMA_BUF_SYNC_RW (DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE)
#define DMA_BUF_SYNC_START (0 << 2)
#define DMA_BUF_SYNC_END (1 << 2)
#define DMA_BUF_BASE 'b'
#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
static void xwl_dmabuf_sync(int fd, __u64 flags) {
struct dma_buf_sync sync = {0};
int rv;
sync.flags = flags;
do {
rv = ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync);
} while (rv == -1 && errno == EINTR);
}
static void xwl_dmabuf_begin_access(int fd) {
xwl_dmabuf_sync(fd, DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW);
}
static void xwl_dmabuf_end_access(int fd) {
xwl_dmabuf_sync(fd, DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW);
}
static struct xwl_mmap *xwl_mmap_create(int fd, size_t size, size_t offset,
size_t stride, size_t bpp) {
struct xwl_mmap *map;
map = malloc(sizeof(*map));
wl_list_init(&map->damage.link);
map->refcount = 1;
map->fd = fd;
map->size = size;
map->offset = offset;
map->stride = stride;
map->bpp = bpp;
map->begin_access = NULL;
map->end_access = NULL;
map->addr =
mmap(NULL, size + offset, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
assert(map->addr != MAP_FAILED);
return map;
}
static struct xwl_mmap *xwl_mmap_ref(struct xwl_mmap *map) {
map->refcount++;
return map;
}
static void xwl_mmap_unref(struct xwl_mmap *map) {
if (map->refcount-- == 1) {
munmap(map->addr, map->size);
close(map->fd);
wl_list_remove(&map->damage.link);
free(map);
}
}
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_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->client_mmap) {
xwl_mmap_unref(host->client_mmap);
host->client_mmap = NULL;
}
if (host->host_mmap) {
xwl_mmap_unref(host->host_mmap);
host->host_mmap = NULL;
}
if (host_buffer) {
host->contents_width = host_buffer->width;
host->contents_height = host_buffer->height;
buffer_proxy = host_buffer->proxy;
if (host_buffer->client_mmap)
host->client_mmap = xwl_mmap_ref(host_buffer->client_mmap);
if (host_buffer->host_mmap)
host->host_mmap = xwl_mmap_ref(host_buffer->host_mmap);
}
wl_surface_attach(host->proxy, buffer_proxy, x / scale, y / scale);
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;
struct xwl_damage_tracker *t;
int32_t x1, y1, x2, y2;
x1 = x;
y1 = y;
x2 = x + width;
y2 = y + height;
wl_list_for_each(t, &host->damage_trackers, link) {
if (t->x1 < t->x2 && t->y1 < t->y2) {
t->x1 = MIN(x1, t->x1);
t->y1 = MIN(y1, t->y1);
t->x2 = MAX(x2, t->x2);
t->y2 = MAX(y2, t->y2);
} else {
t->x1 = x1;
t->y1 = y1;
t->x2 = x2;
t->y2 = y2;
}
}
// Enclosing rect after scaling and outset by one pixel to account for
// potential filtering.
x1 = (x1 - 1) / scale;
y1 = (y1 - 1) / scale;
x2 = ceil((x2 + 1) / scale);
y2 = ceil((y2 + 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;
if (host->host_mmap && host->client_mmap) {
uint8_t *dst = host->host_mmap->addr + host->host_mmap->offset;
uint8_t *src = host->client_mmap->addr + host->client_mmap->offset;
size_t dst_stride = host->host_mmap->stride;
size_t src_stride = host->client_mmap->stride;
size_t bpp = host->host_mmap->bpp;
int x1 = 0;
int y1 = 0;
int x2 = host->contents_width / host->contents_scale;
int y2 = host->contents_height / host->contents_scale;
int width, height, bytes;
if (!wl_list_empty(&host->host_mmap->damage.link)) {
struct xwl_damage_tracker *t;
int x = 0;
wl_list_for_each(t, &host->damage_trackers, link) {
x++;
if (t == &host->host_mmap->damage) {
if (t->x1 > x1)
x1 = MIN(t->x1, x2);
if (t->y1 > y1)
y1 = MIN(t->y1, y2);
if (t->x2 < x2)
x2 = MAX(t->x2, x1);
if (t->y2 < y2)
y2 = MAX(t->y2, y1);
break;
}
}
}
x1 *= host->contents_scale;
y1 *= host->contents_scale;
x2 *= host->contents_scale;
y2 *= host->contents_scale;
assert(host->client_mmap->bpp == host->host_mmap->bpp);
width = x2 - x1;
height = y2 - y1;
bytes = width * bpp;
dst += y1 * dst_stride + x1 * bpp;
src += y1 * src_stride + x1 * bpp;
if (host->host_mmap->begin_access)
host->host_mmap->begin_access(host->host_mmap->fd);
while (height--) {
memcpy(dst, src, bytes);
dst += dst_stride;
src += src_stride;
}
if (host->host_mmap->end_access)
host->host_mmap->end_access(host->host_mmap->fd);
// Empty damage.
host->host_mmap->damage.x1 = host->host_mmap->damage.x2;
host->host_mmap->damage.y1 = host->host_mmap->damage.y2;
wl_list_remove(&host->host_mmap->damage.link);
wl_list_init(&host->host_mmap->damage.link);
// Limit damage tracking to 4 buffers.
if (wl_list_length(&host->damage_trackers) >= 4) {
struct wl_list *first = host->damage_trackers.next;
wl_list_remove(first);
wl_list_init(first);
}
wl_list_insert(host->damage_trackers.prev, &host->host_mmap->damage.link);
}
if (host->contents_width && host->contents_height) {
double scale = host->xwl->scale * host->contents_scale;
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);
}
}
// 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;
}
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->client_mmap)
xwl_mmap_unref(host->client_mmap);
if (host->host_mmap)
xwl_mmap_unref(host->host_mmap);
while (!wl_list_empty(&host->damage_trackers)) {
struct wl_list *first = host->damage_trackers.next;
wl_list_remove(first);
wl_list_init(first);
}
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->client_mmap = NULL;
host_surface->host_mmap = NULL;
wl_list_init(&host_surface->damage_trackers);
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);
host_surface->viewport = NULL;
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);
if (host->client_mmap)
xwl_mmap_unref(host->client_mmap);
if (host->host_mmap)
xwl_mmap_unref(host->host_mmap);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static int xwl_supported_shm_format(uint32_t format) {
switch (format) {
case WL_SHM_FORMAT_RGB565:
case WL_SHM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_XRGB8888:
return 1;
}
return 0;
}
static size_t xwl_bpp_for_shm_format(uint32_t format) {
switch (format) {
case WL_SHM_FORMAT_RGB565:
return 2;
case WL_SHM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_ABGR8888:
case WL_SHM_FORMAT_XRGB8888:
case WL_SHM_FORMAT_XBGR8888:
return 4;
}
assert(0);
return 0;
}
static uint32_t xwl_gbm_format_for_shm_format(uint32_t format) {
switch (format) {
case WL_SHM_FORMAT_RGB565:
return GBM_FORMAT_RGB565;
case WL_SHM_FORMAT_ARGB8888:
return GBM_FORMAT_ARGB8888;
case WL_SHM_FORMAT_ABGR8888:
return GBM_FORMAT_ABGR8888;
case WL_SHM_FORMAT_XRGB8888:
return GBM_FORMAT_XRGB8888;
case WL_SHM_FORMAT_XBGR8888:
return GBM_FORMAT_XBGR8888;
}
assert(0);
return 0;
}
static uint32_t xwl_drm_format_for_shm_format(int format) {
switch (format) {
case WL_SHM_FORMAT_RGB565:
return WL_DRM_FORMAT_RGB565;
case WL_SHM_FORMAT_ARGB8888:
return WL_DRM_FORMAT_ARGB8888;
case WL_SHM_FORMAT_ABGR8888:
return WL_DRM_FORMAT_ABGR8888;
case WL_SHM_FORMAT_XRGB8888:
return WL_DRM_FORMAT_XRGB8888;
case WL_SHM_FORMAT_XBGR8888:
return WL_DRM_FORMAT_XBGR8888;
}
assert(0);
return 0;
}
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;
size_t size = height * stride;
size_t bpp = xwl_bpp_for_shm_format(format);
host_buffer = malloc(sizeof(*host_buffer));
assert(host_buffer);
host_buffer->width = width;
host_buffer->height = height;
host_buffer->stride = stride;
host_buffer->size = size;
host_buffer->offset = offset;
host_buffer->client_mmap = NULL;
host_buffer->host_mmap = NULL;
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);
switch (host->shm->xwl->shm_driver) {
case SHM_DRIVER_NOOP:
assert(host->proxy);
host_buffer->proxy = wl_shm_pool_create_buffer(host->proxy, offset, width,
height, stride, format);
break;
case SHM_DRIVER_DMABUF: {
struct zwp_linux_buffer_params_v1 *buffer_params;
struct gbm_bo *bo;
int stride0;
int fd;
host_buffer->client_mmap =
xwl_mmap_create(host->fd, size, offset, stride, bpp);
bo = gbm_bo_create(host->shm->xwl->gbm, width, height,
xwl_gbm_format_for_shm_format(format),
GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR);
stride0 = gbm_bo_get_stride(bo);
fd = gbm_bo_get_fd(bo);
buffer_params = zwp_linux_dmabuf_v1_create_params(
host->shm->xwl->linux_dmabuf->internal);
zwp_linux_buffer_params_v1_add(buffer_params, fd, 0, 0, stride0, 0, 0);
host_buffer->proxy = zwp_linux_buffer_params_v1_create_immed(
buffer_params, width, height, xwl_drm_format_for_shm_format(format), 0);
zwp_linux_buffer_params_v1_destroy(buffer_params);
host_buffer->host_mmap =
xwl_mmap_create(fd, height * stride0, 0, stride0, bpp);
host_buffer->host_mmap->begin_access = xwl_dmabuf_begin_access;
host_buffer->host_mmap->end_access = xwl_dmabuf_end_access;
gbm_bo_destroy(bo);
} break;
case SHM_DRIVER_VIRTWL: {
struct virtwl_ioctl_new virtwl_ioctl_new = {
.type = VIRTWL_IOCTL_NEW_ALLOC, .fd = -1, .flags = 0, .size = size};
struct wl_shm_pool *pool;
int rv;
host_buffer->client_mmap =
xwl_mmap_create(host->fd, size, offset, stride, bpp);
rv = ioctl(host->shm->xwl->virtwl_fd, VIRTWL_IOCTL_NEW, &virtwl_ioctl_new);
assert(rv == 0);
pool = wl_shm_create_pool(host->shm->internal, virtwl_ioctl_new.fd, size);
host_buffer->proxy =
wl_shm_pool_create_buffer(pool, 0, width, height, stride, format);
host_buffer->host_mmap =
xwl_mmap_create(virtwl_ioctl_new.fd, size, 0, stride, bpp);
wl_shm_pool_destroy(pool);
} break;
}
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);
if (host->proxy)
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);
if (host->fd >= 0)
close(host->fd);
if (host->proxy)
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->shm = host->shm;
host_shm_pool->fd = -1;
host_shm_pool->proxy = NULL;
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);
switch (host->shm->xwl->shm_driver) {
case SHM_DRIVER_NOOP:
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);
break;
case SHM_DRIVER_DMABUF:
case SHM_DRIVER_VIRTWL:
host_shm_pool->fd = fd;
break;
}
}
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);
if (xwl_supported_shm_format(format))
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 wl_resource *surface_resource,
uint32_t serial) {
struct xwl_host_surface *host_surface =
wl_resource_get_user_data(surface_resource);
host_surface->last_event_serial = serial;
}
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);
if (host->focus_resource)
xwl_set_last_event_serial(host->focus_resource, serial);
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);
if (host->focus_resource)
xwl_set_last_event_serial(host->focus_resource, serial);
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);
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);
wl_keyboard_send_key(host->resource, serial, time, key, state);
if (host->focus_resource)
xwl_set_last_event_serial(host->focus_resource, serial);
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);
if (host->focus_resource)
xwl_set_last_event_serial(host->focus_resource, serial);
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);
if (host->focus_resource)
xwl_set_last_event_serial(host->focus_resource, serial);
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);
if (host->focus_resource)
xwl_set_last_event_serial(host->focus_resource, serial);
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);
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