Get reading from clipboard working
This commit is contained in:
parent
7e1380cc0d
commit
d17a6cd3a3
27
glfw/glfw3.h
vendored
27
glfw/glfw3.h
vendored
@ -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
19
glfw/input.c
vendored
@ -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
3
glfw/internal.h
vendored
@ -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
97
glfw/x11_window.c
vendored
@ -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)
|
||||
¬ification))
|
||||
{
|
||||
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)
|
||||
|
||||
@ -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'')
|
||||
|
||||
@ -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
3
kitty/glfw-wrapper.c
generated
@ -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
5
kitty/glfw-wrapper.h
generated
@ -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
|
||||
|
||||
43
kitty/glfw.c
43
kitty/glfw.c
@ -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),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user