From 58eec36f680ce7ba3e71db88350c4828724b779d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Wed, 19 Jan 2011 14:27:42 -0500 Subject: [PATCH] Add proof-of-concept selection support to terminal Next will be to support mouse selections so we can copy and paste something more interesting than just 'selection data'. --- clients/terminal.c | 87 ++++++++++++++++++++++++++++++++ clients/window.c | 120 +++++++++++++++++++++++++++++++++++++++++++++ clients/window.h | 9 ++++ 3 files changed, 216 insertions(+) diff --git a/clients/terminal.c b/clients/terminal.c index a89a5bf8..ab9bdb3d 100644 --- a/clients/terminal.c +++ b/clients/terminal.c @@ -390,6 +390,11 @@ struct terminal { struct terminal_color color_table[256]; cairo_font_extents_t extents; cairo_scaled_font_t *font_normal, *font_bold; + + uint32_t tag; + struct wl_selection *selection; + struct wl_selection_offer *selection_offer; + uint32_t selection_offer_has_text; }; /* Create default tab stops, every 8 characters */ @@ -1872,6 +1877,88 @@ terminal_data(struct terminal *terminal, const char *data, size_t length) } static void +selection_listener_send(void *data, struct wl_selection *selection, + const char *mime_type, int fd) +{ + struct terminal *terminal = data; + static const char msg[] = "selection data"; + + fprintf(stderr, "selection send, fd is %d\n", fd); + write(fd, msg, sizeof msg); + close(fd); +} + +static void +selection_listener_cancelled(void *data, struct wl_selection *selection) +{ + struct terminal *terminal = data; + + fprintf(stderr, "selection cancelled\n"); + wl_selection_destroy(selection); +} + +static const struct wl_selection_listener selection_listener = { + selection_listener_send, + selection_listener_cancelled +}; + +static gboolean +selection_io_func(GIOChannel *source, GIOCondition condition, gpointer data) +{ + struct terminal *terminal = data; + char buffer[256]; + unsigned int len; + int fd; + + fd = g_io_channel_unix_get_fd(source); + len = read(fd, buffer, sizeof buffer); + fprintf(stderr, "read %d bytes: %.*s\n", len, len, buffer); + + close(fd); + g_source_remove(terminal->tag); + + g_io_channel_unref(source); + + return TRUE; +} + +static int +handle_bound_key(struct terminal *terminal, + struct input *input, uint32_t sym, uint32_t time) +{ + struct wl_shell *shell; + GIOChannel *channel; + int fd; + + switch (sym) { + case XK_C: + shell = display_get_shell(terminal->display); + terminal->selection = wl_shell_create_selection(shell); + wl_selection_add_listener(terminal->selection, + &selection_listener, terminal); + wl_selection_offer(terminal->selection, "text/plain"); + wl_selection_activate(terminal->selection, + input_get_input_device(input), time); + + return 1; + case XK_V: + if (input_offers_mime_type(input, "text/plain")) { + fd = input_receive_mime_type(input, "text/plain"); + channel = g_io_channel_unix_new(fd); + terminal->tag = g_io_add_watch(channel, G_IO_IN, + selection_io_func, + terminal); + } + + return 1; + case XK_X: + /* cut selection; terminal doesn't do cut */ + return 0; + default: + return 0; + } +} + static void key_handler(struct window *window, struct input *input, uint32_t time, uint32_t key, uint32_t sym, uint32_t state, void *data) diff --git a/clients/window.c b/clients/window.c index f05405e6..bd7d3929 100644 --- a/clients/window.c +++ b/clients/window.c @@ -118,6 +118,7 @@ struct input { struct wl_input_device *input_device; struct window *pointer_focus; struct window *keyboard_focus; + struct selection_offer *offer; uint32_t current_pointer_image; uint32_t modifiers; int32_t x, y, sx, sy; @@ -1317,6 +1318,117 @@ display_add_input(struct display *d, uint32_t id) wl_input_device_set_user_data(input->input_device, input); } +struct selection_offer { + struct display *display; + struct wl_selection_offer *offer; + struct wl_array types; + struct input *input; +}; + +int +input_offers_mime_type(struct input *input, const char *type) +{ + struct selection_offer *offer = input->offer; + char **p, **end; + + if (offer == NULL) + return 0; + + end = offer->types.data + offer->types.size; + for (p = offer->types.data; p < end; p++) + if (strcmp(*p, type) == 0) + return 1; + + return 0; +} + +int +input_receive_mime_type(struct input *input, const char *type) +{ + struct selection_offer *offer = input->offer; + int p[2]; + + pipe(p); + /* FIXME: A number of things can go wrong here: the object may + * not be the current selection offer any more (which could + * still work, but the source may have gone away or just + * destroyed its wl_selection) or the offer may not have the + * requested type after all (programmer/client error, + * typically) */ + wl_selection_offer_receive(offer->offer, type, p[1]); + close(p[1]); + + return p[0]; +} + +static void +selection_offer_offer(void *data, + struct wl_selection_offer *selection_offer, + const char *type) +{ + struct selection_offer *offer = data; + + char **p; + + p = wl_array_add(&offer->types, sizeof *p); + if (p) + *p = strdup(type); +}; + +static void +selection_offer_keyboard_focus(void *data, + struct wl_selection_offer *selection_offer, + struct wl_input_device *input_device) +{ + struct selection_offer *offer = data; + struct input *input; + char **p, **end; + + if (input_device == NULL) { + printf("selection offer retracted %p\n", selection_offer); + input = offer->input; + input->offer = NULL; + wl_selection_offer_destroy(selection_offer); + wl_array_release(&offer->types); + free(offer); + return; + } + + input = wl_input_device_get_user_data(input_device); + printf("new selection offer %p:", selection_offer); + + offer->input = input; + input->offer = offer; + end = offer->types.data + offer->types.size; + for (p = offer->types.data; p < end; p++) + printf(" %s", *p); + + printf("\n"); +} + +struct wl_selection_offer_listener selection_offer_listener = { + selection_offer_offer, + selection_offer_keyboard_focus +}; + +static void +add_selection_offer(struct display *d, uint32_t id) +{ + struct selection_offer *offer; + + offer = malloc(sizeof *offer); + if (offer == NULL) + return; + + offer->offer = wl_selection_offer_create(d->display, id); + offer->display = d; + wl_array_init(&offer->types); + offer->input = NULL; + + wl_selection_offer_add_listener(offer->offer, + &selection_offer_listener, offer); +} + static void display_handle_global(struct wl_display *display, uint32_t id, const char *interface, uint32_t version, void *data) @@ -1338,6 +1450,8 @@ display_handle_global(struct wl_display *display, uint32_t id, wl_drm_add_listener(d->drm, &drm_listener, d); } else if (strcmp(interface, "shm") == 0) { d->shm = wl_shm_create(display, id); + } else if (strcmp(interface, "selection_offer") == 0) { + add_selection_offer(d, id); } else if (d->global_handler) { d->global_handler(d, interface, id, version); } @@ -1534,6 +1648,12 @@ display_get_egl_display(struct display *d) return d->dpy; } +struct wl_shell * +display_get_shell(struct display *display) +{ + return display->shell; +} + void display_run(struct display *d) { diff --git a/clients/window.h b/clients/window.h index 9d6a22a8..d67aeda9 100644 --- a/clients/window.h +++ b/clients/window.h @@ -47,6 +47,9 @@ display_get_display(struct display *display); struct wl_compositor * display_get_compositor(struct display *display); +struct wl_shell * +display_get_shell(struct display *display); + #ifdef EGL_NO_DISPLAY EGLDisplay display_get_egl_display(struct display *d); @@ -243,4 +246,10 @@ input_get_modifiers(struct input *input); struct wl_input_device * input_get_input_device(struct input *input); +int +input_offers_mime_type(struct input *input, const char *type); +int +input_receive_mime_type(struct input *input, const char *type); + + #endif