Implement primary selection for Wayland
This commit is contained in:
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):
|
def build_wayland_protocols(env, run_tool, emphasis, newer, dest_dir):
|
||||||
for protocol in env.wayland_protocols:
|
for protocol in env.wayland_protocols:
|
||||||
src = os.path.join(env.wayland_packagedir, protocol)
|
if os.path.dirname(protocol) == 'wl_protocols':
|
||||||
if not os.path.exists(src):
|
src = os.path.join(dest_dir, protocol)
|
||||||
raise SystemExit('The wayland-protocols package on your system is missing the {} protocol definition file'.format(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':
|
for ext in 'hc':
|
||||||
dest = wayland_protocol_file_name(src, ext)
|
dest = wayland_protocol_file_name(src, ext)
|
||||||
dest = os.path.join(dest_dir, dest)
|
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 glfwGetCocoaKeyEquivalent(int glfw_key, int glfw_mods, void* cocoa_key, void* cocoa_mods)
|
||||||
void* glfwGetX11Display(void)
|
void* glfwGetX11Display(void)
|
||||||
int32_t glfwGetX11Window(GLFWwindow* window)
|
int32_t glfwGetX11Window(GLFWwindow* window)
|
||||||
void glfwSetX11SelectionString(const char* string)
|
void glfwSetPrimarySelectionString(GLFWwindow* window, const char* string)
|
||||||
const char* glfwGetX11SelectionString(void)
|
const char* glfwGetPrimarySelectionString(GLFWwindow* window, void)
|
||||||
int glfwGetXKBScancode(const char* key_name, int case_sensitive)
|
int glfwGetXKBScancode(const char* key_name, int case_sensitive)
|
||||||
void glfwRequestWaylandFrameEvent(GLFWwindow *handle, unsigned long long id, GLFWwaylandframecallbackfunc callback)
|
void glfwRequestWaylandFrameEvent(GLFWwindow *handle, unsigned long long id, GLFWwaylandframecallbackfunc callback)
|
||||||
'''.splitlines():
|
'''.splitlines():
|
||||||
|
|||||||
16
glfw/input.c
vendored
16
glfw/input.c
vendored
@@ -1375,6 +1375,22 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle)
|
|||||||
return _glfwPlatformGetClipboardString();
|
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)
|
GLFWAPI double glfwGetTime(void)
|
||||||
{
|
{
|
||||||
_GLFW_REQUIRE_INIT_OR_RETURN(0.0);
|
_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);
|
void _glfwPlatformSetClipboardString(const char* string);
|
||||||
const char* _glfwPlatformGetClipboardString(void);
|
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);
|
int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode);
|
||||||
void _glfwPlatformUpdateGamepadGUID(char* guid);
|
void _glfwPlatformUpdateGamepadGUID(char* guid);
|
||||||
|
|||||||
@@ -72,7 +72,8 @@
|
|||||||
"unstable/relative-pointer/relative-pointer-unstable-v1.xml",
|
"unstable/relative-pointer/relative-pointer-unstable-v1.xml",
|
||||||
"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml",
|
"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml",
|
||||||
"unstable/idle-inhibit/idle-inhibit-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": [
|
"sources": [
|
||||||
"wl_init.c",
|
"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);
|
_glfw.wl.seatVersion);
|
||||||
wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL);
|
wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL);
|
||||||
}
|
}
|
||||||
if (_glfw.wl.seat && _glfw.wl.dataDeviceManager && !_glfw.wl.dataDevice) {
|
if (_glfw.wl.seat) {
|
||||||
_glfwSetupWaylandDataDevice();
|
if (_glfw.wl.dataDeviceManager && !_glfw.wl.dataDevice) _glfwSetupWaylandDataDevice();
|
||||||
|
if (_glfw.wl.primarySelectionDeviceManager && !_glfw.wl.primarySelectionDevice) {
|
||||||
|
_glfwSetupWaylandPrimarySelectionDevice();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strcmp(interface, "xdg_wm_base") == 0)
|
else if (strcmp(interface, "xdg_wm_base") == 0)
|
||||||
@@ -615,6 +618,16 @@ static void registryHandleGlobal(void* data,
|
|||||||
_glfwSetupWaylandDataDevice();
|
_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);
|
wl_data_device_destroy(_glfw.wl.dataDevice);
|
||||||
if (_glfw.wl.dataDeviceManager)
|
if (_glfw.wl.dataDeviceManager)
|
||||||
wl_data_device_manager_destroy(_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)
|
if (_glfw.wl.registry)
|
||||||
wl_registry_destroy(_glfw.wl.registry);
|
wl_registry_destroy(_glfw.wl.registry);
|
||||||
if (_glfw.wl.display)
|
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]));
|
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.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-relative-pointer-unstable-v1-client-protocol.h"
|
||||||
#include "wayland-pointer-constraints-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-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_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
|
||||||
#define _glfw_dlclose(handle) dlclose(handle)
|
#define _glfw_dlclose(handle) dlclose(handle)
|
||||||
@@ -186,11 +187,19 @@ typedef struct _GLFWwindowWayland
|
|||||||
|
|
||||||
} _GLFWwindowWayland;
|
} _GLFWwindowWayland;
|
||||||
|
|
||||||
|
typedef enum _GLFWWaylandOfferType
|
||||||
|
{
|
||||||
|
EXPIRED,
|
||||||
|
CLIPBOARD,
|
||||||
|
DRAG_AND_DROP,
|
||||||
|
PRIMARY_SELECTION
|
||||||
|
}_GLFWWaylandOfferType ;
|
||||||
|
|
||||||
typedef struct _GLFWWaylandDataOffer
|
typedef struct _GLFWWaylandDataOffer
|
||||||
{
|
{
|
||||||
struct wl_data_offer *id;
|
struct wl_data_offer *id;
|
||||||
const char *mime;
|
const char *mime;
|
||||||
int offer_type;
|
_GLFWWaylandOfferType offer_type;
|
||||||
size_t idx;
|
size_t idx;
|
||||||
int is_self_offer;
|
int is_self_offer;
|
||||||
int has_uri_list;
|
int has_uri_list;
|
||||||
@@ -199,6 +208,17 @@ typedef struct _GLFWWaylandDataOffer
|
|||||||
struct wl_surface *surface;
|
struct wl_surface *surface;
|
||||||
} _GLFWWaylandDataOffer;
|
} _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
|
// Wayland-specific global data
|
||||||
//
|
//
|
||||||
typedef struct _GLFWlibraryWayland
|
typedef struct _GLFWlibraryWayland
|
||||||
@@ -221,6 +241,9 @@ typedef struct _GLFWlibraryWayland
|
|||||||
struct wl_data_device_manager* dataDeviceManager;
|
struct wl_data_device_manager* dataDeviceManager;
|
||||||
struct wl_data_device* dataDevice;
|
struct wl_data_device* dataDevice;
|
||||||
struct wl_data_source* dataSourceForClipboard;
|
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 compositorVersion;
|
||||||
int seatVersion;
|
int seatVersion;
|
||||||
@@ -261,11 +284,13 @@ typedef struct _GLFWlibraryWayland
|
|||||||
} egl;
|
} egl;
|
||||||
|
|
||||||
EventLoopData eventLoopData;
|
EventLoopData eventLoopData;
|
||||||
|
char* pasteString;
|
||||||
char* clipboardString;
|
char* clipboardString;
|
||||||
char* clipboardSourceString;
|
|
||||||
struct wl_data_offer* clipboardSourceOffer;
|
|
||||||
size_t dataOffersCounter;
|
size_t dataOffersCounter;
|
||||||
_GLFWWaylandDataOffer dataOffers[8];
|
_GLFWWaylandDataOffer dataOffers[8];
|
||||||
|
char* primarySelectionString;
|
||||||
|
size_t primarySelectionOffersCounter;
|
||||||
|
_GLFWWaylandPrimaryOffer primarySelectionOffers[8];
|
||||||
} _GLFWlibraryWayland;
|
} _GLFWlibraryWayland;
|
||||||
|
|
||||||
// Wayland-specific per-monitor data
|
// Wayland-specific per-monitor data
|
||||||
@@ -296,4 +321,5 @@ typedef struct _GLFWcursorWayland
|
|||||||
|
|
||||||
void _glfwAddOutputWayland(uint32_t name, uint32_t version);
|
void _glfwAddOutputWayland(uint32_t name, uint32_t version);
|
||||||
void _glfwSetupWaylandDataDevice();
|
void _glfwSetupWaylandDataDevice();
|
||||||
|
void _glfwSetupWaylandPrimarySelectionDevice();
|
||||||
void animateCursorImage(id_type timer_id, void *data);
|
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) {
|
if (text) {
|
||||||
size_t len = strlen(_glfw.wl.clipboardString), pos = 0;
|
size_t len = strlen(text), pos = 0;
|
||||||
double start = glfwGetTime();
|
double start = glfwGetTime();
|
||||||
while (pos < len && glfwGetTime() - start < 2.0) {
|
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 (ret < 0) {
|
||||||
if (errno == EAGAIN || errno == EINTR) continue;
|
if (errno == EAGAIN || errno == EINTR) continue;
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||||
@@ -1564,22 +1564,27 @@ static void _glfwSendClipboardText(void *data, struct wl_data_source *data_sourc
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char* read_data_offer(struct wl_data_offer *data_offer, const char *mime) {
|
static void _glfwSendClipboardText(void *data, struct wl_data_source *data_source, const char *mime_type, int fd) {
|
||||||
int pipefd[2];
|
send_text(_glfw.wl.clipboardString, fd);
|
||||||
if (pipe2(pipefd, O_CLOEXEC) != 0) return NULL;
|
}
|
||||||
wl_data_offer_receive(data_offer, mime, pipefd[1]);
|
|
||||||
close(pipefd[1]);
|
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);
|
wl_display_flush(_glfw.wl.display);
|
||||||
size_t sz = 0, capacity = 0;
|
size_t sz = 0, capacity = 0;
|
||||||
char *buf = NULL;
|
char *buf = NULL;
|
||||||
struct pollfd fds;
|
struct pollfd fds;
|
||||||
fds.fd = pipefd[0];
|
fds.fd = data_pipe;
|
||||||
fds.events = POLLIN;
|
fds.events = POLLIN;
|
||||||
double start = glfwGetTime();
|
double start = glfwGetTime();
|
||||||
#define bail(...) { \
|
#define bail(...) { \
|
||||||
_glfwInputError(GLFW_PLATFORM_ERROR, __VA_ARGS__); \
|
_glfwInputError(GLFW_PLATFORM_ERROR, __VA_ARGS__); \
|
||||||
free(buf); buf = NULL; \
|
free(buf); buf = NULL; \
|
||||||
close(pipefd[0]); \
|
close(data_pipe); \
|
||||||
return NULL; \
|
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");
|
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 (ret == -1) {
|
||||||
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) continue;
|
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) continue;
|
||||||
bail("Wayland: Failed to read clipboard data from pipe with error: %s", strerror(errno));
|
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;
|
sz += ret;
|
||||||
start = glfwGetTime();
|
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)
|
static char* read_primary_selection_offer(struct gtk_primary_selection_offer *primary_selection_offer, const char *mime) {
|
||||||
{
|
int pipefd[2];
|
||||||
if (_glfw.wl.clipboardSourceOffer == data_offer && _glfw.wl.clipboardSourceString)
|
if (pipe2(pipefd, O_CLOEXEC) != 0) return NULL;
|
||||||
return _glfw.wl.clipboardSourceString;
|
gtk_primary_selection_offer_receive(primary_selection_offer, mime, pipefd[1]);
|
||||||
free(_glfw.wl.clipboardSourceString);
|
close(pipefd[1]);
|
||||||
_glfw.wl.clipboardSourceString = read_data_offer(data_offer, mime);
|
return read_offer_string(pipefd[0]);
|
||||||
return _glfw.wl.clipboardSourceString;
|
}
|
||||||
|
|
||||||
|
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) {
|
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);
|
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) {
|
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,
|
.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() {
|
static void prune_unclaimed_data_offers() {
|
||||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
if (_glfw.wl.dataOffers[i].id && !_glfw.wl.dataOffers[i].offer_type) {
|
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)
|
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++) {
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
if (_glfw.wl.dataOffers[i].id == data_offer) {
|
if (_glfw.wl.dataOffers[i].id == data_offer) {
|
||||||
_glfw.wl.dataOffers[i].offer_type = 1;
|
_glfw.wl.dataOffers[i].offer_type = CLIPBOARD;
|
||||||
} else if (_glfw.wl.dataOffers[i].offer_type == 1) {
|
} else if (_glfw.wl.dataOffers[i].offer_type == CLIPBOARD) {
|
||||||
_glfw.wl.dataOffers[i].offer_type = 0; // previous selection offer
|
_glfw.wl.dataOffers[i].offer_type = EXPIRED; // previous selection offer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prune_unclaimed_data_offers();
|
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) {
|
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++) {
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
if (_glfw.wl.dataOffers[i].id == id) {
|
if (_glfw.wl.dataOffers[i].id == id) {
|
||||||
if (strcmp(mime, "text/plain;charset=utf-8") == 0)
|
set_offer_mimetype(&_glfw.wl.dataOffers[i], mime);
|
||||||
_glfw.wl.dataOffers[i].mime = "text/plain;charset=utf-8";
|
break;
|
||||||
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)
|
static void handle_primary_selection_offer_mimetype(void *data, struct gtk_primary_selection_offer* id, const char *mime) {
|
||||||
_glfw.wl.dataOffers[i].has_uri_list = 1;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1699,6 +1756,10 @@ static const struct wl_data_offer_listener data_offer_listener = {
|
|||||||
.action = data_offer_action,
|
.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) {
|
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;
|
size_t smallest_idx = SIZE_MAX, pos = 0;
|
||||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
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);
|
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) {
|
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++) {
|
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||||
if (_glfw.wl.dataOffers[i].id == id) {
|
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;
|
_glfw.wl.dataOffers[i].surface = surface;
|
||||||
const char *mime = _glfw.wl.dataOffers[i].has_uri_list ? URI_LIST_MIME : NULL;
|
const char *mime = _glfw.wl.dataOffers[i].has_uri_list ? URI_LIST_MIME : NULL;
|
||||||
wl_data_offer_accept(id, serial, mime);
|
wl_data_offer_accept(id, serial, mime);
|
||||||
} else if (_glfw.wl.dataOffers[i].offer_type == 2) {
|
} else if (_glfw.wl.dataOffers[i].offer_type == DRAG_AND_DROP) {
|
||||||
_glfw.wl.dataOffers[i].offer_type = 0; // previous drag offer
|
_glfw.wl.dataOffers[i].offer_type = EXPIRED; // previous drag offer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prune_unclaimed_data_offers();
|
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) {
|
static void drag_leave(void *data, struct wl_data_device *wl_data_device) {
|
||||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
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);
|
wl_data_offer_destroy(_glfw.wl.dataOffers[i].id);
|
||||||
memset(_glfw.wl.dataOffers + i, 0, sizeof(_glfw.wl.dataOffers[0]));
|
memset(_glfw.wl.dataOffers + i, 0, sizeof(_glfw.wl.dataOffers[0]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void drop(void *data, struct wl_data_device *wl_data_device) {
|
static void drop(void *data, struct wl_data_device *wl_data_device) {
|
||||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
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);
|
char *uri_list = read_data_offer(_glfw.wl.dataOffers[i].id, URI_LIST_MIME);
|
||||||
if (uri_list) {
|
if (uri_list) {
|
||||||
wl_data_offer_finish(_glfw.wl.dataOffers[i].id);
|
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,
|
.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
|
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) {
|
if (_glfw.wl.dataDevice && data == (void*)_glfw.wl.dataSourceForClipboard) {
|
||||||
wl_data_device_set_selection(_glfw.wl.dataDevice, data, serial);
|
wl_data_device_set_selection(_glfw.wl.dataDevice, data, serial);
|
||||||
}
|
}
|
||||||
wl_callback_destroy(callback);
|
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() {
|
void _glfwSetupWaylandDataDevice() {
|
||||||
_glfw.wl.dataDevice = wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat);
|
_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);
|
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() {
|
static inline GLFWbool _glfwEnsureDataDevice() {
|
||||||
if (!_glfw.wl.dataDeviceManager)
|
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, "STRING");
|
||||||
wl_data_source_offer(_glfw.wl.dataSourceForClipboard, "UTF8_STRING");
|
wl_data_source_offer(_glfw.wl.dataSourceForClipboard, "UTF8_STRING");
|
||||||
struct wl_callback *callback = wl_display_sync(_glfw.wl.display);
|
struct wl_callback *callback = wl_display_sync(_glfw.wl.display);
|
||||||
const static struct wl_callback_listener copy_callback_listener = {.done = copy_callback_done };
|
const static struct wl_callback_listener clipboard_copy_callback_listener = {.done = clipboard_copy_callback_done};
|
||||||
wl_callback_add_listener(callback, ©_callback_listener, _glfw.wl.dataSourceForClipboard);
|
wl_callback_add_listener(callback, &clipboard_copy_callback_listener, _glfw.wl.dataSourceForClipboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* _glfwPlatformGetClipboardString(void)
|
const char* _glfwPlatformGetClipboardString(void)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
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;
|
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;
|
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);
|
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)
|
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
|
||||||
{
|
{
|
||||||
if (!_glfw.vk.KHR_surface)
|
if (!_glfw.vk.KHR_surface)
|
||||||
@@ -2851,32 +2874,6 @@ GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
|
|||||||
return window->x11.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) {
|
GLFWAPI int glfwGetXKBScancode(const char* keyName, GLFWbool caseSensitive) {
|
||||||
return glfw_xkb_keysym_from_name(keyName, 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
|
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 **) (&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");
|
*(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;
|
glfwGetX11Window_func glfwGetX11Window_impl;
|
||||||
#define glfwGetX11Window glfwGetX11Window_impl
|
#define glfwGetX11Window glfwGetX11Window_impl
|
||||||
|
|
||||||
typedef void (*glfwSetX11SelectionString_func)(const char*);
|
typedef void (*glfwSetPrimarySelectionString_func)(GLFWwindow*, const char*);
|
||||||
glfwSetX11SelectionString_func glfwSetX11SelectionString_impl;
|
glfwSetPrimarySelectionString_func glfwSetPrimarySelectionString_impl;
|
||||||
#define glfwSetX11SelectionString glfwSetX11SelectionString_impl
|
#define glfwSetPrimarySelectionString glfwSetPrimarySelectionString_impl
|
||||||
|
|
||||||
typedef const char* (*glfwGetX11SelectionString_func)();
|
typedef const char* (*glfwGetPrimarySelectionString_func)(GLFWwindow*);
|
||||||
glfwGetX11SelectionString_func glfwGetX11SelectionString_impl;
|
glfwGetPrimarySelectionString_func glfwGetPrimarySelectionString_impl;
|
||||||
#define glfwGetX11SelectionString glfwGetX11SelectionString_impl
|
#define glfwGetPrimarySelectionString glfwGetPrimarySelectionString_impl
|
||||||
|
|
||||||
typedef int (*glfwGetXKBScancode_func)(const char*, int);
|
typedef int (*glfwGetXKBScancode_func)(const char*, int);
|
||||||
glfwGetXKBScancode_func glfwGetXKBScancode_impl;
|
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*
|
static PyObject*
|
||||||
get_primary_selection(PYNOARG) {
|
get_primary_selection(PYNOARG) {
|
||||||
if (glfwGetX11SelectionString) {
|
if (glfwGetPrimarySelectionString) {
|
||||||
return Py_BuildValue("y", glfwGetX11SelectionString());
|
OSWindow *w = current_os_window();
|
||||||
} else log_error("Failed to load glfwGetX11SelectionString");
|
if (w) return Py_BuildValue("y", glfwGetPrimarySelectionString(w->handle));
|
||||||
|
} else log_error("Failed to load glfwGetPrimarySelectionString");
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -899,8 +900,11 @@ static PyObject*
|
|||||||
set_primary_selection(PyObject UNUSED *self, PyObject *args) {
|
set_primary_selection(PyObject UNUSED *self, PyObject *args) {
|
||||||
char *text;
|
char *text;
|
||||||
if (!PyArg_ParseTuple(args, "s", &text)) return NULL;
|
if (!PyArg_ParseTuple(args, "s", &text)) return NULL;
|
||||||
if (glfwSetX11SelectionString) glfwSetX11SelectionString(text);
|
if (glfwSetPrimarySelectionString) {
|
||||||
else log_error("Failed to load glfwSetX11SelectionString");
|
OSWindow *w = current_os_window();
|
||||||
|
if (w) glfwSetPrimarySelectionString(w->handle, text);
|
||||||
|
}
|
||||||
|
else log_error("Failed to load glfwSetPrimarySelectionString");
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user