Implement primary selection for Wayland
This commit is contained in:
parent
e99b1be023
commit
d519553581
13
glfw/glfw.py
13
glfw/glfw.py
@ -90,9 +90,12 @@ def init_env(env, pkg_config, at_least_version, test_compile, module='x11'):
|
||||
|
||||
def build_wayland_protocols(env, run_tool, emphasis, newer, dest_dir):
|
||||
for protocol in env.wayland_protocols:
|
||||
src = os.path.join(env.wayland_packagedir, protocol)
|
||||
if not os.path.exists(src):
|
||||
raise SystemExit('The wayland-protocols package on your system is missing the {} protocol definition file'.format(protocol))
|
||||
if os.path.dirname(protocol) == 'wl_protocols':
|
||||
src = os.path.join(dest_dir, protocol)
|
||||
else:
|
||||
src = os.path.join(env.wayland_packagedir, protocol)
|
||||
if not os.path.exists(src):
|
||||
raise SystemExit('The wayland-protocols package on your system is missing the {} protocol definition file'.format(protocol))
|
||||
for ext in 'hc':
|
||||
dest = wayland_protocol_file_name(src, ext)
|
||||
dest = os.path.join(dest_dir, dest)
|
||||
@ -208,8 +211,8 @@ def generate_wrappers(glfw_header):
|
||||
void glfwGetCocoaKeyEquivalent(int glfw_key, int glfw_mods, void* cocoa_key, void* cocoa_mods)
|
||||
void* glfwGetX11Display(void)
|
||||
int32_t glfwGetX11Window(GLFWwindow* window)
|
||||
void glfwSetX11SelectionString(const char* string)
|
||||
const char* glfwGetX11SelectionString(void)
|
||||
void glfwSetPrimarySelectionString(GLFWwindow* window, const char* string)
|
||||
const char* glfwGetPrimarySelectionString(GLFWwindow* window, void)
|
||||
int glfwGetXKBScancode(const char* key_name, int case_sensitive)
|
||||
void glfwRequestWaylandFrameEvent(GLFWwindow *handle, unsigned long long id, GLFWwaylandframecallbackfunc callback)
|
||||
'''.splitlines():
|
||||
|
||||
16
glfw/input.c
vendored
16
glfw/input.c
vendored
@ -1375,6 +1375,22 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle)
|
||||
return _glfwPlatformGetClipboardString();
|
||||
}
|
||||
|
||||
#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND)
|
||||
GLFWAPI void glfwSetPrimarySelectionString(GLFWwindow* handle, const char* string)
|
||||
{
|
||||
assert(string != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
_glfwPlatformSetPrimarySelectionString(string);
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetPrimarySelectionString(GLFWwindow* handle)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return _glfwPlatformGetPrimarySelectionString();
|
||||
}
|
||||
#endif
|
||||
|
||||
GLFWAPI double glfwGetTime(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(0.0);
|
||||
|
||||
4
glfw/internal.h
vendored
4
glfw/internal.h
vendored
@ -624,6 +624,10 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
|
||||
|
||||
void _glfwPlatformSetClipboardString(const char* string);
|
||||
const char* _glfwPlatformGetClipboardString(void);
|
||||
#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND)
|
||||
void _glfwPlatformSetPrimarySelectionString(const char* string);
|
||||
const char* _glfwPlatformGetPrimarySelectionString(void);
|
||||
#endif
|
||||
|
||||
int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode);
|
||||
void _glfwPlatformUpdateGamepadGUID(char* guid);
|
||||
|
||||
@ -72,7 +72,8 @@
|
||||
"unstable/relative-pointer/relative-pointer-unstable-v1.xml",
|
||||
"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml",
|
||||
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml",
|
||||
"unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
|
||||
"unstable/xdg-decoration/xdg-decoration-unstable-v1.xml",
|
||||
"wl_protocols/gtk-primary-selection.xml"
|
||||
],
|
||||
"sources": [
|
||||
"wl_init.c",
|
||||
|
||||
24
glfw/wl_init.c
vendored
24
glfw/wl_init.c
vendored
@ -563,8 +563,11 @@ static void registryHandleGlobal(void* data,
|
||||
_glfw.wl.seatVersion);
|
||||
wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL);
|
||||
}
|
||||
if (_glfw.wl.seat && _glfw.wl.dataDeviceManager && !_glfw.wl.dataDevice) {
|
||||
_glfwSetupWaylandDataDevice();
|
||||
if (_glfw.wl.seat) {
|
||||
if (_glfw.wl.dataDeviceManager && !_glfw.wl.dataDevice) _glfwSetupWaylandDataDevice();
|
||||
if (_glfw.wl.primarySelectionDeviceManager && !_glfw.wl.primarySelectionDevice) {
|
||||
_glfwSetupWaylandPrimarySelectionDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strcmp(interface, "xdg_wm_base") == 0)
|
||||
@ -615,6 +618,16 @@ static void registryHandleGlobal(void* data,
|
||||
_glfwSetupWaylandDataDevice();
|
||||
}
|
||||
}
|
||||
else if (strcmp(interface, "gtk_primary_selection_device_manager") == 0)
|
||||
{
|
||||
_glfw.wl.primarySelectionDeviceManager =
|
||||
wl_registry_bind(registry, name,
|
||||
>k_primary_selection_device_manager_interface,
|
||||
1);
|
||||
if (_glfw.wl.seat && _glfw.wl.primarySelectionDeviceManager && !_glfw.wl.primarySelectionDevice) {
|
||||
_glfwSetupWaylandPrimarySelectionDevice();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -808,6 +821,10 @@ void _glfwPlatformTerminate(void)
|
||||
wl_data_device_destroy(_glfw.wl.dataDevice);
|
||||
if (_glfw.wl.dataDeviceManager)
|
||||
wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager);
|
||||
if (_glfw.wl.primarySelectionDevice)
|
||||
gtk_primary_selection_device_destroy(_glfw.wl.primarySelectionDevice);
|
||||
if (_glfw.wl.primarySelectionDeviceManager)
|
||||
gtk_primary_selection_device_manager_destroy(_glfw.wl.primarySelectionDeviceManager);
|
||||
if (_glfw.wl.registry)
|
||||
wl_registry_destroy(_glfw.wl.registry);
|
||||
if (_glfw.wl.display)
|
||||
@ -817,7 +834,8 @@ void _glfwPlatformTerminate(void)
|
||||
}
|
||||
closeFds(_glfw.wl.eventLoopData.wakeupFds, sizeof(_glfw.wl.eventLoopData.wakeupFds)/sizeof(_glfw.wl.eventLoopData.wakeupFds[0]));
|
||||
free(_glfw.wl.clipboardString); _glfw.wl.clipboardString = NULL;
|
||||
free(_glfw.wl.clipboardSourceString); _glfw.wl.clipboardSourceString = NULL;
|
||||
free(_glfw.wl.primarySelectionString); _glfw.wl.primarySelectionString = NULL;
|
||||
free(_glfw.wl.pasteString); _glfw.wl.pasteString = NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
32
glfw/wl_platform.h
vendored
32
glfw/wl_platform.h
vendored
@ -60,6 +60,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
|
||||
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
||||
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
||||
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
|
||||
#include "wayland-gtk-primary-selection-client-protocol.h"
|
||||
|
||||
#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
|
||||
#define _glfw_dlclose(handle) dlclose(handle)
|
||||
@ -186,11 +187,19 @@ typedef struct _GLFWwindowWayland
|
||||
|
||||
} _GLFWwindowWayland;
|
||||
|
||||
typedef enum _GLFWWaylandOfferType
|
||||
{
|
||||
EXPIRED,
|
||||
CLIPBOARD,
|
||||
DRAG_AND_DROP,
|
||||
PRIMARY_SELECTION
|
||||
}_GLFWWaylandOfferType ;
|
||||
|
||||
typedef struct _GLFWWaylandDataOffer
|
||||
{
|
||||
struct wl_data_offer *id;
|
||||
const char *mime;
|
||||
int offer_type;
|
||||
_GLFWWaylandOfferType offer_type;
|
||||
size_t idx;
|
||||
int is_self_offer;
|
||||
int has_uri_list;
|
||||
@ -199,6 +208,17 @@ typedef struct _GLFWWaylandDataOffer
|
||||
struct wl_surface *surface;
|
||||
} _GLFWWaylandDataOffer;
|
||||
|
||||
typedef struct _GLFWWaylandPrimaryOffer
|
||||
{
|
||||
struct gtk_primary_selection_offer *id;
|
||||
const char *mime;
|
||||
_GLFWWaylandOfferType offer_type;
|
||||
size_t idx;
|
||||
int is_self_offer;
|
||||
int has_uri_list;
|
||||
struct wl_surface *surface;
|
||||
} _GLFWWaylandPrimaryOffer;
|
||||
|
||||
// Wayland-specific global data
|
||||
//
|
||||
typedef struct _GLFWlibraryWayland
|
||||
@ -221,6 +241,9 @@ typedef struct _GLFWlibraryWayland
|
||||
struct wl_data_device_manager* dataDeviceManager;
|
||||
struct wl_data_device* dataDevice;
|
||||
struct wl_data_source* dataSourceForClipboard;
|
||||
struct gtk_primary_selection_device_manager* primarySelectionDeviceManager;
|
||||
struct gtk_primary_selection_device* primarySelectionDevice;
|
||||
struct gtk_primary_selection_source* dataSourceForPrimarySelection;
|
||||
|
||||
int compositorVersion;
|
||||
int seatVersion;
|
||||
@ -261,11 +284,13 @@ typedef struct _GLFWlibraryWayland
|
||||
} egl;
|
||||
|
||||
EventLoopData eventLoopData;
|
||||
char* pasteString;
|
||||
char* clipboardString;
|
||||
char* clipboardSourceString;
|
||||
struct wl_data_offer* clipboardSourceOffer;
|
||||
size_t dataOffersCounter;
|
||||
_GLFWWaylandDataOffer dataOffers[8];
|
||||
char* primarySelectionString;
|
||||
size_t primarySelectionOffersCounter;
|
||||
_GLFWWaylandPrimaryOffer primarySelectionOffers[8];
|
||||
} _GLFWlibraryWayland;
|
||||
|
||||
// Wayland-specific per-monitor data
|
||||
@ -296,4 +321,5 @@ typedef struct _GLFWcursorWayland
|
||||
|
||||
void _glfwAddOutputWayland(uint32_t name, uint32_t version);
|
||||
void _glfwSetupWaylandDataDevice();
|
||||
void _glfwSetupWaylandPrimarySelectionDevice();
|
||||
void animateCursorImage(id_type timer_id, void *data);
|
||||
|
||||
225
glfw/wl_protocols/gtk-primary-selection.xml
Normal file
225
glfw/wl_protocols/gtk-primary-selection.xml
Normal file
@ -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>
|
||||
235
glfw/wl_window.c
vendored
235
glfw/wl_window.c
vendored
@ -1542,13 +1542,13 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
|
||||
}
|
||||
}
|
||||
|
||||
static void _glfwSendClipboardText(void *data, struct wl_data_source *data_source, const char *mime_type, int fd)
|
||||
static void send_text(char *text, int fd)
|
||||
{
|
||||
if (_glfw.wl.clipboardString) {
|
||||
size_t len = strlen(_glfw.wl.clipboardString), pos = 0;
|
||||
if (text) {
|
||||
size_t len = strlen(text), pos = 0;
|
||||
double start = glfwGetTime();
|
||||
while (pos < len && glfwGetTime() - start < 2.0) {
|
||||
ssize_t ret = write(fd, _glfw.wl.clipboardString + pos, len - pos);
|
||||
ssize_t ret = write(fd, text + pos, len - pos);
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR) continue;
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
@ -1564,22 +1564,27 @@ static void _glfwSendClipboardText(void *data, struct wl_data_source *data_sourc
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static char* read_data_offer(struct wl_data_offer *data_offer, const char *mime) {
|
||||
int pipefd[2];
|
||||
if (pipe2(pipefd, O_CLOEXEC) != 0) return NULL;
|
||||
wl_data_offer_receive(data_offer, mime, pipefd[1]);
|
||||
close(pipefd[1]);
|
||||
static void _glfwSendClipboardText(void *data, struct wl_data_source *data_source, const char *mime_type, int fd) {
|
||||
send_text(_glfw.wl.clipboardString, fd);
|
||||
}
|
||||
|
||||
static void _glfwSendPrimarySelectionText(void *data, struct gtk_primary_selection_source *primary_selection_source,
|
||||
const char *mime_type, int fd) {
|
||||
send_text(_glfw.wl.primarySelectionString, fd);
|
||||
}
|
||||
|
||||
static char* read_offer_string(int data_pipe) {
|
||||
wl_display_flush(_glfw.wl.display);
|
||||
size_t sz = 0, capacity = 0;
|
||||
char *buf = NULL;
|
||||
struct pollfd fds;
|
||||
fds.fd = pipefd[0];
|
||||
fds.fd = data_pipe;
|
||||
fds.events = POLLIN;
|
||||
double start = glfwGetTime();
|
||||
#define bail(...) { \
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, __VA_ARGS__); \
|
||||
free(buf); buf = NULL; \
|
||||
close(pipefd[0]); \
|
||||
close(data_pipe); \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
@ -1599,12 +1604,12 @@ static char* read_data_offer(struct wl_data_offer *data_offer, const char *mime)
|
||||
bail("Wayland: Failed to allocate memory to read clipboard data");
|
||||
}
|
||||
}
|
||||
ret = read(pipefd[0], buf + sz, capacity - sz - 1);
|
||||
ret = read(data_pipe, buf + sz, capacity - sz - 1);
|
||||
if (ret == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) continue;
|
||||
bail("Wayland: Failed to read clipboard data from pipe with error: %s", strerror(errno));
|
||||
}
|
||||
if (ret == 0) { close(pipefd[0]); buf[sz] = 0; return buf; }
|
||||
if (ret == 0) { close(data_pipe); buf[sz] = 0; return buf; }
|
||||
sz += ret;
|
||||
start = glfwGetTime();
|
||||
}
|
||||
@ -1613,13 +1618,20 @@ static char* read_data_offer(struct wl_data_offer *data_offer, const char *mime)
|
||||
|
||||
}
|
||||
|
||||
static const char* _glfwReceiveClipboardText(struct wl_data_offer *data_offer, const char *mime)
|
||||
{
|
||||
if (_glfw.wl.clipboardSourceOffer == data_offer && _glfw.wl.clipboardSourceString)
|
||||
return _glfw.wl.clipboardSourceString;
|
||||
free(_glfw.wl.clipboardSourceString);
|
||||
_glfw.wl.clipboardSourceString = read_data_offer(data_offer, mime);
|
||||
return _glfw.wl.clipboardSourceString;
|
||||
static char* read_primary_selection_offer(struct gtk_primary_selection_offer *primary_selection_offer, const char *mime) {
|
||||
int pipefd[2];
|
||||
if (pipe2(pipefd, O_CLOEXEC) != 0) return NULL;
|
||||
gtk_primary_selection_offer_receive(primary_selection_offer, mime, pipefd[1]);
|
||||
close(pipefd[1]);
|
||||
return read_offer_string(pipefd[0]);
|
||||
}
|
||||
|
||||
static char* read_data_offer(struct wl_data_offer *data_offer, const char *mime) {
|
||||
int pipefd[2];
|
||||
if (pipe2(pipefd, O_CLOEXEC) != 0) return NULL;
|
||||
wl_data_offer_receive(data_offer, mime, pipefd[1]);
|
||||
close(pipefd[1]);
|
||||
return read_offer_string(pipefd[0]);
|
||||
}
|
||||
|
||||
static void data_source_canceled(void *data, struct wl_data_source *wl_data_source) {
|
||||
@ -1628,6 +1640,12 @@ static void data_source_canceled(void *data, struct wl_data_source *wl_data_sour
|
||||
wl_data_source_destroy(wl_data_source);
|
||||
}
|
||||
|
||||
static void primary_selection_source_canceled(void *data, struct gtk_primary_selection_source *primary_selection_source) {
|
||||
if (_glfw.wl.dataSourceForPrimarySelection == primary_selection_source)
|
||||
_glfw.wl.dataSourceForPrimarySelection = NULL;
|
||||
gtk_primary_selection_source_destroy(primary_selection_source);
|
||||
}
|
||||
|
||||
static void data_source_target(void *data, struct wl_data_source *wl_data_source, const char* mime) {
|
||||
}
|
||||
|
||||
@ -1637,6 +1655,11 @@ const static struct wl_data_source_listener data_source_listener = {
|
||||
.target = data_source_target,
|
||||
};
|
||||
|
||||
const static struct gtk_primary_selection_source_listener primary_selection_source_listener = {
|
||||
.send = _glfwSendPrimarySelectionText,
|
||||
.cancelled = primary_selection_source_canceled,
|
||||
};
|
||||
|
||||
static void prune_unclaimed_data_offers() {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].id && !_glfw.wl.dataOffers[i].offer_type) {
|
||||
@ -1646,29 +1669,63 @@ static void prune_unclaimed_data_offers() {
|
||||
}
|
||||
}
|
||||
|
||||
static void prune_unclaimed_primary_selection_offers() {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.primarySelectionOffers); i++) {
|
||||
if (_glfw.wl.primarySelectionOffers[i].id && !_glfw.wl.dataOffers[i].offer_type) {
|
||||
gtk_primary_selection_offer_destroy(_glfw.wl.primarySelectionOffers[i].id);
|
||||
memset(_glfw.wl.primarySelectionOffers + i, 0, sizeof(_glfw.wl.primarySelectionOffers[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mark_selection_offer(void *data, struct wl_data_device *data_device, struct wl_data_offer *data_offer)
|
||||
{
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].id == data_offer) {
|
||||
_glfw.wl.dataOffers[i].offer_type = 1;
|
||||
} else if (_glfw.wl.dataOffers[i].offer_type == 1) {
|
||||
_glfw.wl.dataOffers[i].offer_type = 0; // previous selection offer
|
||||
_glfw.wl.dataOffers[i].offer_type = CLIPBOARD;
|
||||
} else if (_glfw.wl.dataOffers[i].offer_type == CLIPBOARD) {
|
||||
_glfw.wl.dataOffers[i].offer_type = EXPIRED; // previous selection offer
|
||||
}
|
||||
}
|
||||
prune_unclaimed_data_offers();
|
||||
}
|
||||
|
||||
static void mark_primary_selection_offer(void *data, struct gtk_primary_selection_device* primary_selection_device,
|
||||
struct gtk_primary_selection_offer *primary_selection_offer) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.primarySelectionOffers); i++) {
|
||||
if (_glfw.wl.primarySelectionOffers[i].id == primary_selection_offer) {
|
||||
_glfw.wl.primarySelectionOffers[i].offer_type = PRIMARY_SELECTION;
|
||||
} else if (_glfw.wl.primarySelectionOffers[i].offer_type == PRIMARY_SELECTION) {
|
||||
_glfw.wl.primarySelectionOffers[i].offer_type = EXPIRED; // previous selection offer
|
||||
}
|
||||
}
|
||||
prune_unclaimed_primary_selection_offers();
|
||||
}
|
||||
|
||||
static void set_offer_mimetype(struct _GLFWWaylandDataOffer* offer, const char* mime) {
|
||||
if (strcmp(mime, "text/plain;charset=utf-8") == 0)
|
||||
offer->mime = "text/plain;charset=utf-8";
|
||||
else if (!offer->mime && strcmp(mime, "text/plain") == 0)
|
||||
offer->mime = "text/plain";
|
||||
else if (strcmp(mime, clipboard_mime()) == 0)
|
||||
offer->is_self_offer = 1;
|
||||
else if (strcmp(mime, URI_LIST_MIME) == 0)
|
||||
offer->has_uri_list = 1;
|
||||
}
|
||||
|
||||
static void handle_offer_mimetype(void *data, struct wl_data_offer* id, const char *mime) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].id == id) {
|
||||
if (strcmp(mime, "text/plain;charset=utf-8") == 0)
|
||||
_glfw.wl.dataOffers[i].mime = "text/plain;charset=utf-8";
|
||||
else if (!_glfw.wl.dataOffers[i].mime && strcmp(mime, "text/plain") == 0)
|
||||
_glfw.wl.dataOffers[i].mime = "text/plain";
|
||||
else if (strcmp(mime, clipboard_mime()) == 0)
|
||||
_glfw.wl.dataOffers[i].is_self_offer = 1;
|
||||
else if (strcmp(mime, URI_LIST_MIME) == 0)
|
||||
_glfw.wl.dataOffers[i].has_uri_list = 1;
|
||||
set_offer_mimetype(&_glfw.wl.dataOffers[i], mime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_primary_selection_offer_mimetype(void *data, struct gtk_primary_selection_offer* id, const char *mime) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.primarySelectionOffers); i++) {
|
||||
if (_glfw.wl.primarySelectionOffers[i].id == id) {
|
||||
set_offer_mimetype((struct _GLFWWaylandDataOffer*)&_glfw.wl.primarySelectionOffers[i], mime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1699,6 +1756,10 @@ static const struct wl_data_offer_listener data_offer_listener = {
|
||||
.action = data_offer_action,
|
||||
};
|
||||
|
||||
static const struct gtk_primary_selection_offer_listener primary_selection_offer_listener = {
|
||||
.offer = handle_primary_selection_offer_mimetype,
|
||||
};
|
||||
|
||||
static void handle_data_offer(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *id) {
|
||||
size_t smallest_idx = SIZE_MAX, pos = 0;
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
@ -1720,15 +1781,36 @@ end:
|
||||
wl_data_offer_add_listener(id, &data_offer_listener, NULL);
|
||||
}
|
||||
|
||||
static void handle_primary_selection_offer(void *data, struct gtk_primary_selection_device *gtk_primary_selection_device, struct gtk_primary_selection_offer *id) {
|
||||
size_t smallest_idx = SIZE_MAX, pos = 0;
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.primarySelectionOffers); i++) {
|
||||
if (_glfw.wl.primarySelectionOffers[i].idx && _glfw.wl.primarySelectionOffers[i].idx < smallest_idx) {
|
||||
smallest_idx = _glfw.wl.primarySelectionOffers[i].idx;
|
||||
pos = i;
|
||||
}
|
||||
if (_glfw.wl.primarySelectionOffers[i].id == NULL) {
|
||||
_glfw.wl.primarySelectionOffers[i].id = id;
|
||||
_glfw.wl.primarySelectionOffers[i].idx = ++_glfw.wl.primarySelectionOffersCounter;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
if (_glfw.wl.primarySelectionOffers[pos].id) gtk_primary_selection_offer_destroy(_glfw.wl.primarySelectionOffers[pos].id);
|
||||
memset(_glfw.wl.primarySelectionOffers + pos, 0, sizeof(_glfw.wl.primarySelectionOffers[0]));
|
||||
_glfw.wl.primarySelectionOffers[pos].id = id;
|
||||
_glfw.wl.primarySelectionOffers[pos].idx = ++_glfw.wl.primarySelectionOffersCounter;
|
||||
end:
|
||||
gtk_primary_selection_offer_add_listener(id, &primary_selection_offer_listener, NULL);
|
||||
}
|
||||
|
||||
static void drag_enter(void *data, struct wl_data_device *wl_data_device, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].id == id) {
|
||||
_glfw.wl.dataOffers[i].offer_type = 2;
|
||||
_glfw.wl.dataOffers[i].offer_type = DRAG_AND_DROP;
|
||||
_glfw.wl.dataOffers[i].surface = surface;
|
||||
const char *mime = _glfw.wl.dataOffers[i].has_uri_list ? URI_LIST_MIME : NULL;
|
||||
wl_data_offer_accept(id, serial, mime);
|
||||
} else if (_glfw.wl.dataOffers[i].offer_type == 2) {
|
||||
_glfw.wl.dataOffers[i].offer_type = 0; // previous drag offer
|
||||
} else if (_glfw.wl.dataOffers[i].offer_type == DRAG_AND_DROP) {
|
||||
_glfw.wl.dataOffers[i].offer_type = EXPIRED; // previous drag offer
|
||||
}
|
||||
}
|
||||
prune_unclaimed_data_offers();
|
||||
@ -1736,18 +1818,16 @@ static void drag_enter(void *data, struct wl_data_device *wl_data_device, uint32
|
||||
|
||||
static void drag_leave(void *data, struct wl_data_device *wl_data_device) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].offer_type == 2) {
|
||||
if (_glfw.wl.dataOffers[i].offer_type == DRAG_AND_DROP) {
|
||||
wl_data_offer_destroy(_glfw.wl.dataOffers[i].id);
|
||||
memset(_glfw.wl.dataOffers + i, 0, sizeof(_glfw.wl.dataOffers[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void drop(void *data, struct wl_data_device *wl_data_device) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].offer_type == 2) {
|
||||
if (_glfw.wl.dataOffers[i].offer_type == DRAG_AND_DROP) {
|
||||
char *uri_list = read_data_offer(_glfw.wl.dataOffers[i].id, URI_LIST_MIME);
|
||||
if (uri_list) {
|
||||
wl_data_offer_finish(_glfw.wl.dataOffers[i].id);
|
||||
@ -1789,20 +1869,38 @@ const static struct wl_data_device_listener data_device_listener = {
|
||||
.leave = drag_leave,
|
||||
};
|
||||
|
||||
const static struct gtk_primary_selection_device_listener primary_selection_device_listener = {
|
||||
.data_offer = handle_primary_selection_offer,
|
||||
.selection = mark_primary_selection_offer,
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
copy_callback_done(void *data, struct wl_callback *callback, uint32_t serial) {
|
||||
clipboard_copy_callback_done(void *data, struct wl_callback *callback, uint32_t serial) {
|
||||
if (_glfw.wl.dataDevice && data == (void*)_glfw.wl.dataSourceForClipboard) {
|
||||
wl_data_device_set_selection(_glfw.wl.dataDevice, data, serial);
|
||||
}
|
||||
wl_callback_destroy(callback);
|
||||
}
|
||||
|
||||
static void
|
||||
primary_selection_copy_callback_done(void *data, struct wl_callback *callback, uint32_t serial) {
|
||||
if (_glfw.wl.primarySelectionDevice && data == (void*)_glfw.wl.dataSourceForPrimarySelection) {
|
||||
gtk_primary_selection_device_set_selection(_glfw.wl.primarySelectionDevice, data, serial);
|
||||
}
|
||||
wl_callback_destroy(callback);
|
||||
}
|
||||
|
||||
void _glfwSetupWaylandDataDevice() {
|
||||
_glfw.wl.dataDevice = wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat);
|
||||
if (_glfw.wl.dataDevice) wl_data_device_add_listener(_glfw.wl.dataDevice, &data_device_listener, NULL);
|
||||
}
|
||||
|
||||
void _glfwSetupWaylandPrimarySelectionDevice() {
|
||||
_glfw.wl.primarySelectionDevice = gtk_primary_selection_device_manager_get_device(_glfw.wl.primarySelectionDeviceManager, _glfw.wl.seat);
|
||||
if (_glfw.wl.primarySelectionDevice) gtk_primary_selection_device_add_listener(_glfw.wl.primarySelectionDevice, &primary_selection_device_listener, NULL);
|
||||
}
|
||||
|
||||
static inline GLFWbool _glfwEnsureDataDevice() {
|
||||
if (!_glfw.wl.dataDeviceManager)
|
||||
{
|
||||
@ -1851,16 +1949,67 @@ void _glfwPlatformSetClipboardString(const char* string)
|
||||
wl_data_source_offer(_glfw.wl.dataSourceForClipboard, "STRING");
|
||||
wl_data_source_offer(_glfw.wl.dataSourceForClipboard, "UTF8_STRING");
|
||||
struct wl_callback *callback = wl_display_sync(_glfw.wl.display);
|
||||
const static struct wl_callback_listener copy_callback_listener = {.done = copy_callback_done };
|
||||
wl_callback_add_listener(callback, ©_callback_listener, _glfw.wl.dataSourceForClipboard);
|
||||
const static struct wl_callback_listener clipboard_copy_callback_listener = {.done = clipboard_copy_callback_done};
|
||||
wl_callback_add_listener(callback, &clipboard_copy_callback_listener, _glfw.wl.dataSourceForClipboard);
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetClipboardString(void)
|
||||
{
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].id && _glfw.wl.dataOffers[i].mime && _glfw.wl.dataOffers[i].offer_type == 1) {
|
||||
if (_glfw.wl.dataOffers[i].id && _glfw.wl.dataOffers[i].mime && _glfw.wl.dataOffers[i].offer_type == CLIPBOARD) {
|
||||
if (_glfw.wl.dataOffers[i].is_self_offer) return _glfw.wl.clipboardString;
|
||||
return _glfwReceiveClipboardText(_glfw.wl.dataOffers[i].id, _glfw.wl.dataOffers[i].mime);
|
||||
free(_glfw.wl.pasteString);
|
||||
_glfw.wl.pasteString = read_data_offer(_glfw.wl.dataOffers[i].id, _glfw.wl.dataOffers[i].mime);
|
||||
return _glfw.wl.pasteString;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetPrimarySelectionString(const char* string)
|
||||
{
|
||||
if (!_glfw.wl.primarySelectionDevice) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Cannot copy no primary selection device available");
|
||||
return;
|
||||
}
|
||||
if (_glfw.wl.primarySelectionString == string) return;
|
||||
free(_glfw.wl.primarySelectionString);
|
||||
_glfw.wl.primarySelectionString = _glfw_strdup(string);
|
||||
|
||||
if (_glfw.wl.dataSourceForPrimarySelection)
|
||||
gtk_primary_selection_source_destroy(_glfw.wl.dataSourceForPrimarySelection);
|
||||
_glfw.wl.dataSourceForPrimarySelection = gtk_primary_selection_device_manager_create_source(_glfw.wl.primarySelectionDeviceManager);
|
||||
if (!_glfw.wl.dataSourceForPrimarySelection)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Cannot copy failed to create primary selection source");
|
||||
return;
|
||||
}
|
||||
gtk_primary_selection_source_add_listener(_glfw.wl.dataSourceForPrimarySelection, &primary_selection_source_listener, NULL);
|
||||
gtk_primary_selection_source_offer(_glfw.wl.dataSourceForPrimarySelection, clipboard_mime());
|
||||
gtk_primary_selection_source_offer(_glfw.wl.dataSourceForPrimarySelection, "text/plain");
|
||||
gtk_primary_selection_source_offer(_glfw.wl.dataSourceForPrimarySelection, "text/plain;charset=utf-8");
|
||||
gtk_primary_selection_source_offer(_glfw.wl.dataSourceForPrimarySelection, "TEXT");
|
||||
gtk_primary_selection_source_offer(_glfw.wl.dataSourceForPrimarySelection, "STRING");
|
||||
gtk_primary_selection_source_offer(_glfw.wl.dataSourceForPrimarySelection, "UTF8_STRING");
|
||||
struct wl_callback *callback = wl_display_sync(_glfw.wl.display);
|
||||
const static struct wl_callback_listener primary_selection_copy_callback_listener = {.done = primary_selection_copy_callback_done};
|
||||
wl_callback_add_listener(callback, &primary_selection_copy_callback_listener, _glfw.wl.dataSourceForPrimarySelection);
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetPrimarySelectionString(void)
|
||||
{
|
||||
if (_glfw.wl.dataSourceForPrimarySelection != NULL) return _glfw.wl.primarySelectionString;
|
||||
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.primarySelectionOffers); i++) {
|
||||
if (_glfw.wl.primarySelectionOffers[i].id && _glfw.wl.primarySelectionOffers[i].mime && _glfw.wl.primarySelectionOffers[i].offer_type == PRIMARY_SELECTION) {
|
||||
if (_glfw.wl.primarySelectionOffers[i].is_self_offer) {
|
||||
return _glfw.wl.primarySelectionString;
|
||||
}
|
||||
free(_glfw.wl.pasteString);
|
||||
_glfw.wl.pasteString = read_primary_selection_offer(_glfw.wl.primarySelectionOffers[i].id, _glfw.wl.primarySelectionOffers[i].mime);
|
||||
return _glfw.wl.pasteString;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
||||
49
glfw/x11_window.c
vendored
49
glfw/x11_window.c
vendored
@ -2680,6 +2680,29 @@ const char* _glfwPlatformGetClipboardString(void)
|
||||
return getSelectionString(_glfw.x11.CLIPBOARD);
|
||||
}
|
||||
|
||||
void _glfwPlatformSetPrimarySelectionString(const char* string)
|
||||
{
|
||||
free(_glfw.x11.primarySelectionString);
|
||||
_glfw.x11.primarySelectionString = _glfw_strdup(string);
|
||||
|
||||
XSetSelectionOwner(_glfw.x11.display,
|
||||
_glfw.x11.PRIMARY,
|
||||
_glfw.x11.helperWindowHandle,
|
||||
CurrentTime);
|
||||
|
||||
if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) !=
|
||||
_glfw.x11.helperWindowHandle)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"X11: Failed to become owner of primary selection");
|
||||
}
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetPrimarySelectionString(void)
|
||||
{
|
||||
return getSelectionString(_glfw.x11.PRIMARY);
|
||||
}
|
||||
|
||||
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
|
||||
{
|
||||
if (!_glfw.vk.KHR_surface)
|
||||
@ -2851,32 +2874,6 @@ GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
|
||||
return window->x11.handle;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSetX11SelectionString(const char* string)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
free(_glfw.x11.primarySelectionString);
|
||||
_glfw.x11.primarySelectionString = _glfw_strdup(string);
|
||||
|
||||
XSetSelectionOwner(_glfw.x11.display,
|
||||
_glfw.x11.PRIMARY,
|
||||
_glfw.x11.helperWindowHandle,
|
||||
CurrentTime);
|
||||
|
||||
if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) !=
|
||||
_glfw.x11.helperWindowHandle)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"X11: Failed to become owner of primary selection");
|
||||
}
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetX11SelectionString(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return getSelectionString(_glfw.x11.PRIMARY);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetXKBScancode(const char* keyName, GLFWbool caseSensitive) {
|
||||
return glfw_xkb_keysym_from_name(keyName, caseSensitive);
|
||||
}
|
||||
|
||||
@ -125,4 +125,4 @@ if os.environ.get('WAYLAND_DISPLAY') and 'KITTY_ENABLE_WAYLAND' in os.environ an
|
||||
is_wayland = True
|
||||
|
||||
|
||||
supports_primary_selection = not is_macos and not is_wayland
|
||||
supports_primary_selection = not is_macos
|
||||
|
||||
4
kitty/glfw-wrapper.c
generated
4
kitty/glfw-wrapper.c
generated
@ -377,9 +377,9 @@ load_glfw(const char* path) {
|
||||
|
||||
*(void **) (&glfwGetX11Window_impl) = dlsym(handle, "glfwGetX11Window");
|
||||
|
||||
*(void **) (&glfwSetX11SelectionString_impl) = dlsym(handle, "glfwSetX11SelectionString");
|
||||
*(void **) (&glfwSetPrimarySelectionString_impl) = dlsym(handle, "glfwSetPrimarySelectionString");
|
||||
|
||||
*(void **) (&glfwGetX11SelectionString_impl) = dlsym(handle, "glfwGetX11SelectionString");
|
||||
*(void **) (&glfwGetPrimarySelectionString_impl) = dlsym(handle, "glfwGetPrimarySelectionString");
|
||||
|
||||
*(void **) (&glfwGetXKBScancode_impl) = dlsym(handle, "glfwGetXKBScancode");
|
||||
|
||||
|
||||
12
kitty/glfw-wrapper.h
generated
12
kitty/glfw-wrapper.h
generated
@ -1884,13 +1884,13 @@ typedef int32_t (*glfwGetX11Window_func)(GLFWwindow*);
|
||||
glfwGetX11Window_func glfwGetX11Window_impl;
|
||||
#define glfwGetX11Window glfwGetX11Window_impl
|
||||
|
||||
typedef void (*glfwSetX11SelectionString_func)(const char*);
|
||||
glfwSetX11SelectionString_func glfwSetX11SelectionString_impl;
|
||||
#define glfwSetX11SelectionString glfwSetX11SelectionString_impl
|
||||
typedef void (*glfwSetPrimarySelectionString_func)(GLFWwindow*, const char*);
|
||||
glfwSetPrimarySelectionString_func glfwSetPrimarySelectionString_impl;
|
||||
#define glfwSetPrimarySelectionString glfwSetPrimarySelectionString_impl
|
||||
|
||||
typedef const char* (*glfwGetX11SelectionString_func)();
|
||||
glfwGetX11SelectionString_func glfwGetX11SelectionString_impl;
|
||||
#define glfwGetX11SelectionString glfwGetX11SelectionString_impl
|
||||
typedef const char* (*glfwGetPrimarySelectionString_func)(GLFWwindow*);
|
||||
glfwGetPrimarySelectionString_func glfwGetPrimarySelectionString_impl;
|
||||
#define glfwGetPrimarySelectionString glfwGetPrimarySelectionString_impl
|
||||
|
||||
typedef int (*glfwGetXKBScancode_func)(const char*, int);
|
||||
glfwGetXKBScancode_func glfwGetXKBScancode_impl;
|
||||
|
||||
14
kitty/glfw.c
14
kitty/glfw.c
@ -889,9 +889,10 @@ x11_window_id(PyObject UNUSED *self, PyObject *os_wid) {
|
||||
|
||||
static PyObject*
|
||||
get_primary_selection(PYNOARG) {
|
||||
if (glfwGetX11SelectionString) {
|
||||
return Py_BuildValue("y", glfwGetX11SelectionString());
|
||||
} else log_error("Failed to load glfwGetX11SelectionString");
|
||||
if (glfwGetPrimarySelectionString) {
|
||||
OSWindow *w = current_os_window();
|
||||
if (w) return Py_BuildValue("y", glfwGetPrimarySelectionString(w->handle));
|
||||
} else log_error("Failed to load glfwGetPrimarySelectionString");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@ -899,8 +900,11 @@ static PyObject*
|
||||
set_primary_selection(PyObject UNUSED *self, PyObject *args) {
|
||||
char *text;
|
||||
if (!PyArg_ParseTuple(args, "s", &text)) return NULL;
|
||||
if (glfwSetX11SelectionString) glfwSetX11SelectionString(text);
|
||||
else log_error("Failed to load glfwSetX11SelectionString");
|
||||
if (glfwSetPrimarySelectionString) {
|
||||
OSWindow *w = current_os_window();
|
||||
if (w) glfwSetPrimarySelectionString(w->handle, text);
|
||||
}
|
||||
else log_error("Failed to load glfwSetPrimarySelectionString");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user