diff options
author | Sergey Nazaryev <sergey@nazaryev.ru> | 2020-07-19 20:51:47 +0000 |
---|---|---|
committer | Sergey Nazaryev <sergey@nazaryev.ru> | 2020-08-01 15:03:10 +0000 |
commit | 19960609725f3a682d3174e4e55bc76639d9129e (patch) | |
tree | b7e62f0f6caf66545d923e1dd9d863cd3c8d275e | |
parent | 5e0c749d4b9dcbe064bdc4fd48dee563b3aff599 (diff) | |
download | wterm-19960609725f3a682d3174e4e55bc76639d9129e.zip wterm-19960609725f3a682d3174e4e55bc76639d9129e.tar.gz wterm-19960609725f3a682d3174e4e55bc76639d9129e.tar.bz2 |
Use gtk-primary-selection protocol for PRIMARY clipboard
-rw-r--r-- | include/gtk-primary-selection.xml | 225 | ||||
-rw-r--r-- | src/wterm.c | 111 |
2 files changed, 276 insertions, 60 deletions
diff --git a/include/gtk-primary-selection.xml b/include/gtk-primary-selection.xml new file mode 100644 index 0000000..02cab94 --- /dev/null +++ b/include/gtk-primary-selection.xml @@ -0,0 +1,225 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="gtk_primary_selection"> + <copyright> + Copyright © 2015, 2016 Red Hat + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + </copyright> + + <description summary="Primary selection protocol"> + This protocol provides the ability to have a primary selection device to + match that of the X server. This primary selection is a shortcut to the + common clipboard selection, where text just needs to be selected in order + to allow copying it elsewhere. The de facto way to perform this action + is the middle mouse button, although it is not limited to this one. + + Clients wishing to honor primary selection should create a primary + selection source and set it as the selection through + wp_primary_selection_device.set_selection whenever the text selection + changes. In order to minimize calls in pointer-driven text selection, + it should happen only once after the operation finished. Similarly, + a NULL source should be set when text is unselected. + + wp_primary_selection_offer objects are first announced through the + wp_primary_selection_device.data_offer event. Immediately after this event, + the primary data offer will emit wp_primary_selection_offer.offer events + to let know of the mime types being offered. + + When the primary selection changes, the client with the keyboard focus + will receive wp_primary_selection_device.selection events. Only the client + with the keyboard focus will receive such events with a non-NULL + wp_primary_selection_offer. Across keyboard focus changes, previously + focused clients will receive wp_primary_selection_device.events with a + NULL wp_primary_selection_offer. + + In order to request the primary selection data, the client must pass + a recent serial pertaining to the press event that is triggering the + operation, if the compositor deems the serial valid and recent, the + wp_primary_selection_source.send event will happen in the other end + to let the transfer begin. The client owning the primary selection + should write the requested data, and close the file descriptor + immediately. + + If the primary selection owner client disappeared during the transfer, + the client reading the data will receive a + wp_primary_selection_device.selection event with a NULL + wp_primary_selection_offer, the client should take this as a hint + to finish the reads related to the no longer existing offer. + + The primary selection owner should be checking for errors during + writes, merely cancelling the ongoing transfer if any happened. + </description> + + <interface name="gtk_primary_selection_device_manager" version="1"> + <description summary="X primary selection emulation"> + The primary selection device manager is a singleton global object that + provides access to the primary selection. It allows to create + wp_primary_selection_source objects, as well as retrieving the per-seat + wp_primary_selection_device objects. + </description> + + <request name="create_source"> + <description summary="create a new primary selection source"> + Create a new primary selection source. + </description> + <arg name="id" type="new_id" interface="gtk_primary_selection_source"/> + </request> + + <request name="get_device"> + <description summary="create a new primary selection device"> + Create a new data device for a given seat. + </description> + <arg name="id" type="new_id" interface="gtk_primary_selection_device"/> + <arg name="seat" type="object" interface="wl_seat"/> + </request> + + <request name="destroy" type="destructor"> + <description summary="destroy the primary selection device manager"> + Destroy the primary selection device manager. + </description> + </request> + </interface> + + <interface name="gtk_primary_selection_device" version="1"> + <request name="set_selection"> + <description summary="set the primary selection"> + Replaces the current selection. The previous owner of the primary selection + will receive a wp_primary_selection_source.cancelled event. + + To unset the selection, set the source to NULL. + </description> + <arg name="source" type="object" interface="gtk_primary_selection_source" allow-null="true"/> + <arg name="serial" type="uint" summary="serial of the event that triggered this request"/> + </request> + + <event name="data_offer"> + <description summary="introduce a new wp_primary_selection_offer"> + Introduces a new wp_primary_selection_offer object that may be used + to receive the current primary selection. Immediately following this + event, the new wp_primary_selection_offer object will send + wp_primary_selection_offer.offer events to describe the offered mime + types. + </description> + <arg name="offer" type="new_id" interface="gtk_primary_selection_offer"/> + </event> + + <event name="selection"> + <description summary="advertise a new primary selection"> + The wp_primary_selection_device.selection event is sent to notify the + client of a new primary selection. This event is sent after the + wp_primary_selection.data_offer event introducing this object, and after + the offer has announced its mimetypes through + wp_primary_selection_offer.offer. + + The data_offer is valid until a new offer or NULL is received + or until the client loses keyboard focus. The client must destroy the + previous selection data_offer, if any, upon receiving this event. + </description> + <arg name="id" type="object" interface="gtk_primary_selection_offer" allow-null="true"/> + </event> + + <request name="destroy" type="destructor"> + <description summary="destroy the primary selection device"> + Destroy the primary selection device. + </description> + </request> + </interface> + + <interface name="gtk_primary_selection_offer" version="1"> + <description summary="offer to transfer primary selection contents"> + A wp_primary_selection_offer represents an offer to transfer the contents + of the primary selection clipboard to the client. Similar to + wl_data_offer, the offer also describes the mime types that the source + will transferthat the + data can be converted to and provides the mechanisms for transferring the + data directly to the client. + </description> + + <request name="receive"> + <description summary="request that the data is transferred"> + To transfer the contents of the primary selection clipboard, the client + issues this request and indicates the mime type that it wants to + receive. The transfer happens through the passed file descriptor + (typically created with the pipe system call). The source client writes + the data in the mime type representation requested and then closes the + file descriptor. + + The receiving client reads from the read end of the pipe until EOF and + closes its end, at which point the transfer is complete. + </description> + <arg name="mime_type" type="string"/> + <arg name="fd" type="fd"/> + </request> + + <request name="destroy" type="destructor"> + <description summary="destroy the primary selection offer"> + Destroy the primary selection offer. + </description> + </request> + + <event name="offer"> + <description summary="advertise offered mime type"> + Sent immediately after creating announcing the wp_primary_selection_offer + through wp_primary_selection_device.data_offer. One event is sent per + offered mime type. + </description> + <arg name="mime_type" type="string"/> + </event> + </interface> + + <interface name="gtk_primary_selection_source" version="1"> + <description summary="offer to replace the contents of the primary selection"> + The source side of a wp_primary_selection_offer, it provides a way to + describe the offered data and respond to requests to transfer the + requested contents of the primary selection clipboard. + </description> + + <request name="offer"> + <description summary="add an offered mime type"> + This request adds a mime type to the set of mime types advertised to + targets. Can be called several times to offer multiple types. + </description> + <arg name="mime_type" type="string"/> + </request> + + <request name="destroy" type="destructor"> + <description summary="destroy the primary selection source"> + Destroy the primary selection source. + </description> + </request> + + <event name="send"> + <description summary="send the primary selection contents"> + Request for the current primary selection contents from the client. + Send the specified mime type over the passed file descriptor, then + close it. + </description> + <arg name="mime_type" type="string"/> + <arg name="fd" type="fd"/> + </event> + + <event name="cancelled"> + <description summary="request for primary selection contents was canceled"> + This primary selection source is no longer valid. The client should + clean up and destroy this primary selection source. + </description> + </event> + </interface> +</protocol> diff --git a/src/wterm.c b/src/wterm.c index 5870837..963517e 100644 --- a/src/wterm.c +++ b/src/wterm.c @@ -33,6 +33,7 @@ #include "arg.h" #include "xdg-shell-client-protocol.h" +#include "gtk-primary-selection-client-protocol.h" char *argv0; @@ -252,9 +253,9 @@ typedef struct { struct wl_seat *seat; struct wl_keyboard *keyboard; struct wl_pointer *pointer; - struct wl_data_device_manager *datadevmanager; - struct wl_data_device *datadev; - struct wl_data_offer *seloffer; + struct gtk_primary_selection_device_manager *datadevmanager; + struct gtk_primary_selection_device *datadev; + struct gtk_primary_selection_offer *seloffer; struct wl_surface *surface; struct wl_buffer *buffer; struct xdg_wm_base *xdgshell; @@ -328,7 +329,7 @@ typedef struct { } nb, ne, ob, oe; char *primary; - struct wl_data_source *source; + struct gtk_primary_selection_source *source; int alt; uint32_t tclick1, tclick2; } Selection; @@ -505,21 +506,15 @@ static void xdgsurfconfigure(void *, struct xdg_surface *, uint32_t); static void xdgtopconfigure(void *, struct xdg_toplevel *, int32_t, int32_t, struct wl_array *); static void xdgtopclose(void *, struct xdg_toplevel *); -static void datadevoffer(void *, struct wl_data_device *, - struct wl_data_offer *); -static void datadeventer(void *, struct wl_data_device *, uint32_t, - struct wl_surface *, wl_fixed_t, wl_fixed_t, - struct wl_data_offer *); -static void datadevleave(void *, struct wl_data_device *); -static void datadevmotion(void *, struct wl_data_device *, uint32_t, - wl_fixed_t x, wl_fixed_t y); -static void datadevdrop(void *, struct wl_data_device *); -static void datadevselection(void *, struct wl_data_device *, - struct wl_data_offer *); -static void dataofferoffer(void *, struct wl_data_offer *, const char *); -static void datasrctarget(void *, struct wl_data_source *, const char *); -static void datasrcsend(void *, struct wl_data_source *, const char *, int32_t); -static void datasrccancelled(void *, struct wl_data_source *); + +static void datadevoffer(void *, struct gtk_primary_selection_device *, + struct gtk_primary_selection_offer *); +static void datadevselection(void *, struct gtk_primary_selection_device *, + struct gtk_primary_selection_offer *); +static void dataofferoffer(void *, struct gtk_primary_selection_offer *, const char *); + +static void datasrcsend(void *, struct gtk_primary_selection_source *, const char *, int32_t); +static void datasrccancelled(void *, struct gtk_primary_selection_source *); static void selinit(void); static void selnormalize(void); @@ -553,15 +548,23 @@ static struct wl_keyboard_listener kbdlistener = { static struct wl_pointer_listener ptrlistener = {ptrenter, ptrleave, ptrmotion, ptrbutton, ptraxis}; static struct xdg_wm_base_listener base_listener = {xdgshellping}; -static struct wl_data_device_listener datadevlistener = { - datadevoffer, datadeventer, datadevleave, - datadevmotion, datadevdrop, datadevselection}; static struct xdg_surface_listener xdgsurflistener = {xdgsurfconfigure}; static struct xdg_toplevel_listener xdgtoplevellistener = {xdgtopconfigure, xdgtopclose}; -static struct wl_data_offer_listener dataofferlistener = {dataofferoffer}; -static struct wl_data_source_listener datasrclistener = { - datasrctarget, datasrcsend, datasrccancelled}; + +static struct gtk_primary_selection_device_listener datadevlistener = { + .data_offer = datadevoffer, + .selection = datadevselection +}; + +static struct gtk_primary_selection_offer_listener dataofferlistener = { + .offer = dataofferoffer +}; + +static struct gtk_primary_selection_source_listener datasrclistener = { + .send = datasrcsend, + .cancelled = datasrccancelled +}; /* Globals */ static DC dc; @@ -1041,8 +1044,8 @@ void selpaste(const Arg *dummy) { } } else { pipe(fds); - wl_data_offer_receive(wl.seloffer, "text/plain", fds[1]); - wl_display_flush(wl.dpy); + gtk_primary_selection_offer_receive(wl.seloffer, "text/plain", fds[1]); + wl_display_roundtrip(wl.dpy); close(fds[1]); while ((len = read(fds[0], buf, sizeof buf)) > 0) { selwritebuf(buf, len); @@ -1065,13 +1068,14 @@ void wlsetsel(char *str, uint32_t serial) { sel.primary = str; if (str) { - sel.source = wl_data_device_manager_create_data_source(wl.datadevmanager); - wl_data_source_add_listener(sel.source, &datasrclistener, NULL); - wl_data_source_offer(sel.source, "text/plain; charset=utf-8"); + sel.source = gtk_primary_selection_device_manager_create_source(wl.datadevmanager); + gtk_primary_selection_source_add_listener(sel.source, &datasrclistener, NULL); + gtk_primary_selection_source_offer(sel.source, "text/plain"); + gtk_primary_selection_source_offer(sel.source, "text/plain;charset=utf-8"); } else { sel.source = NULL; } - wl_data_device_set_selection(wl.datadev, sel.source, serial); + gtk_primary_selection_device_set_selection(wl.datadev, sel.source, serial); } void die(const char *errstr, ...) { @@ -3044,9 +3048,10 @@ void wlinit(void) { wl_keyboard_add_listener(wl.keyboard, &kbdlistener, NULL); wl.pointer = wl_seat_get_pointer(wl.seat); wl_pointer_add_listener(wl.pointer, &ptrlistener, NULL); + wl.datadev = - wl_data_device_manager_get_data_device(wl.datadevmanager, wl.seat); - wl_data_device_add_listener(wl.datadev, &datadevlistener, NULL); + gtk_primary_selection_device_manager_get_device(wl.datadevmanager, wl.seat); + gtk_primary_selection_device_add_listener(wl.datadev, &datadevlistener, NULL); /* font */ if (!FcInit()) @@ -3541,9 +3546,9 @@ void regglobal(void *data, struct wl_registry *registry, uint32_t name, wl.shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, "wl_seat") == 0) { wl.seat = wl_registry_bind(registry, name, &wl_seat_interface, 4); - } else if (strcmp(interface, "wl_data_device_manager") == 0) { + } else if (strcmp(interface, "gtk_primary_selection_device_manager") == 0) { wl.datadevmanager = - wl_registry_bind(registry, name, &wl_data_device_manager_interface, 1); + wl_registry_bind(registry, name, >k_primary_selection_device_manager_interface, 1); } else if (strcmp(interface, "wl_output") == 0) { /* bind to outputs so we can get surface enter events */ wl_registry_bind(registry, name, &wl_output_interface, 2); @@ -3876,41 +3881,27 @@ void xdgtopclose(void *data, struct xdg_toplevel *top) { close_shell_and_exit(); } -void datadevoffer(void *data, struct wl_data_device *datadev, - struct wl_data_offer *offer) { - wl_data_offer_add_listener(offer, &dataofferlistener, NULL); +void datadevoffer(void *data, struct gtk_primary_selection_device *datadev, + struct gtk_primary_selection_offer *offer) { + gtk_primary_selection_offer_add_listener(offer, &dataofferlistener, NULL); } -void datadeventer(void *data, struct wl_data_device *datadev, uint32_t serial, - struct wl_surface *surf, wl_fixed_t x, wl_fixed_t y, - struct wl_data_offer *offer) {} - -void datadevleave(void *data, struct wl_data_device *datadev) {} - -void datadevmotion(void *data, struct wl_data_device *datadev, uint32_t time, - wl_fixed_t x, wl_fixed_t y) {} - -void datadevdrop(void *data, struct wl_data_device *datadev) {} - -void datadevselection(void *data, struct wl_data_device *datadev, - struct wl_data_offer *offer) { - if (offer && (uintptr_t)wl_data_offer_get_user_data(offer) == 1) +void datadevselection(void *data, struct gtk_primary_selection_device *datadev, + struct gtk_primary_selection_offer *offer) { + if (offer && (uintptr_t)gtk_primary_selection_offer_get_user_data(offer) == 1) wl.seloffer = offer; else wl.seloffer = NULL; } -void dataofferoffer(void *data, struct wl_data_offer *offer, +void dataofferoffer(void *data, struct gtk_primary_selection_offer *offer, const char *mimetype) { /* mark the offer as usable if it supports plain text */ if (strncmp(mimetype, "text/plain", 10) == 0) - wl_data_offer_set_user_data(offer, (void *)(uintptr_t)1); + gtk_primary_selection_offer_set_user_data(offer, (void *)(uintptr_t)1); } -void datasrctarget(void *data, struct wl_data_source *source, - const char *mimetype) {} - -void datasrcsend(void *data, struct wl_data_source *source, +void datasrcsend(void *data, struct gtk_primary_selection_source *source, const char *mimetype, int32_t fd) { char *buf = sel.primary; int len = strlen(sel.primary); @@ -3922,12 +3913,12 @@ void datasrcsend(void *data, struct wl_data_source *source, close(fd); } -void datasrccancelled(void *data, struct wl_data_source *source) { +void datasrccancelled(void *data, struct gtk_primary_selection_source *source) { if (sel.source == source) { sel.source = NULL; selclear(); } - wl_data_source_destroy(source); + gtk_primary_selection_source_destroy(source); } void run(void) { |