diff --git a/examples/compositor/main.c b/examples/compositor/main.c index 1fe810469..02b75dc63 100644 --- a/examples/compositor/main.c +++ b/examples/compositor/main.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include "shared.h" @@ -31,6 +32,7 @@ struct sample_state { struct wlr_wl_shell *wl_shell; struct wlr_seat *wl_seat; struct wlr_xdg_shell_v6 *xdg_shell; + struct wlr_data_device_manager *data_device_manager; struct wl_resource *focus; struct wl_listener keyboard_bound; int keymap_fd; @@ -130,6 +132,9 @@ static void handle_keyboard_bound(struct wl_listener *listener, void *data) { wl_keyboard_send_keymap(handle->keyboard, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, state->keymap_fd, state->keymap_size); + + if (wl_resource_get_version(handle->keyboard) >= 2) + wl_keyboard_send_repeat_info(handle->keyboard, 660, 25); } int main() { @@ -150,6 +155,7 @@ int main() { wl_compositor_init(compositor.display, &state.compositor, state.renderer); state.wl_shell = wlr_wl_shell_create(compositor.display); state.xdg_shell = wlr_xdg_shell_v6_create(compositor.display); + state.data_device_manager = wlr_data_device_manager_create(compositor.display); state.wl_seat = wlr_seat_create(compositor.display, "seat0"); state.keyboard_bound.notify = handle_keyboard_bound; diff --git a/include/wlr/interfaces/wlr_data_source.h b/include/wlr/interfaces/wlr_data_source.h new file mode 100644 index 000000000..221c80071 --- /dev/null +++ b/include/wlr/interfaces/wlr_data_source.h @@ -0,0 +1,15 @@ +#ifndef _WLR_INTERFACES_DATA_SOURCE_H +#define _WLR_INTERFACES_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_manager.h b/include/wlr/types/wlr_data_device_manager.h new file mode 100644 index 000000000..d7ae46c8d --- /dev/null +++ b/include/wlr/types/wlr_data_device_manager.h @@ -0,0 +1,26 @@ +#ifndef _WLR_TYPES_DATA_DEVICE_MANAGER_H +#define _WLR_TYPES_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 new file mode 100644 index 000000000..63b0fe2a3 --- /dev/null +++ b/include/wlr/types/wlr_data_source.h @@ -0,0 +1,32 @@ +#ifndef _WLR_TYPES_DATA_SOURCE_H +#define _WLR_TYPES_DATA_SOURCE_H + +#include +#include + +struct wlr_data_source_impl; + +struct wlr_data_source { + struct wlr_data_source_impl *impl; + list_t *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 99c092586..69f17b1e2 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -14,6 +14,7 @@ struct wlr_seat_handle { struct wl_resource *pointer; struct wl_resource *keyboard; struct wl_resource *touch; + struct wl_resource *data_device; struct wl_list link; }; @@ -23,6 +24,7 @@ struct wlr_seat { struct wl_list handles; char *name; uint32_t capabilities; + struct wlr_data_device *data_device; struct { struct wl_signal client_bound; diff --git a/types/meson.build b/types/meson.build index bb3135658..82175f6d2 100644 --- a/types/meson.build +++ b/types/meson.build @@ -1,4 +1,6 @@ lib_wlr_types = static_library('wlr_types', files( + 'wlr_data_device_manager.c', + 'wlr_data_source.c', 'wlr_input_device.c', 'wlr_keyboard.c', 'wlr_output.c', diff --git a/types/wlr_data_device_manager.c b/types/wlr_data_device_manager.c new file mode 100644 index 000000000..058839f16 --- /dev/null +++ b/types/wlr_data_device_manager.c @@ -0,0 +1,185 @@ +#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 wl_cb_data_device_start_drag(struct wl_client *client, + struct wl_resource *res, struct wl_resource *src_res, + struct wl_resource *origin_res, struct wl_resource *icon_res, + uint32_t serial) { + wlr_log(L_DEBUG, "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 wl_cb_data_device_set_selection(struct wl_client *client, + struct wl_resource *res, struct wl_resource *src_res, + uint32_t serial) { + // TODO: serial validation + 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); + wlr_data_device_set_selection(device, src); +} + +static struct wl_data_device_interface data_device_impl = { + .start_drag = wl_cb_data_device_start_drag, + .set_selection = wl_cb_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; + } + + if (!(seat->data_device = calloc(1, sizeof(*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 *res) { + struct wlr_seat_handle *handle = wl_resource_get_user_data(res); + handle->data_device = NULL; +} + +static void data_device_manager_create_data_source(struct wl_client *client, + struct wl_resource *res, uint32_t id) { + uint32_t version = wl_resource_get_version(res); + 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(res); + return; + } +} + +static void data_device_manager_get_data_device(struct wl_client *client, + struct wl_resource *res, uint32_t id, struct wl_resource *seat_res) { + struct wlr_data_device_manager *manager = wl_resource_get_user_data(res); + struct wlr_seat_handle *seat_handle = wl_resource_get_user_data(seat_res); + struct wlr_data_device *device; + if (!(device = seat_ensure_data_device(manager, seat_handle->wlr_seat))) { + wl_resource_post_no_memory(res); + 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(res), id); + if (!seat_handle->data_device) { + wlr_log(L_ERROR, "Failed to create wl_data_device resource"); + wl_resource_post_no_memory(res); + 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); + if (version > 3) { + wlr_log(L_ERROR, "Client requested unsupported data_device_manager " + "version, disconnecting"); + wl_client_destroy(client); + return; + } + 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 *dpy) { + 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(dpy, &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) { + return; + } + + // TODO: destroy remaining resources? cancel current selection? + // if this is called why there are still resources active we will + // always get problems + + 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) { + 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 new file mode 100644 index 000000000..542b11354 --- /dev/null +++ b/types/wlr_data_source.c @@ -0,0 +1,130 @@ +#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 = list_create()); +} + +void wlr_data_source_finish(struct wlr_data_source *source) { + if (source) { + wl_signal_emit(&source->events.destroy, source); + if (source->types) { + list_foreach(source->types, free); + } + 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; + } + + list_add(src->base.types, dtype); +} + +static void data_source_destroy(struct wl_client *client, struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static struct wl_data_source_interface wl_data_source_impl = { + .offer = data_source_offer, + .destroy = data_source_destroy +}; + +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; +}