aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Nazaryev <sergey@nazaryev.ru>2020-07-19 20:51:47 +0000
committerSergey Nazaryev <sergey@nazaryev.ru>2020-08-01 15:03:10 +0000
commit19960609725f3a682d3174e4e55bc76639d9129e (patch)
treeb7e62f0f6caf66545d923e1dd9d863cd3c8d275e
parent5e0c749d4b9dcbe064bdc4fd48dee563b3aff599 (diff)
downloadwterm-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.xml225
-rw-r--r--src/wterm.c111
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, &gtk_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) {