Start work on giving GLFW a proper clipboard API
This commit is contained in:
parent
9a99554ed3
commit
7e1380cc0d
45
glfw/glfw3.h
vendored
45
glfw/glfw3.h
vendored
@ -1718,6 +1718,17 @@ typedef void (* GLFWuserdatafun)(unsigned long long, void*);
|
||||
typedef void (* GLFWtickcallback)(void*);
|
||||
typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin);
|
||||
typedef char* (* GLFWcurrentselectionfun)(void);
|
||||
typedef void (* GLFWclipboarddatafreefun)(void* data);
|
||||
typedef struct GLFWDataChunk {
|
||||
const char *data;
|
||||
size_t sz;
|
||||
GLFWclipboarddatafreefun free;
|
||||
void *iter, *free_data;
|
||||
} GLFWDataChunk;
|
||||
typedef enum {
|
||||
GLFW_CLIPBOARD, GLFW_PRIMARY_SELECTION
|
||||
} GLFWClipboardType;
|
||||
typedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype);
|
||||
|
||||
/*! @brief Video mode type.
|
||||
*
|
||||
@ -5245,39 +5256,7 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state);
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string);
|
||||
|
||||
/*! @brief Returns the contents of the clipboard as a string.
|
||||
*
|
||||
* This function returns the contents of the system clipboard, if it contains
|
||||
* or is convertible to a UTF-8 encoded string. If the clipboard is empty or
|
||||
* if its contents cannot be converted, `NULL` is returned and a @ref
|
||||
* GLFW_FORMAT_UNAVAILABLE error is generated.
|
||||
*
|
||||
* @param[in] window Deprecated. Any valid window or `NULL`.
|
||||
* @return The contents of the clipboard as a UTF-8 encoded string, or `NULL`
|
||||
* if an [error](@ref error_handling) occurred.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||
* GLFW_PLATFORM_ERROR.
|
||||
*
|
||||
* @remark @wayland Clipboard is currently unimplemented.
|
||||
*
|
||||
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
|
||||
* should not free it yourself. It is valid until the next call to @ref
|
||||
* glfwGetClipboardString or @ref glfwSetClipboardString, or until the library
|
||||
* is terminated.
|
||||
*
|
||||
* @thread_safety This function must only be called from the main thread.
|
||||
*
|
||||
* @sa @ref clipboard
|
||||
* @sa @ref glfwSetClipboardString
|
||||
*
|
||||
* @since Added in version 3.0.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window);
|
||||
GLFWAPI void glfwSetClipboardDataTypes(GLFWClipboardType clipboard_type, const char* const *mime_types, size_t num_mime_types, GLFWclipboarditerfun get_iter);
|
||||
|
||||
/*! @brief Returns the GLFW time.
|
||||
*
|
||||
|
||||
2
glfw/init.c
vendored
2
glfw/init.c
vendored
@ -68,6 +68,8 @@ static void terminate(void)
|
||||
int i;
|
||||
|
||||
memset(&_glfw.callbacks, 0, sizeof(_glfw.callbacks));
|
||||
_glfw_free_clipboard_data(&_glfw.clipboard);
|
||||
_glfw_free_clipboard_data(&_glfw.primary);
|
||||
|
||||
while (_glfw.windowListHead)
|
||||
glfwDestroyWindow((GLFWwindow*) _glfw.windowListHead);
|
||||
|
||||
34
glfw/input.c
vendored
34
glfw/input.c
vendored
@ -1526,12 +1526,30 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
|
||||
return true;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSetClipboardString(GLFWwindow* handle UNUSED, const char* string)
|
||||
{
|
||||
assert(string != NULL);
|
||||
void _glfw_free_clipboard_data(_GLFWClipboardData *cd) {
|
||||
if (cd->mime_types) {
|
||||
for (size_t i = 0; i < cd->num_mime_types; i++) free((void*)cd->mime_types[i]);
|
||||
free((void*)cd->mime_types);
|
||||
}
|
||||
memset(cd, 0, sizeof(cd[0]));
|
||||
}
|
||||
|
||||
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);
|
||||
_GLFW_REQUIRE_INIT();
|
||||
_glfwPlatformSetClipboardString(string);
|
||||
_GLFWClipboardData *cd = NULL;
|
||||
switch(clipboard_type) {
|
||||
case GLFW_CLIPBOARD: cd = &_glfw.clipboard; break;
|
||||
case GLFW_PRIMARY_SELECTION: cd = &_glfw.primary; break;
|
||||
}
|
||||
_glfw_free_clipboard_data(cd);
|
||||
cd->get_data = get_data;
|
||||
cd->mime_types = calloc(num_mime_types, sizeof(char*));
|
||||
cd->num_mime_types = num_mime_types;
|
||||
cd->ctype = clipboard_type;
|
||||
for (size_t i = 0; i < cd->num_mime_types; i++) cd->mime_types[i] = _glfw_strdup(mime_types[i]);
|
||||
_glfwPlatformSetClipboard(clipboard_type);
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle UNUSED)
|
||||
@ -1541,14 +1559,6 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle UNUSED)
|
||||
}
|
||||
|
||||
#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND) || defined(__APPLE__)
|
||||
GLFWAPI void glfwSetPrimarySelectionString(GLFWwindow* handle UNUSED, const char* string)
|
||||
{
|
||||
assert(string != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
_glfwPlatformSetPrimarySelectionString(string);
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetPrimarySelectionString(GLFWwindow* handle UNUSED)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
19
glfw/internal.h
vendored
19
glfw/internal.h
vendored
@ -34,6 +34,9 @@
|
||||
#endif
|
||||
|
||||
#define arraysz(x) (sizeof(x)/sizeof(x[0]))
|
||||
#define MAX(x, y) __extension__ ({ \
|
||||
__typeof__ (x) a = (x); __typeof__ (y) b = (y); \
|
||||
a > b ? a : b;})
|
||||
|
||||
#if defined(GLFW_INCLUDE_GLCOREARB) || \
|
||||
defined(GLFW_INCLUDE_ES1) || \
|
||||
@ -561,6 +564,13 @@ struct _GLFWmutex
|
||||
_GLFW_PLATFORM_MUTEX_STATE;
|
||||
};
|
||||
|
||||
typedef struct _GLFWClipboardData {
|
||||
const char** mime_types;
|
||||
size_t num_mime_types;
|
||||
GLFWclipboarditerfun get_data;
|
||||
GLFWClipboardType ctype;
|
||||
} _GLFWClipboardData;
|
||||
|
||||
// Library global data
|
||||
//
|
||||
struct _GLFWlibrary
|
||||
@ -575,6 +585,8 @@ struct _GLFWlibrary
|
||||
int refreshRate;
|
||||
} hints;
|
||||
|
||||
_GLFWClipboardData primary, clipboard;
|
||||
|
||||
_GLFWerror* errorListHead;
|
||||
_GLFWcursor* cursorListHead;
|
||||
_GLFWwindow* windowListHead;
|
||||
@ -674,12 +686,9 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode);
|
||||
bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp);
|
||||
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp);
|
||||
|
||||
void _glfwPlatformSetClipboardString(const char* string);
|
||||
void _glfwPlatformSetClipboard(GLFWClipboardType t);
|
||||
const char* _glfwPlatformGetClipboardString(void);
|
||||
#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND) || defined(__APPLE__)
|
||||
void _glfwPlatformSetPrimarySelectionString(const char* string);
|
||||
const char* _glfwPlatformGetPrimarySelectionString(void);
|
||||
#endif
|
||||
|
||||
bool _glfwPlatformInitJoysticks(void);
|
||||
void _glfwPlatformTerminateJoysticks(void);
|
||||
@ -855,3 +864,5 @@ void _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval,
|
||||
void _glfwPlatformRemoveTimer(unsigned long long timer_id);
|
||||
|
||||
char* _glfw_strdup(const char* source);
|
||||
|
||||
void _glfw_free_clipboard_data(_GLFWClipboardData *cd);
|
||||
|
||||
10
glfw/x11_init.c
vendored
10
glfw/x11_init.c
vendored
@ -680,8 +680,14 @@ void _glfwPlatformTerminate(void)
|
||||
|
||||
glfw_xkb_release(&_glfw.x11.xkb);
|
||||
glfw_dbus_terminate(&_glfw.x11.dbus);
|
||||
free(_glfw.x11.primarySelectionString);
|
||||
free(_glfw.x11.clipboardString);
|
||||
if (_glfw.x11.mime_atoms.array) {
|
||||
for (size_t i = 0; i < _glfw.x11.mime_atoms.sz; i++) {
|
||||
free((void*)_glfw.x11.mime_atoms.array[i].mime);
|
||||
}
|
||||
free(_glfw.x11.mime_atoms.array);
|
||||
}
|
||||
if (_glfw.x11.clipboard_atoms.array) { free(_glfw.x11.clipboard_atoms.array); }
|
||||
if (_glfw.x11.primary_atoms.array) { free(_glfw.x11.primary_atoms.array); }
|
||||
|
||||
if (_glfw.x11.display)
|
||||
{
|
||||
|
||||
16
glfw/x11_platform.h
vendored
16
glfw/x11_platform.h
vendored
@ -207,6 +207,16 @@ typedef struct _GLFWwindowX11
|
||||
|
||||
} _GLFWwindowX11;
|
||||
|
||||
typedef struct MimeAtom {
|
||||
Atom atom;
|
||||
const char* mime;
|
||||
} MimeAtom;
|
||||
|
||||
typedef struct AtomArray {
|
||||
MimeAtom *array;
|
||||
size_t sz, capacity;
|
||||
} AtomArray;
|
||||
|
||||
// X11-specific global data
|
||||
//
|
||||
typedef struct _GLFWlibraryX11
|
||||
@ -225,10 +235,6 @@ typedef struct _GLFWlibraryX11
|
||||
XContext context;
|
||||
// Most recent error code received by X error handler
|
||||
int errorCode;
|
||||
// Primary selection string (while the primary selection is owned)
|
||||
char* primarySelectionString;
|
||||
// Clipboard string (while the selection is owned)
|
||||
char* clipboardString;
|
||||
// Where to place the cursor when re-enabled
|
||||
double restoreCursorPosX, restoreCursorPosY;
|
||||
// The window whose disabled cursor mode is active
|
||||
@ -291,6 +297,8 @@ typedef struct _GLFWlibraryX11
|
||||
|
||||
// XRM database atom
|
||||
Atom RESOURCE_MANAGER;
|
||||
// Atoms for MIME types
|
||||
AtomArray mime_atoms, clipboard_atoms, primary_atoms;
|
||||
|
||||
struct {
|
||||
bool available;
|
||||
|
||||
224
glfw/x11_window.c
vendored
224
glfw/x11_window.c
vendored
@ -716,19 +716,60 @@ static bool createNativeWindow(_GLFWwindow* window,
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_clipboard_data(const _GLFWClipboardData *cd, const char *mime, char **data) {
|
||||
*data = NULL;
|
||||
GLFWDataChunk chunk = cd->get_data(mime, NULL, cd->ctype);
|
||||
char *buf = NULL;
|
||||
size_t sz = 0, cap = 0;
|
||||
void *iter = chunk.iter;
|
||||
if (!iter) return 0;
|
||||
while (true) {
|
||||
chunk = cd->get_data(mime, iter, cd->ctype);
|
||||
if (!chunk.sz) break;
|
||||
if (cap < sz + chunk.sz) {
|
||||
cap = MAX(cap * 2, sz + 4 * chunk.sz);
|
||||
buf = realloc(buf, cap);
|
||||
}
|
||||
memcpy(buf + sz, chunk.data, chunk.sz);
|
||||
sz += chunk.sz;
|
||||
if (chunk.free) chunk.free((void*)chunk.free_data);
|
||||
}
|
||||
*data = buf;
|
||||
cd->get_data(NULL, iter, cd->ctype);
|
||||
return sz;
|
||||
}
|
||||
|
||||
static void
|
||||
get_atom_names(const Atom *atoms, int count, char **atom_names) {
|
||||
_glfwGrabErrorHandlerX11();
|
||||
XGetAtomNames(_glfw.x11.display, (Atom*)atoms, count, atom_names);
|
||||
_glfwReleaseErrorHandlerX11();
|
||||
if (_glfw.x11.errorCode != Success) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
_glfwGrabErrorHandlerX11();
|
||||
atom_names[i] = XGetAtomName(_glfw.x11.display, atoms[i]);
|
||||
_glfwReleaseErrorHandlerX11();
|
||||
if (_glfw.x11.errorCode != Success) atom_names[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set the specified property to the selection converted to the requested target
|
||||
//
|
||||
static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
|
||||
{
|
||||
int i;
|
||||
char* selectionString = NULL;
|
||||
const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING };
|
||||
const int formatCount = sizeof(formats) / sizeof(formats[0]);
|
||||
const AtomArray *aa;
|
||||
const _GLFWClipboardData *cd;
|
||||
|
||||
if (request->selection == _glfw.x11.PRIMARY)
|
||||
selectionString = _glfw.x11.primarySelectionString;
|
||||
else
|
||||
selectionString = _glfw.x11.clipboardString;
|
||||
if (request->selection == _glfw.x11.PRIMARY) {
|
||||
aa = &_glfw.x11.primary_atoms;
|
||||
cd = &_glfw.primary;
|
||||
} else {
|
||||
aa = &_glfw.x11.clipboard_atoms;
|
||||
cd = &_glfw.clipboard;
|
||||
}
|
||||
|
||||
if (request->property == None)
|
||||
{
|
||||
@ -741,11 +782,10 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
|
||||
{
|
||||
// The list of supported targets was requested
|
||||
|
||||
const Atom targets[] = { _glfw.x11.TARGETS,
|
||||
_glfw.x11.MULTIPLE,
|
||||
_glfw.x11.UTF8_STRING,
|
||||
XA_STRING };
|
||||
|
||||
Atom *targets = calloc(aa->sz + 2, sizeof(Atom));
|
||||
targets[0] = _glfw.x11.TARGETS;
|
||||
targets[1] = _glfw.x11.MULTIPLE;
|
||||
for (size_t i = 0; i < aa->sz; i++) targets[i+2] = aa->array[i].atom;
|
||||
XChangeProperty(_glfw.x11.display,
|
||||
request->requestor,
|
||||
request->property,
|
||||
@ -753,8 +793,8 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
|
||||
32,
|
||||
PropModeReplace,
|
||||
(unsigned char*) targets,
|
||||
sizeof(targets) / sizeof(targets[0]));
|
||||
|
||||
aa->sz + 2);
|
||||
free(targets);
|
||||
return request->property;
|
||||
}
|
||||
|
||||
@ -763,7 +803,7 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
|
||||
// Multiple conversions were requested
|
||||
|
||||
Atom* targets;
|
||||
unsigned long i, count;
|
||||
size_t i, j, count;
|
||||
|
||||
count = _glfwGetWindowPropertyX11(request->requestor,
|
||||
request->property,
|
||||
@ -772,24 +812,25 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
|
||||
|
||||
for (i = 0; i < count; i += 2)
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < formatCount; j++)
|
||||
for (j = 0; j < aa->sz; j++)
|
||||
{
|
||||
if (targets[i] == formats[j])
|
||||
if (targets[i] == aa->array[j].atom)
|
||||
break;
|
||||
}
|
||||
|
||||
if (j < formatCount)
|
||||
if (j < aa->sz)
|
||||
{
|
||||
if (selectionString) XChangeProperty(_glfw.x11.display,
|
||||
char *data = NULL; size_t sz = get_clipboard_data(cd, aa->array[j].mime, &data);
|
||||
|
||||
if (data && sz) XChangeProperty(_glfw.x11.display,
|
||||
request->requestor,
|
||||
targets[i + 1],
|
||||
targets[i],
|
||||
8,
|
||||
PropModeReplace,
|
||||
(unsigned char *) selectionString,
|
||||
strlen(selectionString));
|
||||
(unsigned char *) data,
|
||||
sz);
|
||||
free(data);
|
||||
}
|
||||
else
|
||||
targets[i + 1] = None;
|
||||
@ -828,20 +869,22 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
|
||||
|
||||
// Conversion to a data target was requested
|
||||
|
||||
for (i = 0; i < formatCount; i++)
|
||||
for (size_t i = 0; i < aa->sz; i++)
|
||||
{
|
||||
if (request->target == formats[i])
|
||||
if (request->target == aa->array[i].atom)
|
||||
{
|
||||
// The requested target is one we support
|
||||
|
||||
if (selectionString) XChangeProperty(_glfw.x11.display,
|
||||
char *data = NULL; size_t sz = get_clipboard_data(cd, aa->array[i].mime, &data);
|
||||
if (data && sz) XChangeProperty(_glfw.x11.display,
|
||||
request->requestor,
|
||||
request->property,
|
||||
request->target,
|
||||
8,
|
||||
PropModeReplace,
|
||||
(unsigned char *) selectionString,
|
||||
strlen(selectionString));
|
||||
(unsigned char *) data,
|
||||
sz);
|
||||
free(data);
|
||||
|
||||
return request->property;
|
||||
}
|
||||
@ -856,13 +899,11 @@ static void handleSelectionClear(XEvent* event)
|
||||
{
|
||||
if (event->xselectionclear.selection == _glfw.x11.PRIMARY)
|
||||
{
|
||||
free(_glfw.x11.primarySelectionString);
|
||||
_glfw.x11.primarySelectionString = NULL;
|
||||
_glfw_free_clipboard_data(&_glfw.primary);
|
||||
}
|
||||
else
|
||||
{
|
||||
free(_glfw.x11.clipboardString);
|
||||
_glfw.x11.clipboardString = NULL;
|
||||
_glfw_free_clipboard_data(&_glfw.clipboard);
|
||||
}
|
||||
}
|
||||
|
||||
@ -883,26 +924,20 @@ static void handleSelectionRequest(XEvent* event)
|
||||
|
||||
static const char* getSelectionString(Atom selection)
|
||||
{
|
||||
char** selectionString = NULL;
|
||||
char* selectionString = NULL;
|
||||
const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING };
|
||||
const size_t targetCount = sizeof(targets) / sizeof(targets[0]);
|
||||
|
||||
if (selection == _glfw.x11.PRIMARY)
|
||||
selectionString = &_glfw.x11.primarySelectionString;
|
||||
else
|
||||
selectionString = &_glfw.x11.clipboardString;
|
||||
|
||||
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
|
||||
return *selectionString;
|
||||
_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;
|
||||
}
|
||||
|
||||
free(*selectionString);
|
||||
*selectionString = NULL;
|
||||
|
||||
for (size_t i = 0; i < targetCount; i++)
|
||||
{
|
||||
char* data;
|
||||
@ -998,11 +1033,11 @@ static const char* getSelectionString(Atom selection)
|
||||
{
|
||||
if (targets[i] == XA_STRING)
|
||||
{
|
||||
*selectionString = convertLatin1toUTF8(string);
|
||||
selectionString = convertLatin1toUTF8(string);
|
||||
free(string);
|
||||
}
|
||||
else
|
||||
*selectionString = string;
|
||||
selectionString = string;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -1011,24 +1046,24 @@ static const char* getSelectionString(Atom selection)
|
||||
else if (actualType == targets[i])
|
||||
{
|
||||
if (targets[i] == XA_STRING)
|
||||
*selectionString = convertLatin1toUTF8(data);
|
||||
selectionString = convertLatin1toUTF8(data);
|
||||
else
|
||||
*selectionString = _glfw_strdup(data);
|
||||
selectionString = _glfw_strdup(data);
|
||||
}
|
||||
|
||||
XFree(data);
|
||||
|
||||
if (*selectionString)
|
||||
if (selectionString)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!*selectionString)
|
||||
if (!selectionString)
|
||||
{
|
||||
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
||||
"X11: Failed to convert selection to string");
|
||||
}
|
||||
|
||||
return *selectionString;
|
||||
return selectionString;
|
||||
}
|
||||
|
||||
// Make the specified window and its video mode active on its monitor
|
||||
@ -1098,21 +1133,6 @@ static void onConfigChange(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_atom_names(Atom *atoms, int count, char **atom_names) {
|
||||
_glfwGrabErrorHandlerX11();
|
||||
XGetAtomNames(_glfw.x11.display, atoms, count, atom_names);
|
||||
_glfwReleaseErrorHandlerX11();
|
||||
if (_glfw.x11.errorCode != Success) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
_glfwGrabErrorHandlerX11();
|
||||
atom_names[i] = XGetAtomName(_glfw.x11.display, atoms[i]);
|
||||
_glfwReleaseErrorHandlerX11();
|
||||
if (_glfw.x11.errorCode != Success) atom_names[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process the specified X event
|
||||
//
|
||||
static void processEvent(XEvent *event)
|
||||
@ -2849,22 +2869,48 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor UNUSED)
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformSetClipboardString(const char* string)
|
||||
{
|
||||
char* copy = _glfw_strdup(string);
|
||||
free(_glfw.x11.clipboardString);
|
||||
_glfw.x11.clipboardString = copy;
|
||||
static MimeAtom atom_for_mime(const char *mime) {
|
||||
for (size_t i = 0; i < _glfw.x11.mime_atoms.sz; i++) {
|
||||
MimeAtom ma = _glfw.x11.mime_atoms.array[i];
|
||||
if (strcmp(ma.mime, mime) == 0) {
|
||||
return ma;
|
||||
}
|
||||
}
|
||||
MimeAtom ma = {.mime=_glfw_strdup(mime), .atom=XInternAtom(_glfw.x11.display, mime, 0)};
|
||||
if (_glfw.x11.mime_atoms.capacity < _glfw.x11.mime_atoms.sz + 1) {
|
||||
_glfw.x11.mime_atoms.capacity += 32;
|
||||
_glfw.x11.mime_atoms.array = realloc(_glfw.x11.mime_atoms.array, _glfw.x11.mime_atoms.capacity);
|
||||
}
|
||||
_glfw.x11.mime_atoms.array[_glfw.x11.mime_atoms.sz++] = ma;
|
||||
return ma;
|
||||
}
|
||||
|
||||
XSetSelectionOwner(_glfw.x11.display,
|
||||
_glfw.x11.CLIPBOARD,
|
||||
_glfw.x11.helperWindowHandle,
|
||||
CurrentTime);
|
||||
|
||||
if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) !=
|
||||
_glfw.x11.helperWindowHandle)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"X11: Failed to become owner of clipboard selection");
|
||||
void _glfwPlatformSetClipboard(GLFWClipboardType t) {
|
||||
Atom which = None;
|
||||
_GLFWClipboardData *cd = NULL;
|
||||
AtomArray *aa = NULL;
|
||||
switch (t) {
|
||||
case GLFW_CLIPBOARD: which = _glfw.x11.CLIPBOARD; cd = &_glfw.clipboard; aa = &_glfw.x11.clipboard_atoms; break;
|
||||
case GLFW_PRIMARY_SELECTION: which = _glfw.x11.PRIMARY; cd = &_glfw.primary; aa = &_glfw.x11.primary_atoms; break;
|
||||
}
|
||||
XSetSelectionOwner(_glfw.x11.display, which, _glfw.x11.helperWindowHandle, CurrentTime);
|
||||
if (XGetSelectionOwner(_glfw.x11.display, which) != _glfw.x11.helperWindowHandle) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to become owner of clipboard selection");
|
||||
}
|
||||
if (aa->capacity < cd->num_mime_types + 32) {
|
||||
aa->capacity = cd->num_mime_types + 32;
|
||||
aa->array = malloc(sizeof(aa->array[0]) * aa->capacity);
|
||||
}
|
||||
aa->sz = 0;
|
||||
for (size_t i = 0; i < cd->num_mime_types; i++) {
|
||||
MimeAtom *a = aa->array + aa->sz++;
|
||||
if (strcmp(cd->mime_types[i], "text/plain") == 0) {
|
||||
a->atom = XA_ATOM; a->mime = "text/plain";
|
||||
a = aa->array + aa->sz++;
|
||||
a->atom = _glfw.x11.UTF8_STRING; a->mime = "text/plain";
|
||||
} else {
|
||||
*a = atom_for_mime(cd->mime_types[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2873,24 +2919,6 @@ 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);
|
||||
|
||||
@ -15,13 +15,14 @@ from typing import (
|
||||
|
||||
from kitty.cli import parse_args
|
||||
from kitty.cli_stub import HintsCLIOptions
|
||||
from kitty.clipboard import set_clipboard_string, set_primary_selection
|
||||
from kitty.constants import website_url
|
||||
from kitty.fast_data_types import get_options, set_clipboard_string, wcswidth
|
||||
from kitty.fast_data_types import get_options, wcswidth
|
||||
from kitty.key_encoding import KeyEvent
|
||||
from kitty.typing import BossType, KittyCommonOpts
|
||||
from kitty.utils import (
|
||||
ScreenSize, kitty_ansi_sanitizer_pat, resolve_custom_file,
|
||||
screen_size_function, set_primary_selection
|
||||
screen_size_function
|
||||
)
|
||||
|
||||
from ..tui.handler import Handler, result_handler
|
||||
|
||||
@ -20,6 +20,7 @@ from weakref import WeakValueDictionary
|
||||
from .child import cached_process_data, default_env, set_default_env
|
||||
from .cli import create_opts, parse_args
|
||||
from .cli_stub import CLIOptions
|
||||
from .clipboard import Clipboard, get_primary_selection, set_primary_selection, get_clipboard_string, set_clipboard_string
|
||||
from .conf.utils import BadLine, KeyAction, to_cmdline
|
||||
from .config import common_opts_as_dict, prepare_config_file_for_editing
|
||||
from .constants import (
|
||||
@ -29,19 +30,19 @@ from .constants import (
|
||||
)
|
||||
from .fast_data_types import (
|
||||
CLOSE_BEING_CONFIRMED, GLFW_MOD_ALT, GLFW_MOD_CONTROL, GLFW_MOD_SHIFT,
|
||||
GLFW_MOD_SUPER, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS,
|
||||
GLFW_MOD_SUPER, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, GLFW_PRIMARY_SELECTION,
|
||||
IMPERATIVE_CLOSE_REQUESTED, NO_CLOSE_REQUESTED, ChildMonitor, Color,
|
||||
EllipticCurveKey, KeyEvent, SingleKey, add_timer, apply_options_update,
|
||||
background_opacity_of, change_background_opacity, change_os_window_state,
|
||||
cocoa_set_menubar_title, create_os_window,
|
||||
current_application_quit_request, current_os_window, destroy_global_data,
|
||||
focus_os_window, get_boss, get_clipboard_string, get_options,
|
||||
get_os_window_size, global_font_size, mark_os_window_for_close,
|
||||
os_window_font_size, patch_global_colors, redirect_mouse_handling,
|
||||
ring_bell, safe_pipe, send_data_to_peer, set_application_quit_request,
|
||||
set_background_image, set_boss, set_clipboard_string, set_in_sequence_mode,
|
||||
set_options, set_os_window_size, set_os_window_title, thread_write,
|
||||
toggle_fullscreen, toggle_maximized, toggle_secure_input
|
||||
focus_os_window, get_boss, get_options, get_os_window_size,
|
||||
global_font_size, mark_os_window_for_close, os_window_font_size,
|
||||
patch_global_colors, redirect_mouse_handling, ring_bell, safe_pipe,
|
||||
send_data_to_peer, set_application_quit_request, set_background_image,
|
||||
set_boss, set_in_sequence_mode, set_options, set_os_window_size,
|
||||
set_os_window_title, thread_write, toggle_fullscreen, toggle_maximized,
|
||||
toggle_secure_input
|
||||
)
|
||||
from .key_encoding import get_name_to_functional_number_map
|
||||
from .keys import get_shortcut, shortcut_matches
|
||||
@ -60,10 +61,9 @@ from .types import _T, AsyncResponse, WindowSystemMouseEvent, ac
|
||||
from .typing import PopenType, TypedDict
|
||||
from .utils import (
|
||||
cleanup_ssh_control_masters, func_name, get_editor, get_new_os_window_size,
|
||||
get_primary_selection, is_path_in_temp_dir, less_version, log_error,
|
||||
macos_version, open_url, parse_address_spec, parse_uri_list,
|
||||
platform_window_id, remove_socket_file, safe_print, set_primary_selection,
|
||||
single_instance, startup_notification_handler, which
|
||||
is_path_in_temp_dir, less_version, log_error, macos_version, open_url,
|
||||
parse_address_spec, parse_uri_list, platform_window_id, remove_socket_file,
|
||||
safe_print, single_instance, startup_notification_handler, which
|
||||
)
|
||||
from .window import CommandOutput, CwdRequest, Window
|
||||
|
||||
@ -239,6 +239,8 @@ class Boss:
|
||||
prewarm: PrewarmProcess,
|
||||
):
|
||||
set_layout_options(opts)
|
||||
self.clipboard = Clipboard()
|
||||
self.primary_selection = Clipboard(GLFW_PRIMARY_SELECTION)
|
||||
self.update_check_started = False
|
||||
self.encryption_key = EllipticCurveKey()
|
||||
self.encryption_public_key = f'{RC_ENCRYPTION_PROTOCOL_VERSION}:{base64.b85encode(self.encryption_key.public).decode("ascii")}'
|
||||
|
||||
63
kitty/clipboard.py
Normal file
63
kitty/clipboard.py
Normal file
@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python
|
||||
# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import io
|
||||
from typing import IO, Callable, Dict, Union
|
||||
|
||||
from .constants import supports_primary_selection
|
||||
from .fast_data_types import GLFW_CLIPBOARD, get_boss, set_clipboard_data_types
|
||||
|
||||
DataType = Union[bytes, 'IO[bytes]']
|
||||
|
||||
|
||||
class Clipboard:
|
||||
|
||||
def __init__(self, clipboard_type: int = GLFW_CLIPBOARD) -> None:
|
||||
self.data: Dict[str, DataType] = {}
|
||||
self.clipboard_type = clipboard_type
|
||||
self.enabled = self.clipboard_type == GLFW_CLIPBOARD or supports_primary_selection
|
||||
|
||||
def set_text(self, x: Union[str, bytes]) -> None:
|
||||
if self.enabled:
|
||||
self.data.clear()
|
||||
if isinstance(x, str):
|
||||
x = x.encode('utf-8')
|
||||
self.data['text/plain'] = x
|
||||
set_clipboard_data_types(self.clipboard_type, tuple(self.data))
|
||||
|
||||
def get_text(self) -> str:
|
||||
raise NotImplementedError('TODO: Implement this')
|
||||
|
||||
def __call__(self, mime: str) -> Callable[[], bytes]:
|
||||
data = self.data.get(mime, b'')
|
||||
if isinstance(data, bytes):
|
||||
def chunker() -> bytes:
|
||||
nonlocal data
|
||||
assert isinstance(data, bytes)
|
||||
ans = data
|
||||
data = b''
|
||||
return ans
|
||||
return chunker
|
||||
|
||||
data.seek(0, 0)
|
||||
|
||||
def io_chunker() -> bytes:
|
||||
assert not isinstance(data, bytes)
|
||||
return data.read(io.DEFAULT_BUFFER_SIZE)
|
||||
return io_chunker
|
||||
|
||||
|
||||
def set_clipboard_string(x: Union[str, bytes]) -> None:
|
||||
get_boss().clipboard.set_text(x)
|
||||
|
||||
|
||||
def get_clipboard_string() -> str:
|
||||
return get_boss().clipboard.get_text()
|
||||
|
||||
|
||||
def set_primary_selection(x: Union[str, bytes]) -> None:
|
||||
get_boss().primary_selection.set_text(x)
|
||||
|
||||
|
||||
def get_primary_selection() -> str:
|
||||
return get_boss().primary_selection.get_text()
|
||||
@ -1,7 +1,7 @@
|
||||
import termios
|
||||
from ctypes import Array, c_ubyte
|
||||
from typing import (
|
||||
Any, AnyStr, Callable, Dict, List, NewType, Optional, Tuple, TypedDict,
|
||||
Any, Callable, Dict, List, NewType, Optional, Tuple, TypedDict,
|
||||
Union, Iterator
|
||||
)
|
||||
|
||||
@ -13,6 +13,8 @@ from kitty.options.types import Options
|
||||
from kitty.types import SignalInfo
|
||||
|
||||
# Constants {{{
|
||||
GLFW_PRIMARY_SELECTION: int
|
||||
GLFW_CLIPBOARD: int
|
||||
CLD_KILLED: int
|
||||
CLD_STOPPED: int
|
||||
CLD_CONTINUED: int
|
||||
@ -304,14 +306,6 @@ def log_error_string(s: str) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def set_primary_selection(x: Union[bytes, str]) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def get_primary_selection() -> Optional[bytes]:
|
||||
pass
|
||||
|
||||
|
||||
def redirect_std_streams(devnull: str) -> None:
|
||||
pass
|
||||
|
||||
@ -597,10 +591,6 @@ def set_in_sequence_mode(yes: bool) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def set_clipboard_string(data: AnyStr) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def set_background_image(
|
||||
path: Optional[str],
|
||||
os_window_ids: Tuple[int, ...],
|
||||
@ -764,10 +754,6 @@ def global_font_size(val: float = -1.) -> float:
|
||||
pass
|
||||
|
||||
|
||||
def get_clipboard_string() -> str:
|
||||
pass
|
||||
|
||||
|
||||
def focus_os_window(os_window_id: int, also_raise: bool = True) -> bool:
|
||||
pass
|
||||
|
||||
@ -1486,3 +1472,4 @@ class SingleKey:
|
||||
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: ...
|
||||
|
||||
7
kitty/glfw-wrapper.c
generated
7
kitty/glfw-wrapper.c
generated
@ -365,11 +365,8 @@ load_glfw(const char* path) {
|
||||
*(void **) (&glfwGetGamepadState_impl) = dlsym(handle, "glfwGetGamepadState");
|
||||
if (glfwGetGamepadState_impl == NULL) fail("Failed to load glfw function glfwGetGamepadState with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwSetClipboardString_impl) = dlsym(handle, "glfwSetClipboardString");
|
||||
if (glfwSetClipboardString_impl == NULL) fail("Failed to load glfw function glfwSetClipboardString with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwGetClipboardString_impl) = dlsym(handle, "glfwGetClipboardString");
|
||||
if (glfwGetClipboardString_impl == NULL) fail("Failed to load glfw function glfwGetClipboardString with error: %s", dlerror());
|
||||
*(void **) (&glfwSetClipboardDataTypes_impl) = dlsym(handle, "glfwSetClipboardDataTypes");
|
||||
if (glfwSetClipboardDataTypes_impl == NULL) fail("Failed to load glfw function glfwSetClipboardDataTypes 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());
|
||||
|
||||
21
kitty/glfw-wrapper.h
generated
21
kitty/glfw-wrapper.h
generated
@ -1456,6 +1456,17 @@ typedef void (* GLFWuserdatafun)(unsigned long long, void*);
|
||||
typedef void (* GLFWtickcallback)(void*);
|
||||
typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin);
|
||||
typedef char* (* GLFWcurrentselectionfun)(void);
|
||||
typedef void (* GLFWclipboarddatafreefun)(void* data);
|
||||
typedef struct GLFWDataChunk {
|
||||
const char *data;
|
||||
size_t sz;
|
||||
GLFWclipboarddatafreefun free;
|
||||
void *iter, *free_data;
|
||||
} GLFWDataChunk;
|
||||
typedef enum {
|
||||
GLFW_CLIPBOARD, GLFW_PRIMARY_SELECTION
|
||||
} GLFWClipboardType;
|
||||
typedef GLFWDataChunk (* GLFWclipboarditerfun)(const char *mime_type, void *iter, GLFWClipboardType ctype);
|
||||
|
||||
/*! @brief Video mode type.
|
||||
*
|
||||
@ -2081,13 +2092,9 @@ typedef int (*glfwGetGamepadState_func)(int, GLFWgamepadstate*);
|
||||
GFW_EXTERN glfwGetGamepadState_func glfwGetGamepadState_impl;
|
||||
#define glfwGetGamepadState glfwGetGamepadState_impl
|
||||
|
||||
typedef void (*glfwSetClipboardString_func)(GLFWwindow*, const char*);
|
||||
GFW_EXTERN glfwSetClipboardString_func glfwSetClipboardString_impl;
|
||||
#define glfwSetClipboardString glfwSetClipboardString_impl
|
||||
|
||||
typedef const char* (*glfwGetClipboardString_func)(GLFWwindow*);
|
||||
GFW_EXTERN glfwGetClipboardString_func glfwGetClipboardString_impl;
|
||||
#define glfwGetClipboardString glfwGetClipboardString_impl
|
||||
typedef void (*glfwSetClipboardDataTypes_func)(GLFWClipboardType, const char* const*, size_t, GLFWclipboarditerfun);
|
||||
GFW_EXTERN glfwSetClipboardDataTypes_func glfwSetClipboardDataTypes_impl;
|
||||
#define glfwSetClipboardDataTypes glfwSetClipboardDataTypes_impl
|
||||
|
||||
typedef monotonic_t (*glfwGetTime_func)(void);
|
||||
GFW_EXTERN glfwGetTime_func glfwGetTime_impl;
|
||||
|
||||
95
kitty/glfw.c
95
kitty/glfw.c
@ -1263,13 +1263,6 @@ toggle_secure_input(PYNOARG) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
get_clipboard_string(PYNOARG) {
|
||||
OSWindow *w = current_os_window();
|
||||
if (w) return Py_BuildValue("s", glfwGetClipboardString(w->handle));
|
||||
return Py_BuildValue("s", "");
|
||||
}
|
||||
|
||||
static void
|
||||
ring_audio_bell(void) {
|
||||
static monotonic_t last_bell_at = -1;
|
||||
@ -1298,16 +1291,6 @@ get_content_scale_for_window(PYNOARG) {
|
||||
return Py_BuildValue("ff", xscale, yscale);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
set_clipboard_string(PyObject UNUSED *self, PyObject *args) {
|
||||
char *title;
|
||||
Py_ssize_t sz;
|
||||
if(!PyArg_ParseTuple(args, "s#", &title, &sz)) return NULL;
|
||||
OSWindow *w = current_os_window();
|
||||
if (w) glfwSetClipboardString(w->handle, title);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static OSWindow*
|
||||
find_os_window(id_type os_window_id) {
|
||||
for (size_t i = 0; i < global_state.num_os_windows; i++) {
|
||||
@ -1441,28 +1424,6 @@ cocoa_window_id(PyObject UNUSED *self, PyObject *os_wid) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
get_primary_selection(PYNOARG) {
|
||||
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;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
set_primary_selection(PyObject UNUSED *self, PyObject *args) {
|
||||
char *text;
|
||||
Py_ssize_t sz;
|
||||
if (!PyArg_ParseTuple(args, "s#", &text, &sz)) return NULL;
|
||||
if (glfwSetPrimarySelectionString) {
|
||||
OSWindow *w = current_os_window();
|
||||
if (w) glfwSetPrimarySelectionString(w->handle, text);
|
||||
}
|
||||
else log_error("Failed to load glfwSetPrimarySelectionString");
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
set_custom_cursor(PyObject *self UNUSED, PyObject *args) {
|
||||
int shape;
|
||||
@ -1618,26 +1579,73 @@ set_ignore_os_keyboard_processing(bool enabled) {
|
||||
glfwSetIgnoreOSKeyboardProcessing(enabled);
|
||||
}
|
||||
|
||||
static void
|
||||
decref_pyobj(void *x) {
|
||||
Py_XDECREF(x);
|
||||
}
|
||||
|
||||
static GLFWDataChunk
|
||||
get_clipboard_data(const char *mime_type, void *iter, GLFWClipboardType ct) {
|
||||
GLFWDataChunk ans = {.iter=iter, .free=decref_pyobj};
|
||||
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;
|
||||
}
|
||||
PyObject *i = PyObject_CallFunction(c, "s", mime_type);
|
||||
Py_DECREF(c);
|
||||
if (!i) {
|
||||
PyErr_Print();
|
||||
return ans;
|
||||
}
|
||||
ans.iter = i;
|
||||
return ans;
|
||||
}
|
||||
if (mime_type == NULL) {
|
||||
Py_XDECREF(iter);
|
||||
return ans;
|
||||
}
|
||||
|
||||
PyObject *ret = PyObject_CallFunctionObjArgs(iter, NULL);
|
||||
if (ret == NULL) return ans;
|
||||
ans.data = PyBytes_AS_STRING(ret);
|
||||
ans.sz = PyBytes_GET_SIZE(ret);
|
||||
ans.free_data = ret;
|
||||
return ans;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
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);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// Boilerplate {{{
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
METHODB(set_custom_cursor, METH_VARARGS),
|
||||
{"create_os_window", (PyCFunction)(void (*) (void))(create_os_window), METH_VARARGS | METH_KEYWORDS, NULL},
|
||||
METHODB(set_default_window_icon, METH_VARARGS),
|
||||
METHODB(get_clipboard_string, METH_NOARGS),
|
||||
METHODB(set_clipboard_data_types, METH_VARARGS),
|
||||
METHODB(toggle_secure_input, METH_NOARGS),
|
||||
METHODB(get_content_scale_for_window, METH_NOARGS),
|
||||
METHODB(ring_bell, METH_NOARGS),
|
||||
METHODB(set_clipboard_string, METH_VARARGS),
|
||||
METHODB(toggle_fullscreen, METH_VARARGS),
|
||||
METHODB(toggle_maximized, METH_VARARGS),
|
||||
METHODB(change_os_window_state, METH_VARARGS),
|
||||
METHODB(glfw_window_hint, METH_VARARGS),
|
||||
METHODB(get_primary_selection, METH_NOARGS),
|
||||
METHODB(x11_display, METH_NOARGS),
|
||||
METHODB(get_click_interval, METH_NOARGS),
|
||||
METHODB(x11_window_id, METH_O),
|
||||
METHODB(set_primary_selection, METH_VARARGS),
|
||||
METHODB(strip_csi, METH_O),
|
||||
#ifndef __APPLE__
|
||||
METHODB(dbus_send_notification, METH_VARARGS),
|
||||
@ -1672,6 +1680,7 @@ init_glfw(PyObject *m) {
|
||||
ADDC(GLFW_REPEAT);
|
||||
ADDC(true); ADDC(false);
|
||||
ADDC(GLFW_IBEAM_CURSOR); ADDC(GLFW_HAND_CURSOR); ADDC(GLFW_ARROW_CURSOR);
|
||||
ADDC(GLFW_PRIMARY_SELECTION); ADDC(GLFW_CLIPBOARD);
|
||||
|
||||
/* start glfw functional keys (auto generated by gen-key-constants.py do not edit) */
|
||||
ADDC(GLFW_FKEY_ESCAPE);
|
||||
|
||||
@ -14,17 +14,15 @@ from .boss import Boss
|
||||
from .child import Child
|
||||
from .cli import parse_args
|
||||
from .cli_stub import LaunchCLIOptions
|
||||
from .clipboard import set_clipboard_string, set_primary_selection
|
||||
from .constants import kitty_exe, shell_path
|
||||
from .fast_data_types import (
|
||||
add_timer, get_boss, get_options, get_os_window_title,
|
||||
patch_color_profiles, set_clipboard_string
|
||||
add_timer, get_boss, get_options, get_os_window_title, patch_color_profiles
|
||||
)
|
||||
from .options.utils import env as parse_env
|
||||
from .tabs import Tab, TabManager
|
||||
from .types import OverlayType, run_once
|
||||
from .utils import (
|
||||
get_editor, log_error, resolve_custom_file, set_primary_selection, which
|
||||
)
|
||||
from .utils import get_editor, log_error, resolve_custom_file, which
|
||||
from .window import CwdRequest, CwdRequestType, Watchers, Window
|
||||
|
||||
try:
|
||||
|
||||
@ -19,8 +19,7 @@ from typing import (
|
||||
|
||||
from .constants import (
|
||||
appname, clear_handled_signals, config_dir, is_macos, is_wayland,
|
||||
read_kitty_resource, runtime_dir, shell_path, ssh_control_master_template,
|
||||
supports_primary_selection
|
||||
read_kitty_resource, runtime_dir, shell_path, ssh_control_master_template
|
||||
)
|
||||
from .fast_data_types import Color, open_tty
|
||||
from .rgb import to_color
|
||||
@ -230,20 +229,6 @@ def fit_image(width: int, height: int, pwidth: int, pheight: int) -> Tuple[int,
|
||||
return int(width), int(height)
|
||||
|
||||
|
||||
def set_primary_selection(text: Union[str, bytes]) -> None:
|
||||
if not supports_primary_selection:
|
||||
return # There is no primary selection
|
||||
from kitty.fast_data_types import set_primary_selection as s
|
||||
s(text)
|
||||
|
||||
|
||||
def get_primary_selection() -> str:
|
||||
if not supports_primary_selection:
|
||||
return '' # There is no primary selection
|
||||
from kitty.fast_data_types import get_primary_selection as g
|
||||
return (g() or b'').decode('utf-8', 'replace')
|
||||
|
||||
|
||||
def base64_encode(
|
||||
integer: int,
|
||||
chars: str = string.ascii_uppercase + string.ascii_lowercase + string.digits +
|
||||
|
||||
@ -20,6 +20,10 @@ from typing import (
|
||||
|
||||
from .child import ProcessDesc
|
||||
from .cli_stub import CLIOptions
|
||||
from .clipboard import (
|
||||
get_clipboard_string, get_primary_selection, set_clipboard_string,
|
||||
set_primary_selection
|
||||
)
|
||||
from .config import build_ansi_color_table
|
||||
from .constants import (
|
||||
appname, clear_handled_signals, config_dir, is_macos, wakeup_io_loop
|
||||
@ -34,23 +38,25 @@ from .fast_data_types import (
|
||||
KeyEvent, Screen, add_timer, add_window, cell_size_for_window,
|
||||
click_mouse_cmd_output, click_mouse_url, compile_program,
|
||||
current_os_window, encode_key_for_tty, get_boss, get_click_interval,
|
||||
get_clipboard_string, get_options, init_cell_program, mark_os_window_dirty,
|
||||
mouse_selection, move_cursor_to_mouse_if_in_prompt, pt_to_px,
|
||||
set_clipboard_string, set_titlebar_color, set_window_logo,
|
||||
set_window_padding, set_window_render_data, update_ime_position_for_window,
|
||||
update_window_title, update_window_visibility, wakeup_main_loop
|
||||
get_options, init_cell_program, mark_os_window_dirty, mouse_selection,
|
||||
move_cursor_to_mouse_if_in_prompt, pt_to_px, set_titlebar_color,
|
||||
set_window_logo, set_window_padding, set_window_render_data,
|
||||
update_ime_position_for_window, update_window_title,
|
||||
update_window_visibility, wakeup_main_loop
|
||||
)
|
||||
from .keys import keyboard_mode_name, mod_mask
|
||||
from .notify import NotificationCommand, handle_notification_cmd, sanitize_identifier_pat
|
||||
from .notify import (
|
||||
NotificationCommand, handle_notification_cmd, sanitize_identifier_pat
|
||||
)
|
||||
from .options.types import Options
|
||||
from .rgb import to_color
|
||||
from .terminfo import get_capabilities
|
||||
from .types import MouseEvent, OverlayType, WindowGeometry, ac, run_once
|
||||
from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict
|
||||
from .utils import (
|
||||
docs_url, get_primary_selection, kitty_ansi_sanitizer_pat, load_shaders,
|
||||
log_error, open_cmd, open_url, parse_color_set, path_from_osc7_url,
|
||||
resolve_custom_file, resolved_shell, sanitize_title, set_primary_selection
|
||||
docs_url, kitty_ansi_sanitizer_pat, load_shaders, log_error, open_cmd,
|
||||
open_url, parse_color_set, path_from_osc7_url, resolve_custom_file,
|
||||
resolved_shell, sanitize_title
|
||||
)
|
||||
|
||||
MatchPatternType = Union[Pattern[str], Tuple[Pattern[str], Optional[Pattern[str]]]]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user