diff --git a/include/rootston/input.h b/include/rootston/input.h index ddd5a05d..33750d7b 100644 --- a/include/rootston/input.h +++ b/include/rootston/input.h @@ -64,6 +64,17 @@ struct roots_input_event { struct wlr_input_device *device; }; +struct roots_drag_icon { + struct wlr_surface *surface; + struct wl_list link; // roots_input::drag_icons + + int32_t sx; + int32_t sy; + + struct wl_listener surface_destroy; + struct wl_listener surface_commit; +}; + struct roots_touch_point { struct roots_touch *device; int32_t slot; @@ -80,6 +91,7 @@ struct roots_input { struct wlr_xcursor_theme *theme; struct wlr_xcursor *xcursor; struct wlr_seat *wl_seat; + struct wl_list drag_icons; struct wl_client *cursor_client; enum roots_cursor_mode mode; @@ -113,6 +125,7 @@ struct roots_input { struct wl_listener cursor_tool_axis; struct wl_listener cursor_tool_tip; + struct wl_listener pointer_grab_begin; struct wl_list touch_points; struct wl_listener pointer_grab_end; diff --git a/include/rootston/server.h b/include/rootston/server.h index a4eacb7f..8fc6530e 100644 --- a/include/rootston/server.h +++ b/include/rootston/server.h @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #ifdef HAS_XWAYLAND #include diff --git a/include/wlr/interfaces/wlr_data_source.h b/include/wlr/interfaces/wlr_data_source.h deleted file mode 100644 index 821bdea0..00000000 --- a/include/wlr/interfaces/wlr_data_source.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef WLR_INTERFACES_WLR_DATA_SOURCE_H -#define WLR_INTERFACES_WLR_DATA_SOURCE_H - -#include - -struct wlr_data_source_impl { - void (*send)(struct wlr_data_source *data_source, const char *type, int fd); - void (*accepted)(struct wlr_data_source *data_source, const char *type); - void (*cancelled)(struct wlr_data_source *data_source); -}; - -bool wlr_data_source_init(struct wlr_data_source *source, - struct wlr_data_source_impl *impl); -void wlr_data_source_finish(struct wlr_data_source *source); - -#endif diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h new file mode 100644 index 00000000..e1e1e516 --- /dev/null +++ b/include/wlr/types/wlr_data_device.h @@ -0,0 +1,89 @@ +#ifndef WLR_TYPES_WLR_DATA_DEVICE_H +#define WLR_TYPES_WLR_DATA_DEVICE_H + +#include +#include + +extern const struct +wlr_pointer_grab_interface wlr_data_device_pointer_drag_interface; + +extern const struct +wlr_keyboard_grab_interface wlr_data_device_keyboard_drag_interface; + +struct wlr_data_device_manager { + struct wl_global *global; +}; + +struct wlr_data_offer { + struct wl_resource *resource; + struct wlr_data_source *source; + + uint32_t dnd_actions; + enum wl_data_device_manager_dnd_action preferred_dnd_action; + bool in_ask; + + struct wl_listener source_destroy; +}; + +struct wlr_data_source { + struct wl_resource *resource; + struct wlr_data_offer *offer; + struct wlr_seat_handle *seat; + struct wl_array mime_types; + + bool accepted; + + // drag and drop + enum wl_data_device_manager_dnd_action current_dnd_action; + uint32_t dnd_actions; + uint32_t compositor_action; + bool actions_set; + + void (*accept)(struct wlr_data_source *source, uint32_t serial, + const char *mime_type); + void (*send)(struct wlr_data_source *source, const char *mime_type, + int32_t fd); + void (*cancel)(struct wlr_data_source *source); + + struct { + struct wl_signal destroy; + } events; +}; + +struct wlr_drag { + struct wlr_seat_pointer_grab pointer_grab; + struct wlr_seat_keyboard_grab keyboard_grab; + + struct wlr_seat_handle *handle; + struct wlr_seat_handle *focus_handle; + + struct wlr_surface *icon; + struct wlr_surface *focus; + struct wlr_data_source *source; + + struct wl_listener icon_destroy; + struct wl_listener source_destroy; + struct wl_listener handle_unbound; +}; + +/** + * Create a wl data device manager global for this display. + */ +struct wlr_data_device_manager *wlr_data_device_manager_create( + struct wl_display *display); + +/** + * Creates a new wl_data_offer if there is a wl_data_source currently set as the + * seat selection and sends it to the client for this handle, followed by the + * wl_data_device.selection() event. + * If there is no current selection, the wl_data_device.selection() event will + * carry a NULL wl_data_offer. + * If the client does not have a wl_data_device for the seat nothing * will be + * done. + */ +void wlr_seat_handle_send_selection(struct wlr_seat_handle *handle); + +void wlr_seat_set_selection(struct wlr_seat *seat, + struct wlr_data_source *source, uint32_t serial); + +#endif diff --git a/include/wlr/types/wlr_data_device_manager.h b/include/wlr/types/wlr_data_device_manager.h deleted file mode 100644 index 500f8acd..00000000 --- a/include/wlr/types/wlr_data_device_manager.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef WLR_TYPES_WLR_DATA_DEVICE_MANAGER_H -#define WLR_TYPES_WLR_DATA_DEVICE_MANAGER_H - -#include - -struct wlr_data_device_manager { - struct wl_global *global; -}; - -struct wlr_data_device_manager *wlr_data_device_manager_create(struct wl_display *dpy); -void wlr_data_device_manager_destroy(struct wlr_data_device_manager *manager); - -struct wlr_data_device { - struct wlr_seat *seat; - struct wlr_data_source *selection; - struct wl_listener selection_destroyed; - - struct { - struct wl_signal selection_change; - } events; -}; - -void wlr_data_device_set_selection(struct wlr_data_device *manager, - struct wlr_data_source *source); - -#endif diff --git a/include/wlr/types/wlr_data_source.h b/include/wlr/types/wlr_data_source.h deleted file mode 100644 index f54ac0a9..00000000 --- a/include/wlr/types/wlr_data_source.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef WLR_TYPES_WLR_DATA_SOURCE_H -#define WLR_TYPES_WLR_DATA_SOURCE_H - -#include -#include - -struct wlr_data_source_impl; - -struct wlr_data_source { - struct wlr_data_source_impl *impl; - struct wlr_list *types; - void *data; - - struct { - struct wl_signal destroy; - } events; -}; - -void wlr_data_source_send(struct wlr_data_source *src, const char *type, int fd); -void wlr_data_source_accepted(struct wlr_data_source *src, const char *type); -void wlr_data_source_cancelled(struct wlr_data_source *src); - -struct wlr_wl_data_source { - struct wlr_data_source base; - struct wl_resource *resource; -}; - -struct wlr_wl_data_source *wlr_wl_data_source_create( - struct wl_client *client, - uint32_t version, uint32_t id); - -#endif diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index d267924c..f5d5c357 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -75,11 +75,16 @@ struct wlr_seat_pointer_state { struct wlr_seat_handle *focused_handle; struct wlr_surface *focused_surface; - struct wl_listener surface_destroy; - struct wl_listener resource_destroy; - struct wlr_seat_pointer_grab *grab; struct wlr_seat_pointer_grab *default_grab; + + uint32_t button_count; + uint32_t grab_button; + uint32_t grab_serial; + uint32_t grab_time; + + struct wl_listener surface_destroy; + struct wl_listener resource_destroy; }; struct wlr_seat_keyboard { @@ -111,11 +116,16 @@ struct wlr_seat { struct wl_list keyboards; char *name; uint32_t capabilities; - struct wlr_data_device *data_device; + + struct wlr_data_device *data_device; // TODO needed? + struct wlr_data_source *selection_source; + uint32_t selection_serial; struct wlr_seat_pointer_state pointer_state; struct wlr_seat_keyboard_state keyboard_state; + struct wl_listener selection_data_source_destroy; + struct { struct wl_signal client_bound; struct wl_signal client_unbound; @@ -127,6 +137,8 @@ struct wlr_seat { struct wl_signal keyboard_grab_end; struct wl_signal request_set_cursor; + + struct wl_signal selection; } events; void *data; diff --git a/meson.build b/meson.build index adf3a1fa..74bb6dcd 100644 --- a/meson.build +++ b/meson.build @@ -51,6 +51,7 @@ udev = dependency('libudev') pixman = dependency('pixman-1') xcb = dependency('xcb') xcb_composite = dependency('xcb-composite') +xcb_xfixes = dependency('xcb-xfixes') xcb_icccm = dependency('xcb-icccm', required: false) x11_xcb = dependency('x11-xcb') libcap = dependency('libcap', required: false) diff --git a/rootston/cursor.c b/rootston/cursor.c index 2a3a7c25..b55eab3f 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef __linux__ #include #elif __FreeBSD__ @@ -12,6 +13,7 @@ #include #include #include +#include #include "rootston/config.h" #include "rootston/input.h" #include "rootston/desktop.h" @@ -355,6 +357,58 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { (uint32_t)(event->time_usec / 1000), BTN_LEFT, event->state); } +static void handle_drag_icon_destroy(struct wl_listener *listener, void *data) { + struct roots_drag_icon *drag_icon = + wl_container_of(listener, drag_icon, surface_destroy); + wl_list_remove(&drag_icon->link); + wl_list_remove(&drag_icon->surface_destroy.link); + wl_list_remove(&drag_icon->surface_commit.link); + free(drag_icon); +} + +static void handle_drag_icon_commit(struct wl_listener *listener, void *data) { + struct roots_drag_icon *drag_icon = + wl_container_of(listener, drag_icon, surface_commit); + // TODO the spec hints at rules that can determine whether the drag icon is + // mapped here, but it is not completely clear so we need to test more + // toolkits to see how we should interpret the surface state here. + drag_icon->sx += drag_icon->surface->current->sx; + drag_icon->sy += drag_icon->surface->current->sy; +} + +static void handle_pointer_grab_begin(struct wl_listener *listener, + void *data) { + struct roots_input *input = + wl_container_of(listener, input, pointer_grab_begin); + struct wlr_seat_pointer_grab *grab = data; + + if (grab->interface == &wlr_data_device_pointer_drag_interface) { + struct wlr_drag *drag = grab->data; + if (drag->icon) { + struct roots_drag_icon *iter_icon; + wl_list_for_each(iter_icon, &input->drag_icons, link) { + if (iter_icon->surface == drag->icon) { + // already in the list + return; + } + } + + struct roots_drag_icon *drag_icon = + calloc(1, sizeof(struct roots_drag_icon)); + drag_icon->surface = drag->icon; + wl_list_insert(&input->drag_icons, &drag_icon->link); + + wl_signal_add(&drag->icon->events.destroy, + &drag_icon->surface_destroy); + drag_icon->surface_destroy.notify = handle_drag_icon_destroy; + + wl_signal_add(&drag->icon->events.commit, + &drag_icon->surface_commit); + drag_icon->surface_commit.notify = handle_drag_icon_commit; + } + } +} + static void handle_pointer_grab_end(struct wl_listener *listener, void *data) { struct roots_input *input = wl_container_of(listener, input, pointer_grab_end); @@ -428,6 +482,11 @@ void cursor_initialize(struct roots_input *input) { wl_signal_add(&input->wl_seat->events.pointer_grab_end, &input->pointer_grab_end); input->pointer_grab_end.notify = handle_pointer_grab_end; + wl_signal_add(&input->wl_seat->events.pointer_grab_begin, &input->pointer_grab_begin); + input->pointer_grab_begin.notify = handle_pointer_grab_begin; + + wl_list_init(&input->request_set_cursor.link); + wl_signal_add(&input->wl_seat->events.request_set_cursor, &input->request_set_cursor); input->request_set_cursor.notify = handle_request_set_cursor; diff --git a/rootston/input.c b/rootston/input.c index 5dc7d16d..f424485e 100644 --- a/rootston/input.c +++ b/rootston/input.c @@ -124,6 +124,8 @@ struct roots_input *input_create(struct roots_server *server, cursor_load_config(config, input->cursor, input, server->desktop); + wl_list_init(&input->drag_icons); + return input; } diff --git a/rootston/main.c b/rootston/main.c index b80c2efd..2a054e6c 100644 --- a/rootston/main.c +++ b/rootston/main.c @@ -20,11 +20,11 @@ int main(int argc, char **argv) { assert(server.backend = wlr_backend_autocreate(server.wl_display)); assert(server.renderer = wlr_gles2_renderer_create(server.backend)); + server.data_device_manager = + wlr_data_device_manager_create(server.wl_display); wl_display_init_shm(server.wl_display); server.desktop = desktop_create(&server, server.config); server.input = input_create(&server, server.config); - server.data_device_manager = wlr_data_device_manager_create( - server.wl_display); const char *socket = wl_display_add_socket_auto(server.wl_display); if (!socket) { diff --git a/rootston/output.c b/rootston/output.c index 99f70bcf..89e1475b 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -150,6 +150,15 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { render_view(view, desktop, wlr_output, &now); } + struct roots_drag_icon *drag_icon = NULL; + wl_list_for_each(drag_icon, &server->input->drag_icons, link) { + struct wlr_surface *icon = drag_icon->surface; + struct wlr_cursor *cursor = server->input->cursor; + double icon_x = cursor->x + drag_icon->sx; + double icon_y = cursor->y + drag_icon->sy; + render_surface(icon, desktop, wlr_output, &now, icon_x, icon_y, 0); + } + wlr_renderer_end(server->renderer); wlr_output_swap_buffers(wlr_output); diff --git a/types/meson.build b/types/meson.build index bd71dac6..cc21f209 100644 --- a/types/meson.build +++ b/types/meson.build @@ -1,11 +1,10 @@ lib_wlr_types = static_library( 'wlr_types', files( + 'wlr_data_device.c', 'wlr_box.c', 'wlr_compositor.c', 'wlr_cursor.c', - 'wlr_data_device_manager.c', - 'wlr_data_source.c', 'wlr_gamma_control.c', 'wlr_input_device.c', 'wlr_keyboard.c', diff --git a/types/wlr_data_device.c b/types/wlr_data_device.c new file mode 100644 index 00000000..8902c894 --- /dev/null +++ b/types/wlr_data_device.c @@ -0,0 +1,812 @@ +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \ + WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \ + WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) + +static uint32_t data_offer_choose_action(struct wlr_data_offer *offer) { + uint32_t available_actions, preferred_action = 0; + uint32_t source_actions, offer_actions; + + if (wl_resource_get_version(offer->resource) >= + WL_DATA_OFFER_ACTION_SINCE_VERSION) { + offer_actions = offer->dnd_actions; + preferred_action = offer->preferred_dnd_action; + } else { + offer_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + } + + if (wl_resource_get_version(offer->source->resource) >= + WL_DATA_SOURCE_ACTION_SINCE_VERSION) { + source_actions = offer->source->dnd_actions; + } else { + source_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + } + + available_actions = offer_actions & source_actions; + + if (!available_actions) { + return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + } + + if (offer->source->seat && + offer->source->compositor_action & available_actions) { + return offer->source->compositor_action; + } + + // If the dest side has a preferred DnD action, use it + if ((preferred_action & available_actions) != 0) { + return preferred_action; + } + + // Use the first found action, in bit order + return 1 << (ffs(available_actions) - 1); +} + +static void data_offer_update_action(struct wlr_data_offer *offer) { + uint32_t action; + + if (!offer->source) { + return; + } + + action = data_offer_choose_action(offer); + + if (offer->source->current_dnd_action == action) { + return; + } + + offer->source->current_dnd_action = action; + + if (offer->in_ask) { + return; + } + + if (wl_resource_get_version(offer->source->resource) >= + WL_DATA_SOURCE_ACTION_SINCE_VERSION) { + wl_data_source_send_action(offer->source->resource, action); + } + + if (wl_resource_get_version(offer->resource) >= + WL_DATA_OFFER_ACTION_SINCE_VERSION) { + wl_data_offer_send_action(offer->resource, action); + } +} + +static void client_data_source_accept(struct wlr_data_source *source, + uint32_t serial, const char *mime_type) { + wl_data_source_send_target(source->resource, mime_type); +} + +static void client_data_source_send(struct wlr_data_source *source, + const char *mime_type, int32_t fd) { + wl_data_source_send_send(source->resource, mime_type, fd); + close(fd); +} + +static void client_data_source_cancel(struct wlr_data_source *source) { + wl_data_source_send_cancelled(source->resource); +} + + +static void data_offer_accept(struct wl_client *client, + struct wl_resource *resource, uint32_t serial, const char *mime_type) { + struct wlr_data_offer *offer = wl_resource_get_user_data(resource); + + if (!offer->source || offer != offer->source->offer) { + return; + } + + // TODO check that client is currently focused by the input device + + offer->source->accept(offer->source, serial, mime_type); + offer->source->accepted = (mime_type != NULL); +} + +static void data_offer_receive(struct wl_client *client, + struct wl_resource *resource, const char *mime_type, int32_t fd) { + struct wlr_data_offer *offer = wl_resource_get_user_data(resource); + + if (offer->source && offer == offer->source->offer) { + offer->source->send(offer->source, mime_type, fd); + } else { + close(fd); + } +} +static void data_offer_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void data_source_notify_finish(struct wlr_data_source *source) { + if (!source->actions_set) { + return; + } + + if (source->offer->in_ask && wl_resource_get_version(source->resource) >= + WL_DATA_SOURCE_ACTION_SINCE_VERSION) { + wl_data_source_send_action(source->resource, + source->current_dnd_action); + } + + if (wl_resource_get_version(source->resource) >= + WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { + wl_data_source_send_dnd_finished(source->resource); + } + + source->offer = NULL; +} + +static void data_offer_finish(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_data_offer *offer = wl_resource_get_user_data(resource); + + if (!offer->source || offer->source->offer != offer) { + return; + } + + data_source_notify_finish(offer->source); +} + +static void data_offer_set_actions(struct wl_client *client, + struct wl_resource *resource, uint32_t dnd_actions, + uint32_t preferred_action) { + struct wlr_data_offer *offer = wl_resource_get_user_data(resource); + + if (dnd_actions & ~ALL_ACTIONS) { + wl_resource_post_error(offer->resource, + WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK, + "invalid action mask %x", dnd_actions); + return; + } + + if (preferred_action && (!(preferred_action & dnd_actions) || + __builtin_popcount(preferred_action) > 1)) { + wl_resource_post_error(offer->resource, + WL_DATA_OFFER_ERROR_INVALID_ACTION, + "invalid action %x", preferred_action); + return; + } + + offer->dnd_actions = dnd_actions; + offer->preferred_dnd_action = preferred_action; + + data_offer_update_action(offer); +} + +static void data_offer_resource_destroy(struct wl_resource *resource) { + struct wlr_data_offer *offer = wl_resource_get_user_data(resource); + + if (!offer->source) { + goto out; + } + + wl_list_remove(&offer->source_destroy.link); + + if (offer->source->offer != offer) { + goto out; + } + + // If the drag destination has version < 3, wl_data_offer.finish + // won't be called, so do this here as a safety net, because + // we still want the version >=3 drag source to be happy. + if (wl_resource_get_version(offer->resource) < + WL_DATA_OFFER_ACTION_SINCE_VERSION) { + data_source_notify_finish(offer->source); + } else if (offer->source->resource && + wl_resource_get_version(offer->source->resource) >= + WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { + wl_data_source_send_cancelled(offer->source->resource); + } + + offer->source->offer = NULL; +out: + free(offer); +} + +static const struct wl_data_offer_interface data_offer_impl = { + .accept = data_offer_accept, + .receive = data_offer_receive, + .destroy = data_offer_destroy, + .finish = data_offer_finish, + .set_actions = data_offer_set_actions, +}; + +static void handle_offer_source_destroyed(struct wl_listener *listener, + void *data) { + struct wlr_data_offer *offer = + wl_container_of(listener, offer, source_destroy); + + offer->source = NULL; +} + +static struct wlr_data_offer *wlr_data_source_send_offer( + struct wlr_data_source *source, + struct wl_resource *target) { + struct wlr_data_offer *offer = calloc(1, sizeof(struct wlr_data_offer)); + + offer->resource = + wl_resource_create(wl_resource_get_client(target), + &wl_data_offer_interface, + wl_resource_get_version(target), 0); + if (offer->resource == NULL) { + free(offer); + return NULL; + } + + wl_resource_set_implementation(offer->resource, &data_offer_impl, offer, + data_offer_resource_destroy); + + offer->source_destroy.notify = handle_offer_source_destroyed; + wl_signal_add(&source->events.destroy, &offer->source_destroy); + + wl_data_device_send_data_offer(target, offer->resource); + char **p; + wl_array_for_each(p, &source->mime_types) { + wl_data_offer_send_offer(offer->resource, *p); + } + + offer->source = source; + source->offer = offer; + source->accepted = false; + + return offer; +} + + +void wlr_seat_handle_send_selection(struct wlr_seat_handle *handle) { + if (!handle->data_device) { + return; + } + + if (handle->wlr_seat->selection_source) { + struct wlr_data_offer *offer = + wlr_data_source_send_offer(handle->wlr_seat->selection_source, + handle->data_device); + wl_data_device_send_selection(handle->data_device, offer->resource); + } else { + wl_data_device_send_selection(handle->data_device, NULL); + } +} + +static void seat_handle_selection_data_source_destroy( + struct wl_listener *listener, void *data) { + struct wlr_seat *seat = + wl_container_of(listener, seat, selection_data_source_destroy); + + if (seat->keyboard_state.focused_handle && + seat->keyboard_state.focused_surface && + seat->keyboard_state.focused_handle->data_device) { + wl_data_device_send_selection( + seat->keyboard_state.focused_handle->data_device, NULL); + } + + seat->selection_source = NULL; + + wl_signal_emit(&seat->events.selection, seat); +} + +void wlr_seat_set_selection(struct wlr_seat *seat, + struct wlr_data_source *source, uint32_t serial) { + if (seat->selection_source && + seat->selection_serial - serial < UINT32_MAX / 2) { + return; + } + + if (seat->selection_source) { + seat->selection_source->cancel(seat->selection_source); + seat->selection_source = NULL; + wl_list_remove(&seat->selection_data_source_destroy.link); + } + + seat->selection_source = source; + seat->selection_serial = serial; + + struct wlr_seat_handle *focused_handle = + seat->keyboard_state.focused_handle; + + if (focused_handle) { + wlr_seat_handle_send_selection(focused_handle); + } + + wl_signal_emit(&seat->events.selection, seat); + + if (source) { + seat->selection_data_source_destroy.notify = + seat_handle_selection_data_source_destroy; + wl_signal_add(&source->events.destroy, + &seat->selection_data_source_destroy); + } +} + +static void data_device_set_selection(struct wl_client *client, + struct wl_resource *seat_resource, struct wl_resource *source_resource, + uint32_t serial) { + if (!source_resource) { + return; + } + + struct wlr_data_source *source = wl_resource_get_user_data(source_resource); + struct wlr_seat_handle *handle = wl_resource_get_user_data(seat_resource); + + // TODO: store serial and check against incoming serial here + wlr_seat_set_selection(handle->wlr_seat, source, serial); +} + +static void data_device_release(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void drag_handle_seat_unbound(struct wl_listener *listener, void *data) { + struct wlr_drag *drag = wl_container_of(listener, drag, handle_unbound); + struct wlr_seat_handle *unbound_handle = data; + + if (drag->focus_handle == unbound_handle) { + drag->focus_handle = NULL; + wl_list_remove(&drag->handle_unbound.link); + } +} + +static void wlr_drag_set_focus(struct wlr_drag *drag, + struct wlr_surface *surface, double sx, double sy) { + if (drag->focus == surface) { + return; + } + + if (drag->focus_handle && drag->focus_handle->data_device) { + wl_list_remove(&drag->handle_unbound.link); + wl_data_device_send_leave(drag->focus_handle->data_device); + drag->focus_handle = NULL; + drag->focus = NULL; + } + + if (!surface || !surface->resource) { + return; + } + + if (!drag->source && + wl_resource_get_client(surface->resource) != + wl_resource_get_client(drag->handle->wl_resource)) { + return; + } + + if (drag->source && drag->source->offer) { + // unlink the offer from the source + wl_list_remove(&drag->source->offer->source_destroy.link); + drag->source->offer->source = NULL; + drag->source->offer = NULL; + } + + struct wlr_seat_handle *focus_handle = + wlr_seat_handle_for_client(drag->handle->wlr_seat, + wl_resource_get_client(surface->resource)); + + if (!focus_handle || !focus_handle->data_device) { + return; + } + + struct wl_resource *offer_resource = NULL; + if (drag->source) { + drag->source->accepted = false; + struct wlr_data_offer *offer = + wlr_data_source_send_offer(drag->source, focus_handle->data_device); + if (offer == NULL) { + return; + } + + data_offer_update_action(offer); + + if (wl_resource_get_version(offer->resource) >= + WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) { + wl_data_offer_send_source_actions(offer->resource, + drag->source->dnd_actions); + } + + offer_resource = offer->resource; + } + + uint32_t serial = wl_display_next_serial(drag->handle->wlr_seat->display); + + wl_data_device_send_enter(focus_handle->data_device, serial, + surface->resource, wl_fixed_from_double(sx), + wl_fixed_from_double(sy), offer_resource); + + drag->focus = surface; + drag->focus_handle = focus_handle; + drag->handle_unbound.notify = drag_handle_seat_unbound; + wl_signal_add(&focus_handle->wlr_seat->events.client_unbound, + &drag->handle_unbound); +} + +static void wlr_drag_end(struct wlr_drag *drag) { + if (drag->icon) { + wl_list_remove(&drag->icon_destroy.link); + } + + if (drag->source) { + wl_list_remove(&drag->source_destroy.link); + } + + wlr_drag_set_focus(drag, NULL, 0, 0); + wlr_seat_pointer_end_grab(drag->pointer_grab.seat); + wlr_seat_keyboard_end_grab(drag->keyboard_grab.seat); + free(drag); +} + +static void pointer_drag_enter(struct wlr_seat_pointer_grab *grab, + struct wlr_surface *surface, double sx, double sy) { + struct wlr_drag *drag = grab->data; + wlr_drag_set_focus(drag, surface, sx, sy); +} + +static void pointer_drag_motion(struct wlr_seat_pointer_grab *grab, + uint32_t time, double sx, double sy) { + struct wlr_drag *drag = grab->data; + if (drag->focus && drag->focus_handle && drag->focus_handle->data_device) { + wl_data_device_send_motion(drag->focus_handle->data_device, time, + wl_fixed_from_double(sx), wl_fixed_from_double(sy)); + } +} + +static uint32_t pointer_drag_button(struct wlr_seat_pointer_grab *grab, + uint32_t time, uint32_t button, uint32_t state) { + struct wlr_drag *drag = grab->data; + + if (drag->source && + grab->seat->pointer_state.grab_button == button && + state == WL_POINTER_BUTTON_STATE_RELEASED) { + if (drag->focus_handle && drag->focus_handle->data_device && + drag->source->current_dnd_action && + drag->source->accepted) { + wl_data_device_send_drop(drag->focus_handle->data_device); + if (wl_resource_get_version(drag->source->resource) >= + WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) { + wl_data_source_send_dnd_drop_performed( + drag->source->resource); + } + + drag->source->offer->in_ask = + drag->source->current_dnd_action == + WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; + } else if (wl_resource_get_version(drag->source->resource) >= + WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { + wl_data_source_send_cancelled(drag->source->resource); + } + } + + if (grab->seat->pointer_state.button_count == 0 && + state == WL_POINTER_BUTTON_STATE_RELEASED) { + wlr_drag_end(drag); + } + + return 0; +} + +static void pointer_drag_axis(struct wlr_seat_pointer_grab *grab, uint32_t time, + enum wlr_axis_orientation orientation, double value) { +} + +static void pointer_drag_cancel(struct wlr_seat_pointer_grab *grab) { + struct wlr_drag *drag = grab->data; + wlr_drag_end(drag); +} + +const struct +wlr_pointer_grab_interface wlr_data_device_pointer_drag_interface = { + .enter = pointer_drag_enter, + .motion = pointer_drag_motion, + .button = pointer_drag_button, + .axis = pointer_drag_axis, + .cancel = pointer_drag_cancel, +}; + +static void keyboard_drag_enter(struct wlr_seat_keyboard_grab *grab, + struct wlr_surface *surface) { + // nothing has keyboard focus during drags +} + +static void keyboard_drag_key(struct wlr_seat_keyboard_grab *grab, + uint32_t time, uint32_t key, uint32_t state) { + // no keyboard input during drags +} + +static void keyboard_drag_modifiers(struct wlr_seat_keyboard_grab *grab, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) { + // TODO change the dnd action based on what modifier is pressed on the + // keyboard +} + +static void keyboard_drag_cancel(struct wlr_seat_keyboard_grab *grab) { + struct wlr_drag *drag = grab->data; + wlr_drag_end(drag); +} + +const struct +wlr_keyboard_grab_interface wlr_data_device_keyboard_drag_interface = { + .enter = keyboard_drag_enter, + .key = keyboard_drag_key, + .modifiers = keyboard_drag_modifiers, + .cancel = keyboard_drag_cancel, +}; + +static void drag_handle_icon_destroy(struct wl_listener *listener, void *data) { + struct wlr_drag *drag = wl_container_of(listener, drag, icon_destroy); + drag->icon = NULL; +} + +static void drag_handle_drag_source_destroy(struct wl_listener *listener, + void *data) { + struct wlr_drag *drag = wl_container_of(listener, drag, source_destroy); + wlr_drag_end(drag); +} + +static bool seat_handle_start_drag(struct wlr_seat_handle *handle, + struct wlr_data_source *source, struct wlr_surface *icon) { + struct wlr_drag *drag = calloc(1, sizeof(struct wlr_drag)); + if (drag == NULL) { + return false; + } + + struct wlr_seat *seat = handle->wlr_seat; + + if (icon) { + drag->icon = icon; + drag->icon_destroy.notify = drag_handle_icon_destroy; + wl_signal_add(&icon->events.destroy, &drag->icon_destroy); + drag->icon = icon; + } + + if (source) { + drag->source_destroy.notify = drag_handle_drag_source_destroy; + wl_signal_add(&source->events.destroy, &drag->source_destroy); + drag->source = source; + } + + drag->handle = handle; + drag->pointer_grab.data = drag; + drag->pointer_grab.interface = &wlr_data_device_pointer_drag_interface; + + drag->keyboard_grab.data = drag; + drag->keyboard_grab.interface = &wlr_data_device_keyboard_drag_interface; + + wlr_seat_pointer_clear_focus(seat); + + wlr_seat_keyboard_start_grab(seat, &drag->keyboard_grab); + wlr_seat_pointer_start_grab(seat, &drag->pointer_grab); + + // TODO keyboard grab + + return true; +} + +static void data_device_start_drag(struct wl_client *client, + struct wl_resource *handle_resource, + struct wl_resource *source_resource, + struct wl_resource *origin_resource, struct wl_resource *icon_resource, + uint32_t serial) { + struct wlr_seat_handle *handle = wl_resource_get_user_data(handle_resource); + struct wlr_seat *seat = handle->wlr_seat; + struct wlr_surface *origin = wl_resource_get_user_data(origin_resource); + struct wlr_data_source *source = NULL; + struct wlr_surface *icon = NULL; + + bool is_pointer_grab = seat->pointer_state.button_count == 1 && + seat->pointer_state.grab_serial == serial && + seat->pointer_state.focused_surface && + seat->pointer_state.focused_surface == origin; + + if (!is_pointer_grab) { + return; + } + + if (source_resource) { + source = wl_resource_get_user_data(source_resource); + } + + if (icon_resource) { + icon = wl_resource_get_user_data(icon_resource); + } + if (icon) { + if (wlr_surface_set_role(icon, "wl_data_device-icon", + handle_resource, WL_DATA_DEVICE_ERROR_ROLE) < 0) { + return; + } + } + + // TODO touch grab + + if (!seat_handle_start_drag(handle, source, icon)) { + wl_resource_post_no_memory(handle_resource); + } else { + source->seat = handle; + } +} + +static const struct wl_data_device_interface data_device_impl = { + .start_drag = data_device_start_drag, + .set_selection = data_device_set_selection, + .release = data_device_release, +}; + +void data_device_manager_get_data_device(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id, + struct wl_resource *seat_resource) { + struct wlr_seat_handle *handle = wl_resource_get_user_data(seat_resource); + + struct wl_resource *resource = + wl_resource_create(client, + &wl_data_device_interface, + wl_resource_get_version(manager_resource), id); + if (resource == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + + if (handle->data_device != NULL) { + // XXX this is probably a protocol violation, but it simplfies our code + // and it's stupid to create several data devices for the same seat. + wl_resource_destroy(handle->data_device); + } + + handle->data_device = resource; + + wl_resource_set_implementation(resource, &data_device_impl, + handle, NULL); +} + +static void data_source_resource_destroy(struct wl_resource *resource) { + struct wlr_data_source *source = + wl_resource_get_user_data(resource); + char **p; + + wl_signal_emit(&source->events.destroy, source); + + wl_array_for_each(p, &source->mime_types) { + free(*p); + } + + wl_array_release(&source->mime_types); + + free(source); +} + +static void data_source_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void data_source_set_actions(struct wl_client *client, + struct wl_resource *resource, uint32_t dnd_actions) { + struct wlr_data_source *source = + wl_resource_get_user_data(resource); + + if (source->actions_set) { + wl_resource_post_error(source->resource, + WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, + "cannot set actions more than once"); + return; + } + + if (dnd_actions & ~ALL_ACTIONS) { + wl_resource_post_error(source->resource, + WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, + "invalid action mask %x", dnd_actions); + return; + } + + if (source->seat) { + wl_resource_post_error(source->resource, + WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, + "invalid action change after " + "wl_data_device.start_drag"); + return; + } + + source->dnd_actions = dnd_actions; + source->actions_set = true; +} + +static void data_source_offer(struct wl_client *client, + struct wl_resource *resource, const char *mime_type) { + struct wlr_data_source *source = + wl_resource_get_user_data(resource); + char **p; + + p = wl_array_add(&source->mime_types, sizeof *p); + + if (p) { + *p = strdup(mime_type); + } + if (!p || !*p){ + wl_resource_post_no_memory(resource); + } +} + +static struct wl_data_source_interface data_source_impl = { + .offer = data_source_offer, + .destroy = data_source_destroy, + .set_actions = data_source_set_actions, +}; + +static void data_device_manager_create_data_source(struct wl_client *client, + struct wl_resource *resource, uint32_t id) { + struct wlr_data_source *source = calloc(1, sizeof(struct wlr_data_source)); + if (source == NULL) { + wl_resource_post_no_memory(resource); + return; + } + + source->resource = + wl_resource_create(client, &wl_data_source_interface, + wl_resource_get_version(resource), id); + if (source->resource == NULL) { + free(source); + wl_resource_post_no_memory(resource); + return; + } + + source->accept = client_data_source_accept; + source->send = client_data_source_send; + source->cancel = client_data_source_cancel; + + wl_array_init(&source->mime_types); + wl_signal_init(&source->events.destroy); + + wl_resource_set_implementation(source->resource, &data_source_impl, + source, data_source_resource_destroy); +} + +static const struct wl_data_device_manager_interface +data_device_manager_impl = { + .create_data_source = data_device_manager_create_data_source, + .get_data_device = data_device_manager_get_data_device, +}; + +static void data_device_manager_bind(struct wl_client *client, + void *data, uint32_t version, uint32_t id) { + struct wl_resource *resource; + + resource = wl_resource_create(client, + &wl_data_device_manager_interface, + version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, + &data_device_manager_impl, NULL, NULL); +} + +struct wlr_data_device_manager *wlr_data_device_manager_create( + struct wl_display *display) { + struct wlr_data_device_manager *manager = + calloc(1, sizeof(struct wlr_data_device_manager)); + if (manager == NULL) { + wlr_log(L_ERROR, "could not create data device manager"); + return NULL; + } + + manager->global = + wl_global_create(display, &wl_data_device_manager_interface, + 3, NULL, data_device_manager_bind); + + if (!manager->global) { + wlr_log(L_ERROR, "could not create data device manager wl global"); + free(manager); + return NULL; + } + + return manager; +} diff --git a/types/wlr_data_device_manager.c b/types/wlr_data_device_manager.c deleted file mode 100644 index a390e5a2..00000000 --- a/types/wlr_data_device_manager.c +++ /dev/null @@ -1,193 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -static void resource_destroy(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static void data_device_start_drag(struct wl_client *client, - struct wl_resource *res, struct wl_resource *source_resource, - struct wl_resource *origin_res, struct wl_resource *icon_res, - uint32_t serial) { - wlr_log(L_DEBUG, "TODO: implement data_device:start_drag"); - - // Will probably look like this: - // struct wlr_seat_handle *handle = wl_resource_get_user_data(res); - // struct wlr_data_device *device = handle->wlr_seat->data_device; - // struct wlr_data_source *src = wl_resource_get_user_data(src_res); - // struct wlr_surface *origin = wl_resource_get_user_data(origin_res); - // struct wlr_surface *icon; - // if (icon_res) - // icon = wl_resource_get_user_data(icon_res); - // wlr_seat_start_drag(serial, device->seat, src, - // origin, icon); // will set surface roles and emit signal for user -} - -static void data_device_set_selection(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *source_resource, - uint32_t serial) { - if (!source_resource) { - return; - } - - // TODO: serial validation - struct wlr_seat_handle *handle = wl_resource_get_user_data(resource); - struct wlr_data_device *device = handle->wlr_seat->data_device; - struct wlr_data_source *source = wl_resource_get_user_data(source_resource); - wlr_data_device_set_selection(device, source); -} - -static struct wl_data_device_interface data_device_impl = { - .start_drag = data_device_start_drag, - .set_selection = data_device_set_selection, - .release = resource_destroy -}; - -static void data_device_selection_destroy(struct wl_listener *listener, - void *data) { - struct wlr_data_device *device = - wl_container_of(listener, device, selection_destroyed); - assert(data == device->selection); - device->selection = NULL; // make sure no cancel is sent - wlr_data_device_set_selection(device, NULL); -} - -static struct wlr_data_device *seat_ensure_data_device( - struct wlr_data_device_manager *manager, struct wlr_seat *seat) { - if (seat->data_device) { - return seat->data_device; - } - - seat->data_device = calloc(1, sizeof(*seat->data_device)); - if (!seat->data_device) { - wlr_log(L_ERROR, "Failed to allocate wlr_data_device"); - return NULL; - } - - seat->data_device->seat = seat; - wl_signal_init(&seat->data_device->events.selection_change); - seat->data_device->selection_destroyed.notify = - data_device_selection_destroy; - return seat->data_device; -} - -static void data_device_destroy(struct wl_resource *resource) { - struct wlr_seat_handle *handle = wl_resource_get_user_data(resource); - handle->data_device = NULL; -} - -static void data_device_manager_create_data_source(struct wl_client *client, - struct wl_resource *resource, uint32_t id) { - uint32_t version = wl_resource_get_version(resource); - if (!wlr_wl_data_source_create(client, version, id)) { - wlr_log(L_ERROR, "Failed to create wlr_wl_data_source"); - wl_resource_post_no_memory(resource); - return; - } -} - -static void data_device_manager_get_data_device(struct wl_client *client, - struct wl_resource *resource, uint32_t id, - struct wl_resource *seat_resource) { - struct wlr_data_device_manager *manager = - wl_resource_get_user_data(resource); - struct wlr_seat_handle *seat_handle = - wl_resource_get_user_data(seat_resource); - struct wlr_data_device *device; - if (!(device = seat_ensure_data_device(manager, seat_handle->wlr_seat))) { - wl_resource_post_no_memory(resource); - return; - } - - if (seat_handle->data_device) { - // TODO: implement resource lists for seat related handles - // this is a protocol violation, see the todos in wlr_seat.c - wl_resource_destroy(seat_handle->data_device); - } - - seat_handle->data_device = wl_resource_create(client, - &wl_data_device_interface, wl_resource_get_version(resource), id); - if (!seat_handle->data_device) { - wlr_log(L_ERROR, "Failed to create wl_data_device resource"); - wl_resource_post_no_memory(resource); - return; - } - - wl_resource_set_implementation(seat_handle->data_device, &data_device_impl, - seat_handle, data_device_destroy); -} - -struct wl_data_device_manager_interface data_device_manager_impl = { - .create_data_source = data_device_manager_create_data_source, - .get_data_device = data_device_manager_get_data_device -}; - -static void data_device_manager_bind(struct wl_client *client, void *data, - uint32_t version, uint32_t id) { - struct wlr_data_device_manager *manager = data; - assert(client && manager); - - struct wl_resource *resource = wl_resource_create( - client, &wl_data_device_manager_interface, version, id); - if (!resource) { - wlr_log(L_ERROR, "Failed to allocate wl_data_device_manager resource"); - wl_client_post_no_memory(client); - return; - } - wl_resource_set_implementation(resource, &data_device_manager_impl, - manager, NULL); -} - -struct wlr_data_device_manager *wlr_data_device_manager_create( - struct wl_display *display) { - struct wlr_data_device_manager *manager = calloc(1, sizeof(*manager)); - if (!manager) { - wlr_log(L_ERROR, "Failed to allocated wlr_data_device_manager"); - return NULL; - } - - manager->global = - wl_global_create(display, &wl_data_device_manager_interface, 3, manager, - data_device_manager_bind); - - if (!manager->global) { - wlr_log(L_ERROR, "Failed to create global for wlr_data_device_manager"); - free(manager); - return NULL; - } - - return manager; -} - -void wlr_data_device_manager_destroy(struct wlr_data_device_manager *manager) { - if (manager) { - wl_global_destroy(manager->global); - free(manager); - } -} - -void wlr_data_device_set_selection(struct wlr_data_device *device, - struct wlr_data_source *source) { - if (device->selection == source) { - return; - } - - if (device->selection) { - wl_list_remove(&device->selection_destroyed.link); - wlr_data_source_cancelled(device->selection); - } - - device->selection = source; - wl_signal_emit(&device->events.selection_change, device); - - if (source) { - wl_signal_add(&source->events.destroy, &device->selection_destroyed); - } -} diff --git a/types/wlr_data_source.c b/types/wlr_data_source.c deleted file mode 100644 index 98de4f97..00000000 --- a/types/wlr_data_source.c +++ /dev/null @@ -1,140 +0,0 @@ -#define _XOPEN_SOURCE 700 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -bool wlr_data_source_init(struct wlr_data_source *source, - struct wlr_data_source_impl *impl) { - source->impl = impl; - wl_signal_init(&source->events.destroy); - return (source->types = wlr_list_create()); -} - -void wlr_data_source_finish(struct wlr_data_source *source) { - if (source) { - wl_signal_emit(&source->events.destroy, source); - if (source->types) { - wlr_list_foreach(source->types, free); - } - wlr_list_free(source->types); - } -} - -void wlr_data_source_send(struct wlr_data_source *src, const char *type, - int fd) { - assert(src && src->impl && src->impl->send); - src->impl->send(src, type, fd); -} - -void wlr_data_source_accepted(struct wlr_data_source *src, const char *type) { - assert(src && src->impl); - if (src->impl->accepted) { - src->impl->accepted(src, type); - } -} - -void wlr_data_source_cancelled(struct wlr_data_source *src) { - assert(src && src->impl); - if (src->impl->cancelled) { - src->impl->cancelled(src); - } -} - -static void data_source_send(struct wlr_data_source *src, - const char *type, int fd) { - struct wlr_wl_data_source *wl_src = (struct wlr_wl_data_source *) src; - wl_data_source_send_send(wl_src->resource, type, fd); - close(fd); -} - -static void data_source_accepted(struct wlr_data_source *src, - const char *type) { - struct wlr_wl_data_source *wl_src = (struct wlr_wl_data_source *) src; - wl_data_source_send_target(wl_src->resource, type); -} - -static void data_source_cancelled(struct wlr_data_source *src) { - struct wlr_wl_data_source *wl_src = (struct wlr_wl_data_source *) src; - wl_data_source_send_cancelled(wl_src->resource); -} - -static struct wlr_data_source_impl data_source_wl_impl = { - .send = data_source_send, - .accepted = data_source_accepted, - .cancelled = data_source_cancelled, -}; - -static void data_source_offer(struct wl_client *client, - struct wl_resource *resource, - const char *type) { - struct wlr_wl_data_source *src = wl_resource_get_user_data(resource); - char *dtype = strdup(type); - if (!dtype) { - wl_resource_post_no_memory(resource); - return; - } - - wlr_list_add(src->base.types, dtype); -} - -static void data_source_destroy(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static void data_source_set_actions(struct wl_client *client, - struct wl_resource *resource, uint32_t dnd_actions) { - wlr_log(L_DEBUG, "TODO: data source set actions"); -} - -static struct wl_data_source_interface wl_data_source_impl = { - .offer = data_source_offer, - .destroy = data_source_destroy, - .set_actions = data_source_set_actions, -}; - -static void destroy_wl_data_source(struct wl_resource *resource) { - struct wlr_wl_data_source *src = wl_resource_get_user_data(resource); - wlr_data_source_finish(&src->base); - free(src); -} - -struct wlr_wl_data_source *wlr_wl_data_source_create( - struct wl_client *client, - uint32_t version, uint32_t id) { - struct wlr_wl_data_source *src = calloc(1, sizeof(*src)); - if (!src) { - wlr_log(L_ERROR, "Failed to allocator wlr_wl_data_source"); - wl_client_post_no_memory(client); - return NULL; - } - - if (!wlr_data_source_init(&src->base, &data_source_wl_impl)) { - wlr_log(L_ERROR, "Failed to init wlr_wl_data_source"); - wl_client_post_no_memory(client); - goto err; - } - - if (!(src->resource = wl_resource_create(client, &wl_data_source_interface, - version, id))) { - wlr_log(L_ERROR, "Failed to create wl_resource for wlr_wl_data_source"); - wl_client_post_no_memory(client); - goto err; - } - - wl_resource_set_implementation(src->resource, &wl_data_source_impl, src, - destroy_wl_data_source); - return src; - -err: - wlr_data_source_finish(&src->base); - free(src); - return NULL; -} diff --git a/types/wlr_seat.c b/types/wlr_seat.c index e0fb3e8d..5f64bac1 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -6,6 +6,7 @@ #include #include #include +#include static void resource_destroy(struct wl_client *client, struct wl_resource *resource) { @@ -140,6 +141,8 @@ static void wl_seat_get_touch(struct wl_client *client, static void wlr_seat_handle_resource_destroy(struct wl_resource *resource) { struct wlr_seat_handle *handle = wl_resource_get_user_data(resource); + wl_signal_emit(&handle->wlr_seat->events.client_unbound, handle); + if (handle == handle->wlr_seat->pointer_state.focused_handle) { handle->wlr_seat->pointer_state.focused_handle = NULL; } @@ -159,7 +162,6 @@ static void wlr_seat_handle_resource_destroy(struct wl_resource *resource) { if (handle->data_device) { wl_resource_destroy(handle->data_device); } - wl_signal_emit(&handle->wlr_seat->events.client_unbound, handle); wl_list_remove(&handle->link); free(handle); } @@ -302,7 +304,9 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { wl_signal_init(&wlr_seat->events.client_bound); wl_signal_init(&wlr_seat->events.client_unbound); + wl_signal_init(&wlr_seat->events.request_set_cursor); + wl_signal_init(&wlr_seat->events.selection); wl_signal_init(&wlr_seat->events.pointer_grab_begin); wl_signal_init(&wlr_seat->events.pointer_grab_end); @@ -529,8 +533,24 @@ void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, uint32_t time, uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat, uint32_t time, uint32_t button, uint32_t state) { + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (wlr_seat->pointer_state.button_count == 0) { + wlr_seat->pointer_state.grab_button = button; + wlr_seat->pointer_state.grab_time = time; + } + wlr_seat->pointer_state.button_count++; + } else { + wlr_seat->pointer_state.button_count--; + } + struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; - return grab->interface->button(grab, time, button, state); + uint32_t serial = grab->interface->button(grab, time, button, state); + + if (wlr_seat->pointer_state.button_count == 1) { + wlr_seat->pointer_state.grab_serial = serial; + } + + return serial; } void wlr_seat_pointer_notify_axis(struct wlr_seat *wlr_seat, uint32_t time, @@ -667,9 +687,11 @@ void wlr_seat_keyboard_start_grab(struct wlr_seat *wlr_seat, void wlr_seat_keyboard_end_grab(struct wlr_seat *wlr_seat) { struct wlr_seat_keyboard_grab *grab = wlr_seat->keyboard_state.grab; - wlr_seat->keyboard_state.grab = wlr_seat->keyboard_state.default_grab; - wl_signal_emit(&wlr_seat->events.keyboard_grab_end, grab); + if (grab != wlr_seat->keyboard_state.default_grab) { + wlr_seat->keyboard_state.grab = wlr_seat->keyboard_state.default_grab; + wl_signal_emit(&wlr_seat->events.keyboard_grab_end, grab); + } } static void keyboard_surface_destroy_notify(struct wl_listener *listener, @@ -741,6 +763,7 @@ void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat, uint32_t serial = wl_display_next_serial(wlr_seat->display); wl_keyboard_send_enter(handle->keyboard, serial, surface->resource, &keys); + wlr_seat_handle_send_selection(handle); } // reinitialize the focus destroy events diff --git a/types/wlr_surface.c b/types/wlr_surface.c index f8689d8e..72996157 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -269,6 +269,8 @@ static void wlr_surface_move_state(struct wlr_surface *surface, struct wlr_surfa wlr_surface_state_release_buffer(state); wlr_surface_state_set_buffer(state, next->buffer); wlr_surface_state_reset_buffer(next); + state->sx = next->sx; + state->sy = next->sy; update_size = true; } if (update_size) { @@ -406,8 +408,7 @@ static void wlr_surface_commit_pending(struct wlr_surface *surface) { wlr_surface_move_state(surface, surface->pending, surface->current); if (null_buffer_commit) { - wlr_texture_destroy(surface->texture); - surface->texture = NULL; + surface->texture->valid = false; } bool reupload_buffer = oldw != surface->current->buffer_width || diff --git a/xwayland/meson.build b/xwayland/meson.build index d989f6dd..a05ae584 100644 --- a/xwayland/meson.build +++ b/xwayland/meson.build @@ -6,5 +6,12 @@ lib_wlr_xwayland = static_library( 'xwm.c', ), include_directories: wlr_inc, - dependencies: [wayland_server, xcb, xcb_composite, xcb_icccm, pixman], + dependencies: [ + wayland_server, + xcb, + xcb_composite, + xcb_xfixes, + xcb_icccm, + pixman, + ], ) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index c278f1bd..90801d99 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -3,6 +3,7 @@ #endif #include #include +#include #include "wlr/util/log.h" #include "wlr/types/wlr_surface.h" #include "wlr/xwayland.h" @@ -787,6 +788,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland) { WL_EVENT_READABLE, x11_event_handler, xwm); wl_event_source_check(xwm->event_source); + xcb_prefetch_extension_data(xwm->xcb_conn, &xcb_xfixes_id); xwm_get_resources(xwm); xcb_screen_iterator_t screen_iterator = @@ -824,6 +826,25 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland) { xwm->atoms[NET_WM_S0], XCB_CURRENT_TIME)); xcb_flush(xwm->xcb_conn); + xwm->xfixes = xcb_get_extension_data(xwm->xcb_conn, &xcb_xfixes_id); + + if (!xwm->xfixes || !xwm->xfixes->present) { + wlr_log(L_DEBUG, "xfixes not available"); + } + + xcb_xfixes_query_version_cookie_t xfixes_cookie; + xcb_xfixes_query_version_reply_t *xfixes_reply; + xfixes_cookie = + xcb_xfixes_query_version(xwm->xcb_conn, XCB_XFIXES_MAJOR_VERSION, + XCB_XFIXES_MINOR_VERSION); + xfixes_reply = + xcb_xfixes_query_version_reply(xwm->xcb_conn, xfixes_cookie, NULL); + + wlr_log(L_DEBUG, "xfixes version: %d.%d", + xfixes_reply->major_version, xfixes_reply->minor_version); + + free(xfixes_reply); + xwm->surface_create_listener.notify = create_surface_handler; wl_signal_add(&wlr_xwayland->compositor->events.create_surface, &xwm->surface_create_listener); diff --git a/xwayland/xwm.h b/xwayland/xwm.h index be710a1b..a04b1065 100644 --- a/xwayland/xwm.h +++ b/xwayland/xwm.h @@ -85,6 +85,8 @@ struct wlr_xwm { struct wl_list new_surfaces; struct wl_list unpaired_surfaces; + + const xcb_query_extension_reply_t *xfixes; }; void xwm_destroy(struct wlr_xwm *xwm);