diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 3d1bfc9ed..63f213d20 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -1585,8 +1585,7 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { (!sendType || [sendType isEqual:NSPasteboardTypeString] || [sendType isEqual:@"NSStringPboardType"]) && (!returnType || [returnType isEqual:NSPasteboardTypeString] || [returnType isEqual:@"NSStringPboardType"]) ) { - NSString *text = [self accessibilitySelectedText]; - if (text && text.length > 0) return self; + if (_glfw.callbacks.has_current_selection && _glfw.callbacks.has_current_selection()) return self; } return [super validRequestorForSendType:sendType returnType:returnType]; } @@ -1594,17 +1593,21 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { // Selected text as input to be sent to Services - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard types:(NSArray *)types { - NSString *text = [self accessibilitySelectedText]; - if (text && [text length] > 0) { + if (!_glfw.callbacks.get_current_selection) return NO; + char *text = _glfw.callbacks.get_current_selection(); + if (!text) return NO; + BOOL ans = NO; + if (text[0]) { if ([types containsObject:NSPasteboardTypeString] == YES) { [pboard declareTypes:@[NSPasteboardTypeString] owner:self]; - return [pboard setString:text forType:NSPasteboardTypeString]; + ans = [pboard setString:@(text) forType:NSPasteboardTypeString]; } else if ([types containsObject:@"NSStringPboardType"] == YES) { [pboard declareTypes:@[@"NSStringPboardType"] owner:self]; - return [pboard setString:text forType:NSPasteboardTypeString]; + ans = [pboard setString:@(text) forType:NSPasteboardTypeString]; } + free(text); } - return NO; + return ans; } // Service output to be handled diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 863ae6040..3d02e5448 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1719,6 +1719,7 @@ typedef void (* GLFWtickcallback)(void*); typedef void (* GLFWactivationcallback)(GLFWwindow *window, const char *token, void *data); 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 bool (* GLFWhascurrentselectionfun)(void); typedef void (* GLFWclipboarddatafreefun)(void* data); typedef struct GLFWDataChunk { const char *data; @@ -1889,6 +1890,7 @@ GLFWAPI void glfwUpdateTimer(unsigned long long timer_id, monotonic_t interval, GLFWAPI void glfwRemoveTimer(unsigned long long); GLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun function); GLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselectionfun callback); +GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascurrentselectionfun callback); /*! @brief Terminates the GLFW library. * diff --git a/glfw/init.c b/glfw/init.c index f85252e67..d9fd00b75 100644 --- a/glfw/init.c +++ b/glfw/init.c @@ -395,3 +395,10 @@ GLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselec _GLFW_SWAP_POINTERS(_glfw.callbacks.get_current_selection, cbfun); return cbfun; } + +GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascurrentselectionfun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(_glfw.callbacks.has_current_selection, cbfun); + return cbfun; +} diff --git a/glfw/internal.h b/glfw/internal.h index 5873c5ec9..fe4125a5f 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -634,6 +634,7 @@ struct _GLFWlibrary GLFWapplicationclosefun application_close; GLFWdrawtextfun draw_text; GLFWcurrentselectionfun get_current_selection; + GLFWhascurrentselectionfun has_current_selection; } callbacks; diff --git a/kitty/boss.py b/kitty/boss.py index 6e687ced2..1947fde2e 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -1985,6 +1985,12 @@ class Boss: return w.text_for_selection() return None + def has_active_selection(self) -> bool: + w = self.active_window + if w is not None and not w.destroyed: + return w.has_selection() + return False + @ac('cp', ''' Copy the selection from the active window to the specified buffer diff --git a/kitty/glfw-wrapper.c b/kitty/glfw-wrapper.c index 4c76622cc..6ec499bd1 100644 --- a/kitty/glfw-wrapper.c +++ b/kitty/glfw-wrapper.c @@ -41,6 +41,9 @@ load_glfw(const char* path) { *(void **) (&glfwSetCurrentSelectionCallback_impl) = dlsym(handle, "glfwSetCurrentSelectionCallback"); if (glfwSetCurrentSelectionCallback_impl == NULL) fail("Failed to load glfw function glfwSetCurrentSelectionCallback with error: %s", dlerror()); + *(void **) (&glfwSetHasCurrentSelectionCallback_impl) = dlsym(handle, "glfwSetHasCurrentSelectionCallback"); + if (glfwSetHasCurrentSelectionCallback_impl == NULL) fail("Failed to load glfw function glfwSetHasCurrentSelectionCallback with error: %s", dlerror()); + *(void **) (&glfwTerminate_impl) = dlsym(handle, "glfwTerminate"); if (glfwTerminate_impl == NULL) fail("Failed to load glfw function glfwTerminate with error: %s", dlerror()); diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 70ec8f30b..d847480b2 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1457,6 +1457,7 @@ typedef void (* GLFWtickcallback)(void*); typedef void (* GLFWactivationcallback)(GLFWwindow *window, const char *token, void *data); 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 bool (* GLFWhascurrentselectionfun)(void); typedef void (* GLFWclipboarddatafreefun)(void* data); typedef struct GLFWDataChunk { const char *data; @@ -1662,6 +1663,10 @@ typedef GLFWcurrentselectionfun (*glfwSetCurrentSelectionCallback_func)(GLFWcurr GFW_EXTERN glfwSetCurrentSelectionCallback_func glfwSetCurrentSelectionCallback_impl; #define glfwSetCurrentSelectionCallback glfwSetCurrentSelectionCallback_impl +typedef GLFWhascurrentselectionfun (*glfwSetHasCurrentSelectionCallback_func)(GLFWhascurrentselectionfun); +GFW_EXTERN glfwSetHasCurrentSelectionCallback_func glfwSetHasCurrentSelectionCallback_impl; +#define glfwSetHasCurrentSelectionCallback glfwSetHasCurrentSelectionCallback_impl + typedef void (*glfwTerminate_func)(void); GFW_EXTERN glfwTerminate_func glfwTerminate_impl; #define glfwTerminate glfwTerminate_impl diff --git a/kitty/glfw.c b/kitty/glfw.c index f3dbe69a7..3c490c2b1 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -496,6 +496,16 @@ get_current_selection(void) { return ans; } +static bool +has_current_selection(void) { + if (!global_state.boss) return false; + PyObject *ret = PyObject_CallMethod(global_state.boss, "has_active_selection", NULL); + if (!ret) { PyErr_Print(); return false; } + bool ans = ret == Py_True; + Py_DECREF(ret); + return ans; +} + static void get_window_dpi(GLFWwindow *w, double *x, double *y); @@ -800,6 +810,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) { if (OPT(hide_window_decorations) & 1) glfwWindowHint(GLFW_DECORATED, false); glfwSetApplicationCloseCallback(application_close_requested_callback); glfwSetCurrentSelectionCallback(get_current_selection); + glfwSetHasCurrentSelectionCallback(has_current_selection); #ifdef __APPLE__ cocoa_set_activation_policy(OPT(macos_hide_from_tasks)); glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, true); diff --git a/kitty/screen.c b/kitty/screen.c index ba04248a2..3287d6f1e 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -2785,7 +2785,12 @@ screen_detect_url(Screen *screen, unsigned int x, unsigned int y) { #define WRAP2B(name) static PyObject* name(Screen *self, PyObject *args) { unsigned int a, b; int p; if(!PyArg_ParseTuple(args, "IIp", &a, &b, &p)) return NULL; screen_##name(self, a, b, (bool)p); Py_RETURN_NONE; } WRAP0(garbage_collect_hyperlink_pool) -WRAP0x(has_selection) + +static PyObject* +has_selection(Screen *self, PyObject *a UNUSED) { + if (screen_has_selection(self)) Py_RETURN_TRUE; + Py_RETURN_FALSE; +} static PyObject* hyperlinks_as_list(Screen *self, PyObject *args UNUSED) { @@ -4076,7 +4081,7 @@ static PyMethodDef methods[] = { MND(cursor_down1, METH_VARARGS) MND(cursor_forward, METH_VARARGS) {"index", (PyCFunction)xxx_index, METH_VARARGS, ""}, - {"has_selection", (PyCFunction)xxx_has_selection, METH_VARARGS, ""}, + {"has_selection", (PyCFunction)has_selection, METH_VARARGS, ""}, MND(set_pending_timeout, METH_O) MND(as_text, METH_VARARGS) MND(as_text_non_visual, METH_VARARGS) diff --git a/kitty/window.py b/kitty/window.py index b2f3ab29c..c30b11259 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -1354,6 +1354,9 @@ class Window: lines = self.screen.text_for_selection(as_ansi, strip_trailing_spaces) return ''.join(lines) + def has_selection(self) -> bool: + return self.screen.has_selection() + def call_watchers(self, which: Iterable[Watcher], data: Dict[str, Any]) -> None: boss = get_boss() for w in which: