| /* |
| * 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); |
| } |