blob: 131dc5c370156c11c1ad1dd645f04f7dbeaad9a6 [file] [log] [blame]
/*
* Copyright 2016 The Chromium Authors. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define WL_HIDE_DEPRECATED
#include <wayland-client.h>
#include <wayland-server.h>
#include <xcb/composite.h>
#include <xcb/xcb.h>
struct xwl;
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 wl_global *host_global;
struct wl_compositor *internal;
};
struct xwl_host_surface {
struct xwl_compositor *compositor;
struct wl_resource *resource;
struct wl_surface *proxy;
int has_contents;
};
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;
};
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 wl_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 wl_global *host_global;
struct wl_shell *internal;
};
struct xwl_host_output {
struct xwl_output *output;
struct wl_resource *resource;
struct wl_output *proxy;
};
struct xwl_output {
struct xwl *xwl;
uint32_t id;
uint32_t version;
struct wl_global *host_global;
struct wl_list link;
};
struct xwl_seat {
struct xwl *xwl;
uint32_t id;
uint32_t version;
struct wl_global *host_global;
struct wl_list link;
};
struct xwl_host_pointer {
struct xwl_seat *seat;
struct wl_resource *resource;
struct wl_pointer *proxy;
};
struct xwl_host_keyboard {
struct xwl_seat *seat;
struct wl_resource *resource;
struct wl_keyboard *proxy;
};
struct xwl_host_touch {
struct xwl_seat *seat;
struct wl_resource *resource;
struct wl_touch *proxy;
};
struct xwl_host_seat {
struct xwl_seat *seat;
struct wl_resource *resource;
struct wl_seat *proxy;
};
struct xwl_window {
struct xwl *xwl;
xcb_window_t id;
uint32_t host_surface_id;
int unpaired;
int x;
int y;
int mapped;
int override_redirect;
struct wl_shell_surface *shell_surface;
struct wl_list link;
};
enum {
ATOM_WM_S0,
ATOM_WM_PROTOCOLS,
ATOM_WM_DELETE_WINDOW,
ATOM_WL_SURFACE_ID,
ATOM_LAST = ATOM_WL_SURFACE_ID,
};
struct xwl {
pthread_barrier_t *barrier;
struct wl_display *display;
struct wl_display *host_display;
struct wl_client *client;
struct xwl_compositor* compositor;
struct xwl_shm* shm;
struct xwl_shell* shell;
struct wl_list outputs;
struct wl_list seats;
struct wl_event_source *display_event_source;
int wm_fd;
xcb_connection_t *connection;
xcb_screen_t *screen;
xcb_window_t window;
struct wl_list windows, unpaired_windows;
struct xwl_window *host_focus_window;
xcb_window_t focus_window;
int32_t scale;
union {
const char *name;
xcb_intern_atom_cookie_t cookie;
xcb_atom_t value;
} atoms[ATOM_LAST + 1];
};
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
static void
xwl_shell_surface_ping(void *data,
struct wl_shell_surface *shell_surface,
uint32_t serial)
{
struct xwl_window *window = wl_shell_surface_get_user_data(shell_surface);
if (getenv("_AWT_XWAYLAND_SEND_DELETE_ON_PING") != NULL) {
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 void
xwl_shell_surface_configure(void *data,
struct wl_shell_surface *shell_surface,
uint32_t edges,
int32_t width,
int32_t height)
{
struct xwl_window *window = wl_shell_surface_get_user_data(shell_surface);
int32_t scale = window->xwl->scale;
uint32_t mask, values[2];
mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
values[0] = width * scale;
values[1] = height * scale;
xcb_configure_window(window->xwl->connection, window->id, mask, values);
}
static void
xwl_shell_surface_popup_done(void *data,
struct wl_shell_surface *shell_surface)
{
}
static const struct wl_shell_surface_listener xwl_shell_surface_listener = {
xwl_shell_surface_ping,
xwl_shell_surface_configure,
xwl_shell_surface_popup_done
};
static void
xwl_window_update(struct xwl_window *window)
{
struct wl_resource *host_resource = NULL;
struct xwl_host_surface *host_surface;
struct wl_surface *surface;
struct xwl *xwl = window->xwl;
struct xwl_window *parent = NULL;
xcb_window_t parent_window = 0;
uint32_t flags = 0;
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 (!window->mapped) {
if (window->shell_surface) {
wl_shell_surface_destroy(window->shell_surface);
window->shell_surface = NULL;
}
return;
}
if (window->shell_surface)
return;
if (!host_resource)
return;
host_surface = wl_resource_get_user_data(host_resource);
assert(host_surface);
assert(xwl->shell);
assert(xwl->shell->internal);
if (window->override_redirect) {
parent_window = xwl->focus_window;
flags = WL_SHELL_SURFACE_TRANSIENT_INACTIVE;
} else {
xcb_get_property_reply_t *reply =
xcb_get_property_reply(xwl->connection,
xcb_get_property(xwl->connection,
0,
window->id,
XCB_ATOM_WM_TRANSIENT_FOR,
XCB_ATOM_WINDOW,
0,
1),
NULL);
if (reply)
parent_window = *((uint32_t *) xcb_get_property_value(reply));
}
if (parent_window) {
struct xwl_window *sibling;
wl_list_for_each(sibling, &xwl->windows, link) {
if (sibling->id == parent_window) {
parent = sibling;
break;
}
}
}
window->shell_surface = wl_shell_get_shell_surface(xwl->shell->internal,
host_surface->proxy);
wl_shell_surface_set_user_data(window->shell_surface, window);
wl_shell_surface_add_listener(window->shell_surface,
&xwl_shell_surface_listener,
window);
if (parent) {
struct wl_resource *parent_resource =
wl_client_get_object(xwl->client, parent->host_surface_id);
struct xwl_host_surface *parent_host_surface =
wl_resource_get_user_data(parent_resource);
wl_shell_surface_set_transient(window->shell_surface,
parent_host_surface->proxy,
(window->x - parent->x) / xwl->scale,
(window->y - parent->y) / xwl->scale,
flags);
} else {
wl_shell_surface_set_toplevel(window->shell_surface);
}
if (host_surface->has_contents)
wl_surface_commit(host_surface->proxy);
}
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 = wl_resource_get_user_data(
buffer_resource);
int32_t scale = host->compositor->xwl->scale;
wl_surface_attach(host->proxy, host_buffer->proxy, x / scale, y / scale);
wl_surface_set_buffer_scale(host->proxy, scale);
}
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);
int32_t scale = host->compositor->xwl->scale;
int32_t x1, y1, x2, y2;
// Round to enclosing rect.
x1 = x / scale;
y1 = y / scale;
x2 = (x + width + scale - 1) / scale;
y2 = (y + height + scale - 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);
}
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)
{
/* Not implemented */
wl_resource_post_no_memory(resource);
}
static void
xwl_host_surface_set_input_region(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region_resource)
{
/* Not implemented */
wl_resource_post_no_memory(resource);
}
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);
wl_surface_commit(host->proxy);
host->has_contents = 1;
}
static void
xwl_host_surface_set_buffer_transform(struct wl_client *client,
struct wl_resource *resource,
int32_t transform)
{
/* Not implemented */
wl_resource_post_no_memory(resource);
}
static void
xwl_host_surface_set_buffer_scale(struct wl_client *client,
struct wl_resource *resource,
int32_t scale)
{
/* Not implemented */
wl_resource_post_no_memory(resource);
}
#ifdef WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION
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)
{
/* Not implemented */
wl_resource_post_no_memory(resource);
}
#endif
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,
#ifdef WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION
xwl_host_surface_damage_buffer
#endif
};
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;
wl_list_for_each(window, &host->compositor->xwl->windows, link) {
if (window->host_surface_id == wl_resource_get_id(resource)) {
window->host_surface_id = 0;
xwl_window_update(window);
break;
}
}
wl_surface_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;
host_surface = malloc(sizeof(*host_surface));
assert(host_surface);
host_surface->compositor = host->compositor;
host_surface->has_contents = 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);
wl_list_for_each(window, &host->compositor->xwl->unpaired_windows, link) {
if (window->host_surface_id == id) {
xwl_window_update(window);
break;
}
}
}
static void
xwl_compositor_create_host_region(struct wl_client *client,
struct wl_resource *resource,
uint32_t id)
{
/* Not implemented */
wl_resource_post_no_memory(resource);
}
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->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_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);
}
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)
{
/* Not implemented */
wl_resource_post_no_memory(resource);
}
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);
wl_output_send_mode(host->resource, flags, width, height, refresh);
}
static void
xwl_output_done(void *data,
struct wl_output *output)
{
struct xwl_host_output *host = wl_output_get_user_data(output);
wl_output_send_done(host->resource);
}
static void
xwl_output_scale(void *data,
struct wl_output *output,
int32_t scale)
{
struct xwl_host_output *host = wl_output_get_user_data(output);
// Always 1 as device scale factor is emulated.
wl_output_send_scale(host->resource, 1);
}
static const struct wl_output_listener xwl_output_listener = {
xwl_output_geometry,
xwl_output_mode,
xwl_output_done,
xwl_output_scale
};
static void
xwl_destroy_host_output(struct wl_resource *resource)
{
struct xwl_host_output *host = wl_resource_get_user_data(resource);
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_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(output->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);
}
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;
int32_t scale = host->seat->xwl->scale;
if (surface_resource)
host_surface = wl_resource_get_user_data(surface_resource);
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_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 = wl_surface_get_user_data(surface);
int32_t scale = host->seat->xwl->scale;
wl_pointer_send_enter(host->resource,
serial,
host_surface->resource,
x * scale,
y * scale);
}
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);
struct xwl_host_surface *host_surface =
surface ? wl_surface_get_user_data(surface) : NULL;
if (!host_surface)
return;
wl_pointer_send_leave(host->resource, serial, host_surface->resource);
}
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);
int32_t 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);
}
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);
int32_t scale = host->seat->xwl->scale;
wl_pointer_send_axis(host->resource, time, axis, value * scale);
}
#ifdef WL_POINTER_FRAME_SINCE_VERSION
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);
}
#endif
#ifdef WL_POINTER_AXIS_SOURCE_SINCE_VERSION
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);
}
#endif
#ifdef WL_POINTER_AXIS_STOP_SINCE_VERSION
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);
}
#endif
#ifdef WL_POINTER_AXIS_DISCRETE_SINCE_VERSION
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);
}
#endif
static const struct wl_pointer_listener xwl_pointer_listener = {
xwl_pointer_enter,
xwl_pointer_leave,
xwl_pointer_motion,
xwl_pointer_button,
xwl_pointer_axis,
#ifdef WL_POINTER_FRAME_SINCE_VERSION
xwl_pointer_frame,
#endif
#ifdef WL_POINTER_AXIS_SOURCE_SINCE_VERSION
xwl_pointer_axis_source,
#endif
#ifdef WL_POINTER_AXIS_DISCRETE_SINCE_VERSION
xwl_pointer_axis_stop,
#endif
#ifdef WL_POINTER_AXIS_DISCRETE_SINCE_VERSION
xwl_pointer_axis_discrete
#endif
};
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_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 = wl_surface_get_user_data(surface);
struct xwl *xwl = host->seat->xwl;
struct xwl_window *window;
wl_keyboard_send_enter(host->resource,
serial,
host_surface->resource,
keys);
wl_list_for_each(window, &xwl->windows, link) {
uint32_t host_resource_id = wl_resource_get_id(host_surface->resource);
if (window->host_surface_id == host_resource_id) {
uint32_t values[1];
xwl->host_focus_window = window;
if (window->override_redirect)
return;
xcb_set_input_focus(xwl->connection,
XCB_INPUT_FOCUS_NONE,
window->id,
XCB_CURRENT_TIME);
values[0] = XCB_STACK_MODE_ABOVE;
xcb_configure_window(xwl->connection,
window->id,
XCB_CONFIG_WINDOW_STACK_MODE,
values);
return;
}
}
}
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);
struct xwl_host_surface *host_surface =
surface ? wl_surface_get_user_data(surface) : NULL;
struct xwl *xwl = host->seat->xwl;
if (!host_surface)
return;
wl_keyboard_send_leave(host->resource, serial, host_surface->resource);
if (xwl->host_focus_window) {
uint32_t host_resource_id = wl_resource_get_id(host_surface->resource);
if (xwl->host_focus_window->host_surface_id == host_resource_id) {
xwl->host_focus_window = NULL;
xcb_set_input_focus(xwl->connection,
XCB_INPUT_FOCUS_NONE,
XCB_NONE,
XCB_CURRENT_TIME);
}
}
}
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);
}
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);
}
#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION
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);
}
#endif
static const struct wl_keyboard_listener xwl_keyboard_listener = {
xwl_keyboard_keymap,
xwl_keyboard_enter,
xwl_keyboard_leave,
xwl_keyboard_key,
xwl_keyboard_modifiers,
#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION
xwl_keyboard_repeat_info
#endif
};
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 = wl_surface_get_user_data(surface);
int32_t scale = host->seat->xwl->scale;
wl_touch_send_down(host->resource,
serial,
time,
host_surface->resource,
id,
x * scale,
y * scale);
}
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_touch_send_up(host->resource, serial, time, id);
}
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);
int32_t 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);
wl_pointer_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
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);
}
static void
xwl_destroy_host_keyboard(struct wl_resource *resource)
{
struct xwl_host_keyboard *host = wl_resource_get_user_data(resource);
wl_keyboard_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
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);
}
static void
xwl_destroy_host_touch(struct wl_resource *resource)
{
struct xwl_host_touch *host = wl_resource_get_user_data(resource);
wl_touch_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
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);
}
#ifdef WL_SEAT_RELEASE_SINCE_VERSION
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);
}
#endif
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,
#ifdef WL_SEAT_RELEASE_SINCE_VERSION
xwl_host_seat_release
#endif
};
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);
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);
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);
}
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 = wl_global_create(xwl->host_display,
&wl_compositor_interface,
compositor->version,
compositor,
xwl_bind_host_compositor);
compositor->internal = wl_registry_bind(registry,
id,
&wl_shell_interface,
compositor->version);
assert(!xwl->compositor);
xwl->compositor = compositor;
} 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 = wl_global_create(xwl->host_display,
&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 = wl_global_create(xwl->host_display,
&wl_shell_interface,
1,
shell,
xwl_bind_host_shell);
shell->internal = wl_registry_bind(registry,
id,
&wl_shell_interface,
1);
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 = wl_global_create(xwl->host_display,
&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;
#ifdef WL_POINTER_FRAME_SINCE_VERSION
seat->version = MIN(5, version);
#else
seat->version = MIN(3, version);
#endif
seat->host_global = wl_global_create(xwl->host_display,
&wl_seat_interface,
seat->version,
seat,
xwl_bind_host_seat);
wl_list_insert(&xwl->seats, &seat->link);
}
}
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) {
wl_global_destroy(xwl->compositor->host_global);
wl_compositor_destroy(xwl->compositor->internal);
free(xwl->compositor);
xwl->compositor = NULL;
return;
}
if (xwl->shm && xwl->shm->id == id) {
wl_global_destroy(xwl->shm->host_global);
free(xwl->shm);
xwl->shm = NULL;
return;
}
if (xwl->shell && xwl->shell->id == id) {
wl_global_destroy(xwl->shell->host_global);
wl_shell_destroy(xwl->shell->internal);
free(xwl->shell);
xwl->shell = NULL;
return;
}
wl_list_for_each(output, &xwl->outputs, link) {
if (output->id == id) {
wl_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) {
wl_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 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 (window->id == id)
return window;
}
wl_list_for_each(window, &xwl->unpaired_windows, link) {
if (window->id == id)
return window;
}
return NULL;
}
static void
xwl_handle_create_notify(struct xwl *xwl,
xcb_create_notify_event_t *event)
{
struct xwl_window *window = malloc(sizeof(struct xwl_window));
assert(window);
window->xwl = xwl;
window->id = event->window;
window->host_surface_id = 0;
window->unpaired = 1;
window->x = event->x;
window->y = event->y;
window->mapped = 0;
window->override_redirect = event->override_redirect;
window->shell_surface = NULL;
wl_list_insert(&xwl->unpaired_windows, &window->link);
if (!event->override_redirect) {
uint32_t values[1];
values[0] = XCB_EVENT_MASK_FOCUS_CHANGE;
xcb_change_window_attributes(xwl->connection,
window->id,
XCB_CW_EVENT_MASK,
values);
}
}
static void
xwl_handle_destroy_notify(struct xwl *xwl,
xcb_destroy_notify_event_t *event)
{
struct xwl_window *window = xwl_lookup_window(xwl, event->window);
if (window) {
if (xwl->host_focus_window == window)
xwl->host_focus_window = NULL;
if (xwl->focus_window == event->window)
xwl->focus_window = 0;
if (window->shell_surface)
wl_shell_surface_destroy(window->shell_surface);
wl_list_remove(&window->link);
free(window);
}
}
static void
xwl_handle_map_request(struct xwl *xwl,
xcb_map_request_event_t *event)
{
xcb_map_window(xwl->connection, event->window);
}
static void
xwl_handle_map_notify(struct xwl *xwl,
xcb_map_notify_event_t *event)
{
struct xwl_window *window = xwl_lookup_window(xwl, event->window);
if (window) {
assert(!window->mapped);
window->mapped = 1;
xwl_window_update(window);
}
}
static void
xwl_handle_unmap_notify(struct xwl *xwl,
xcb_unmap_notify_event_t *event)
{
struct xwl_window *window = xwl_lookup_window(xwl, event->window);
if (window) {
assert(window->mapped);
window->mapped = 0;
xwl_window_update(window);
}
}
static void
xwl_handle_configure_request(struct xwl *xwl,
xcb_configure_request_event_t *event)
{
uint32_t mask = 0, values[16];
int i = 0;
// Keep all managed windows centered horizontally.
if (event->value_mask & (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_WIDTH)) {
values[i++] = xwl->screen->width_in_pixels / 2 - event->width / 2;
mask |= XCB_CONFIG_WINDOW_X;
}
// Keep all managed windows centered vertically.
if (event->value_mask & (XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_HEIGHT)) {
values[i++] = xwl->screen->height_in_pixels / 2 - event->height / 2;
mask |= XCB_CONFIG_WINDOW_Y;
}
if (event->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
values[i++] = event->width;
mask |= XCB_CONFIG_WINDOW_WIDTH;
}
if (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
values[i++] = event->height;
mask |= XCB_CONFIG_WINDOW_HEIGHT;
}
xcb_configure_window(xwl->connection, event->window, mask, values);
}
static void
xwl_handle_configure_notify(struct xwl *xwl,
xcb_configure_notify_event_t *event)
{
struct xwl_window *window = xwl_lookup_window(xwl, event->window);
if (window) {
window->x = event->x;
window->y = event->y;
}
}
static void
xwl_handle_client_message(struct xwl *xwl,
xcb_client_message_event_t *event)
{
struct xwl_window *window;
if (event->type != xwl->atoms[ATOM_WL_SURFACE_ID].value)
return;
wl_list_for_each(window, &xwl->unpaired_windows, link) {
if (window->id == event->window) {
window->host_surface_id = event->data.data32[0];
xwl_window_update(window);
return;
}
}
}
static void
xwl_handle_focus_in(struct xwl *xwl, xcb_focus_in_event_t *event)
{
xwl->focus_window = event->event;
}
static void
xwl_handle_focus_out(struct xwl *xwl, xcb_focus_out_event_t *event)
{
}
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 & ~0x80) {
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_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;
default:
break;
}
free(event);
++count;
}
if ((mask & ~WL_EVENT_WRITABLE) == 0)
xcb_flush(xwl->connection);
return count;
}
static void
xwl_connect(struct xwl *xwl)
{
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;
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_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_SUBSTRUCTURE_NOTIFY |
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
change_attributes_cookie = xcb_change_window_attributes(xwl->connection,
xwl->screen->root,
XCB_CW_EVENT_MASK,
values);
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);
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);
}
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);
}
static int
xwl_handle_display_event(int fd,
uint32_t mask,
void *data)
{
struct xwl *xwl = (struct xwl *) data;
char display_name[9];
int bytes_read = 0;
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_event_source);
xwl->display_event_source = NULL;
close(fd);
pthread_barrier_wait(xwl->barrier);
return 1;
}
void
xwayland_run(pthread_barrier_t *barrier)
{
struct xwl xwl = {
.barrier = barrier,
.display = NULL,
.host_display = NULL,
.client = NULL,
.compositor = NULL,
.shm = NULL,
.shell = NULL,
.display_event_source = NULL,
.connection = NULL,
.screen = NULL,
.window = 0,
.host_focus_window = NULL,
.focus_window = 0,
.scale = 1,
.atoms = {
[ATOM_WM_S0] = {"WM_S0"},
[ATOM_WM_PROTOCOLS] = {"WM_PROTOCOLS"},
[ATOM_WM_DELETE_WINDOW] = {"WM_DELETE_WINDOW"},
[ATOM_WL_SURFACE_ID] = {"WL_SURFACE_ID"},
}
};
struct wl_event_loop *event_loop;
int sv[2], ds[2], wm[2];
pid_t pid;
int rv;
char *scale_str = getenv("_AWT_XWAYLAND_SCALE");
if (scale_str != NULL) {
xwl.scale = atoi(scale_str);
assert(xwl.scale >= 1);
}
xwl.display = wl_display_connect(NULL);
assert(!xwl.display);
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);
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);
/* Wayland connection from Xwayland */
rv = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv);
assert(!rv);
xwl.client = wl_client_create(xwl.host_display, sv[0]);
/* Xwayland display ready socket */
rv = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ds);
assert(!rv);
xwl.display_event_source = wl_event_loop_add_fd(event_loop,
ds[0],
WL_EVENT_READABLE,
xwl_handle_display_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 fd_str[8], display_fd_str[8], wm_fd_str[8];
int fd;
/* SOCK_CLOEXEC closes both ends, so we need to unset
* the flag on the client fd. */
fd = dup(sv[1]);
snprintf(fd_str, sizeof(fd_str), "%d", fd);
setenv("WAYLAND_SOCKET", fd_str, 1);
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);
execlp("Xwayland", "Xwayland",
"-rootless",
"-terminate",
"-displayfd", display_fd_str,
"-wm", wm_fd_str,
NULL);
perror("Xwayland");
_exit(EXIT_FAILURE);
}
close(sv[1]);
close(wm[1]);
do {
wl_display_flush_clients(xwl.host_display);
if (xwl.connection)
xcb_flush(xwl.connection);
wl_display_flush(xwl.display);
} while (wl_event_loop_dispatch(event_loop, -1) != -1);
}