Get reading from clipboard working

This commit is contained in:
Kovid Goyal 2022-09-07 21:40:32 +05:30
parent 7e1380cc0d
commit d17a6cd3a3
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
9 changed files with 98 additions and 111 deletions

27
glfw/glfw3.h vendored
View File

@ -1729,6 +1729,7 @@ typedef enum {
GLFW_CLIPBOARD, GLFW_PRIMARY_SELECTION
} GLFWClipboardType;
typedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype);
typedef bool (* GLFWclipboardwritedatafun)(void *object, const char *data, size_t sz);
/*! @brief Video mode type.
*
@ -5231,32 +5232,8 @@ GLFWAPI const char* glfwGetGamepadName(int jid);
*/
GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state);
/*! @brief Sets the clipboard to the specified string.
*
* This function sets the system clipboard to the specified, UTF-8 encoded
* string.
*
* @param[in] window Deprecated. Any valid window or `NULL`.
* @param[in] string A UTF-8 encoded string.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @remark @wayland Clipboard is currently unimplemented.
*
* @pointer_lifetime The specified string is copied before this function
* returns.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa @ref glfwGetClipboardString
*
* @since Added in version 3.0.
*
* @ingroup input
*/
GLFWAPI void glfwSetClipboardDataTypes(GLFWClipboardType clipboard_type, const char* const *mime_types, size_t num_mime_types, GLFWclipboarditerfun get_iter);
GLFWAPI void glfwGetClipboard(GLFWClipboardType clipboard_type, const char* mime_type, GLFWclipboardwritedatafun write_data, void *object);
/*! @brief Returns the GLFW time.
*

19
glfw/input.c vendored
View File

@ -1534,6 +1534,11 @@ void _glfw_free_clipboard_data(_GLFWClipboardData *cd) {
memset(cd, 0, sizeof(cd[0]));
}
GLFWAPI void glfwGetClipboard(GLFWClipboardType clipboard_type, const char* mime_type, GLFWclipboardwritedatafun write_data, void *object) {
_GLFW_REQUIRE_INIT();
_glfwPlatformGetClipboard(clipboard_type, mime_type, write_data, object);
}
GLFWAPI void glfwSetClipboardDataTypes(GLFWClipboardType clipboard_type, const char* const *mime_types, size_t num_mime_types, GLFWclipboarditerfun get_data) {
assert(mime_types != NULL);
assert(get_data != NULL);
@ -1552,20 +1557,6 @@ GLFWAPI void glfwSetClipboardDataTypes(GLFWClipboardType clipboard_type, const c
_glfwPlatformSetClipboard(clipboard_type);
}
GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle UNUSED)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return _glfwPlatformGetClipboardString();
}
#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND) || defined(__APPLE__)
GLFWAPI const char* glfwGetPrimarySelectionString(GLFWwindow* handle UNUSED)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return _glfwPlatformGetPrimarySelectionString();
}
#endif
GLFWAPI monotonic_t glfwGetTime(void)
{
_GLFW_REQUIRE_INIT_OR_RETURN(0);

3
glfw/internal.h vendored
View File

@ -687,8 +687,7 @@ bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp);
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp);
void _glfwPlatformSetClipboard(GLFWClipboardType t);
const char* _glfwPlatformGetClipboardString(void);
const char* _glfwPlatformGetPrimarySelectionString(void);
void _glfwPlatformGetClipboard(GLFWClipboardType clipboard_type, const char* mime_type, GLFWclipboardwritedatafun write_data, void *object);
bool _glfwPlatformInitJoysticks(void);
void _glfwPlatformTerminateJoysticks(void);

97
glfw/x11_window.c vendored
View File

@ -922,28 +922,28 @@ static void handleSelectionRequest(XEvent* event)
XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
}
static const char* getSelectionString(Atom selection)
static void
getSelectionString(Atom selection, const char *mime_type, Atom *targets, size_t num_targets, GLFWclipboardwritedatafun write_data, void *object)
{
char* selectionString = NULL;
const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING };
const size_t targetCount = sizeof(targets) / sizeof(targets[0]);
#define XFREE(x) { if (x) XFree(x); x = NULL; }
if (XGetSelectionOwner(_glfw.x11.display, selection) ==
_glfw.x11.helperWindowHandle)
{
// Instead of doing a large number of X round-trips just to put this
// string into a window property and then read it back, just return it
_GLFWClipboardData *cd = selection == _glfw.x11.PRIMARY ? &_glfw.primary : &_glfw.clipboard;
char *data = NULL; size_t sz = get_clipboard_data(cd, "text/plain", &data);
if (data && sz) return data;
char *data = NULL; size_t sz = get_clipboard_data(cd, mime_type, &data);
write_data(object, data, sz); free(data);
return;
}
for (size_t i = 0; i < targetCount; i++)
bool found = false;
for (size_t i = 0; !found && i < num_targets; i++)
{
char* data;
Atom actualType;
int actualFormat;
unsigned long itemCount, bytesAfter;
char* data = NULL;
Atom actualType = None;
int actualFormat = 0;
unsigned long itemCount = 0, bytesAfter = 0;
monotonic_t start = glfwGetTime();
XEvent notification, dummy;
@ -960,8 +960,7 @@ static const char* getSelectionString(Atom selection)
&notification))
{
monotonic_t time = glfwGetTime();
if (time - start > s_to_monotonic_t(2ll))
return "";
if (time - start > s_to_monotonic_t(2ll)) return;
waitForX11Event(s_to_monotonic_t(2ll) - (time - start));
}
@ -988,9 +987,6 @@ static const char* getSelectionString(Atom selection)
if (actualType == _glfw.x11.INCR)
{
size_t size = 1;
char* string = NULL;
for (;;)
{
start = glfwGetTime();
@ -1001,13 +997,12 @@ static const char* getSelectionString(Atom selection)
{
monotonic_t time = glfwGetTime();
if (time - start > s_to_monotonic_t(2ll)) {
free(string);
return "";
return;
}
waitForX11Event(s_to_monotonic_t(2ll) - (time - start));
}
XFree(data);
XFREE(data);
XGetWindowProperty(_glfw.x11.display,
notification.xselection.requestor,
notification.xselection.property,
@ -1023,47 +1018,37 @@ static const char* getSelectionString(Atom selection)
if (itemCount)
{
size += itemCount;
string = realloc(string, size);
string[size - itemCount - 1] = '\0';
strcat(string, data);
}
if (!itemCount)
{
if (targets[i] == XA_STRING)
{
selectionString = convertLatin1toUTF8(string);
free(string);
const char *string = data;
if (targets[i] == XA_STRING) {
string = convertLatin1toUTF8(data);
itemCount = strlen(string);
}
else
selectionString = string;
bool ok = write_data(object, string, itemCount);
if (string != data) free((void*)string);
if (!ok) { XFREE(data); break; }
} else { found = true; break; }
break;
}
}
}
else if (actualType == targets[i])
{
if (targets[i] == XA_STRING)
selectionString = convertLatin1toUTF8(data);
else
selectionString = _glfw_strdup(data);
if (targets[i] == XA_STRING) {
const char *string = convertLatin1toUTF8(data);
write_data(object, string, strlen(string)); free((void*)string);
} else write_data(object, data, itemCount);
found = true;
}
XFree(data);
XFREE(data);
if (selectionString)
break;
}
if (!selectionString)
if (!found)
{
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
"X11: Failed to convert selection to string");
}
return selectionString;
#undef XFREE
}
// Make the specified window and its video mode active on its monitor
@ -2914,14 +2899,18 @@ void _glfwPlatformSetClipboard(GLFWClipboardType t) {
}
}
const char* _glfwPlatformGetClipboardString(void)
{
return getSelectionString(_glfw.x11.CLIPBOARD);
}
const char* _glfwPlatformGetPrimarySelectionString(void)
{
return getSelectionString(_glfw.x11.PRIMARY);
void
_glfwPlatformGetClipboard(GLFWClipboardType clipboard_type, const char* mime_type, GLFWclipboardwritedatafun write_data, void *object) {
// TODO: Handle NULL mime_type
Atom atoms[2], which = clipboard_type == GLFW_PRIMARY_SELECTION ? _glfw.x11.PRIMARY : _glfw.x11.CLIPBOARD;
size_t count = 1;
if (strcmp(mime_type, "text/plain") == 0) {
atoms[0] = _glfw.x11.UTF8_STRING;
atoms[count++] = XA_STRING;
} else {
atoms[0] = atom_for_mime(mime_type).atom;
}
getSelectionString(which, mime_type, atoms, count, write_data, object);
}
EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs)

View File

@ -2,10 +2,10 @@
# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>
import io
from typing import IO, Callable, Dict, Union
from typing import IO, Callable, Dict, Union, List
from .constants import supports_primary_selection
from .fast_data_types import GLFW_CLIPBOARD, get_boss, set_clipboard_data_types
from .fast_data_types import GLFW_CLIPBOARD, get_boss, set_clipboard_data_types, get_clipboard_mime
DataType = Union[bytes, 'IO[bytes]']
@ -26,7 +26,12 @@ class Clipboard:
set_clipboard_data_types(self.clipboard_type, tuple(self.data))
def get_text(self) -> str:
raise NotImplementedError('TODO: Implement this')
parts: List[bytes] = []
self.get_mime("text/plain", parts.append)
return b''.join(parts).decode('utf-8', 'replace')
def get_mime(self, mime: str, output: Callable[[bytes], None]) -> None:
get_clipboard_mime(self.clipboard_type, mime, output)
def __call__(self, mime: str) -> Callable[[], bytes]:
data = self.data.get(mime, b'')

View File

@ -1473,3 +1473,4 @@ def set_use_os_log(yes: bool) -> None: ...
def get_docs_ref_map() -> bytes: ...
def clearenv() -> None: ...
def set_clipboard_data_types(ct: int, mime_types: Tuple[str, ...]) -> None: ...
def get_clipboard_mime(ct: int, mime: Optional[str], callback: Callable[[bytes], None]) -> None: ...

3
kitty/glfw-wrapper.c generated
View File

@ -368,6 +368,9 @@ load_glfw(const char* path) {
*(void **) (&glfwSetClipboardDataTypes_impl) = dlsym(handle, "glfwSetClipboardDataTypes");
if (glfwSetClipboardDataTypes_impl == NULL) fail("Failed to load glfw function glfwSetClipboardDataTypes with error: %s", dlerror());
*(void **) (&glfwGetClipboard_impl) = dlsym(handle, "glfwGetClipboard");
if (glfwGetClipboard_impl == NULL) fail("Failed to load glfw function glfwGetClipboard with error: %s", dlerror());
*(void **) (&glfwGetTime_impl) = dlsym(handle, "glfwGetTime");
if (glfwGetTime_impl == NULL) fail("Failed to load glfw function glfwGetTime with error: %s", dlerror());

5
kitty/glfw-wrapper.h generated
View File

@ -1467,6 +1467,7 @@ typedef enum {
GLFW_CLIPBOARD, GLFW_PRIMARY_SELECTION
} GLFWClipboardType;
typedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype);
typedef bool (* GLFWclipboardwritedatafun)(void *object, const char *data, size_t sz);
/*! @brief Video mode type.
*
@ -2096,6 +2097,10 @@ typedef void (*glfwSetClipboardDataTypes_func)(GLFWClipboardType, const char* co
GFW_EXTERN glfwSetClipboardDataTypes_func glfwSetClipboardDataTypes_impl;
#define glfwSetClipboardDataTypes glfwSetClipboardDataTypes_impl
typedef void (*glfwGetClipboard_func)(GLFWClipboardType, const char*, GLFWclipboardwritedatafun, void*);
GFW_EXTERN glfwGetClipboard_func glfwGetClipboard_impl;
#define glfwGetClipboard glfwGetClipboard_impl
typedef monotonic_t (*glfwGetTime_func)(void);
GFW_EXTERN glfwGetTime_func glfwGetTime_impl;
#define glfwGetTime glfwGetTime_impl

View File

@ -1590,16 +1590,10 @@ get_clipboard_data(const char *mime_type, void *iter, GLFWClipboardType ct) {
if (global_state.boss == NULL) return ans;
if (iter == NULL) {
PyObject *c = PyObject_GetAttrString(global_state.boss, ct == GLFW_PRIMARY_SELECTION ? "primary_selection" : "clipboard");
if (c == NULL) {
PyErr_Print();
return ans;
}
if (c == NULL) { return ans; }
PyObject *i = PyObject_CallFunction(c, "s", mime_type);
Py_DECREF(c);
if (!i) {
PyErr_Print();
return ans;
}
if (!i) { return ans; }
ans.iter = i;
return ans;
}
@ -1621,14 +1615,36 @@ set_clipboard_data_types(PyObject *self UNUSED, PyObject *args) {
PyObject *mta;
int ctype;
if (!PyArg_ParseTuple(args, "iO!", &ctype, &PyTuple_Type, &mta)) return NULL;
const char **mime_types = calloc(PyTuple_GET_SIZE(mta), sizeof(char*));
if (!mime_types) return PyErr_NoMemory();
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(mta); i++) mime_types[i] = PyUnicode_AsUTF8(PyTuple_GET_ITEM(mta, i));
glfwSetClipboardDataTypes(ctype, mime_types, PyTuple_GET_SIZE(mta), get_clipboard_data);
free(mime_types);
if (glfwSetClipboardDataTypes) {
const char **mime_types = calloc(PyTuple_GET_SIZE(mta), sizeof(char*));
if (!mime_types) return PyErr_NoMemory();
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(mta); i++) mime_types[i] = PyUnicode_AsUTF8(PyTuple_GET_ITEM(mta, i));
glfwSetClipboardDataTypes(ctype, mime_types, PyTuple_GET_SIZE(mta), get_clipboard_data);
free(mime_types);
} else log_error("GLFW not initialized cannot set clipboard data");
if (PyErr_Occurred()) return NULL;
Py_RETURN_NONE;
}
static bool
write_clipboard_data(void *callback, const char *data, size_t sz) {
Py_ssize_t z = sz;
PyObject *ret = PyObject_CallFunction(callback, "y#", data, z);
bool ok = false;
if (ret != NULL) { ok = true; Py_DECREF(ret); }
return ok;
}
static PyObject*
get_clipboard_mime(PyObject *self UNUSED, PyObject *args) {
int ctype;
const char *mime;
PyObject *callback;
if (!PyArg_ParseTuple(args, "izO", &ctype, &mime, &callback)) return NULL;
glfwGetClipboard(ctype, mime, write_clipboard_data, callback);
if (PyErr_Occurred()) return NULL;
Py_RETURN_NONE;
}
// Boilerplate {{{
static PyMethodDef module_methods[] = {
@ -1636,6 +1652,7 @@ static PyMethodDef module_methods[] = {
{"create_os_window", (PyCFunction)(void (*) (void))(create_os_window), METH_VARARGS | METH_KEYWORDS, NULL},
METHODB(set_default_window_icon, METH_VARARGS),
METHODB(set_clipboard_data_types, METH_VARARGS),
METHODB(get_clipboard_mime, METH_VARARGS),
METHODB(toggle_secure_input, METH_NOARGS),
METHODB(get_content_scale_for_window, METH_NOARGS),
METHODB(ring_bell, METH_NOARGS),