diff --git a/glfw/glfw.py b/glfw/glfw.py index 0545fe5f2..4186103ec 100755 --- a/glfw/glfw.py +++ b/glfw/glfw.py @@ -94,12 +94,13 @@ class Arg: class Function: - def __init__(self, declaration): + def __init__(self, declaration, check_fail=True): + self.check_fail = check_fail m = re.match( r'(.+?)\s+(glfw[A-Z][a-zA-Z0-9]+)[(](.+)[)]$', declaration ) if m is None: - raise SystemExit('Failed to parse ' + declaration) + raise SystemExit('Failed to parse ' + repr(declaration)) self.restype = m.group(1).strip() self.name = m.group(2) args = m.group(3).strip().split(',') @@ -121,9 +122,10 @@ class Function: ans = '*(void **) (&{name}_impl) = dlsym(handle, "{name}");'.format( name=self.name ) - ans += '\n if ({name}_impl == NULL) fail("Failed to load glfw function {name} with error: %s", dlerror());'.format( - name=self.name - ) + if self.check_fail: + ans += '\n if ({name}_impl == NULL) fail("Failed to load glfw function {name} with error: %s", dlerror());'.format( + name=self.name + ) return ans @@ -138,6 +140,17 @@ def generate_wrappers(glfw_header, glfw_native_header): if 'VkInstance' in decl: continue functions.append(Function(decl)) + for line in '''\ + void* glfwGetCocoaWindow(GLFWwindow* window) + uint32_t glfwGetCocoaMonitor(GLFWmonitor* monitor) + void* glfwGetX11Display(void) + int32_t glfwGetX11Window(GLFWwindow* window) + void glfwSetX11SelectionString(const char* string) + const char* glfwGetX11SelectionString(void) +'''.splitlines(): + if line: + functions.append(Function(line.strip(), check_fail=False)) + declarations = [f.declaration() for f in functions] p = src.find(' * GLFW API tokens') p = src.find('*/', p) diff --git a/kitty/constants.py b/kitty/constants.py index 1e7a40c66..e74ce2b8b 100644 --- a/kitty/constants.py +++ b/kitty/constants.py @@ -4,11 +4,10 @@ import os import pwd -import ctypes import sys from collections import namedtuple -from .fast_data_types import set_boss as set_c_boss, handle_for_window_id +from .fast_data_types import set_boss as set_c_boss appname = 'kitty' version = (0, 5, 1) @@ -59,67 +58,4 @@ except KeyError: print('Failed to read login shell from /etc/passwd for current user, falling back to /bin/sh', file=sys.stderr) shell_path = '/bin/sh' -GLint = ctypes.c_int if ctypes.sizeof(ctypes.c_int) == 4 else ctypes.c_long -GLuint = ctypes.c_uint if ctypes.sizeof(ctypes.c_uint) == 4 else ctypes.c_ulong -GLfloat = ctypes.c_float -if ctypes.sizeof(GLfloat) != 4: - raise RuntimeError('float size is not 4') -if ctypes.sizeof(GLint) != 4: - raise RuntimeError('int size is not 4') - - -def get_glfw_lib_name(): - try: - for line in open('/proc/self/maps'): - lib = line.split()[-1] - if '/libglfw.so' in lib: - return lib - except Exception as err: - try: - print(str(err), file=sys.stderr) - except Exception: - pass - return 'libglfw.so.3' - - -def glfw_lib(): - ans = getattr(glfw_lib, 'ans', None) - if ans is None: - ans = glfw_lib.ans = ctypes.CDLL('libglfw.3.dylib' if isosx else get_glfw_lib_name()) - return ans - - -def selection_clipboard_funcs(): - ans = getattr(selection_clipboard_funcs, 'ans', None) - if ans is None: - lib = glfw_lib() - if hasattr(lib, 'glfwGetX11SelectionString'): - g = lib.glfwGetX11SelectionString - g.restype = ctypes.c_char_p - g.argtypes = [] - s = lib.glfwSetX11SelectionString - s.restype = None - s.argtypes = [ctypes.c_char_p] - ans = g, s - else: - ans = None, None - selection_clipboard_funcs.ans = ans - return ans - - -def x11_window_id(window_id): - lib = glfw_lib() - lib.glfwGetX11Window.restype = ctypes.c_int32 - lib.glfwGetX11Window.argtypes = [ctypes.c_void_p] - return lib.glfwGetX11Window(handle_for_window_id(window_id)) - - -def x11_display(): - lib = glfw_lib() - ans = lib.glfwGetX11Display - ans.restype = ctypes.c_void_p - ans.argtypes = [] - return ans() - - -iswayland = not isosx and hasattr(glfw_lib(), 'glfwGetWaylandDisplay') +iswayland = False diff --git a/kitty/glfw-wrapper.c b/kitty/glfw-wrapper.c index a40d38c8e..93bd22111 100644 --- a/kitty/glfw-wrapper.c +++ b/kitty/glfw-wrapper.c @@ -341,6 +341,18 @@ load_glfw(const char* path) { *(void **) (&glfwGetRequiredInstanceExtensions_impl) = dlsym(handle, "glfwGetRequiredInstanceExtensions"); if (glfwGetRequiredInstanceExtensions_impl == NULL) fail("Failed to load glfw function glfwGetRequiredInstanceExtensions with error: %s", dlerror()); + *(void **) (&glfwGetCocoaWindow_impl) = dlsym(handle, "glfwGetCocoaWindow"); + + *(void **) (&glfwGetCocoaMonitor_impl) = dlsym(handle, "glfwGetCocoaMonitor"); + + *(void **) (&glfwGetX11Display_impl) = dlsym(handle, "glfwGetX11Display"); + + *(void **) (&glfwGetX11Window_impl) = dlsym(handle, "glfwGetX11Window"); + + *(void **) (&glfwSetX11SelectionString_impl) = dlsym(handle, "glfwSetX11SelectionString"); + + *(void **) (&glfwGetX11SelectionString_impl) = dlsym(handle, "glfwGetX11SelectionString"); + return NULL; } diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 9cb4d61a1..52ae153b2 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1783,4 +1783,28 @@ typedef const char** (*glfwGetRequiredInstanceExtensions_func)(uint32_t*); glfwGetRequiredInstanceExtensions_func glfwGetRequiredInstanceExtensions_impl; #define glfwGetRequiredInstanceExtensions glfwGetRequiredInstanceExtensions_impl +typedef void* (*glfwGetCocoaWindow_func)(GLFWwindow*); +glfwGetCocoaWindow_func glfwGetCocoaWindow_impl; +#define glfwGetCocoaWindow glfwGetCocoaWindow_impl + +typedef uint32_t (*glfwGetCocoaMonitor_func)(GLFWmonitor*); +glfwGetCocoaMonitor_func glfwGetCocoaMonitor_impl; +#define glfwGetCocoaMonitor glfwGetCocoaMonitor_impl + +typedef void* (*glfwGetX11Display_func)(); +glfwGetX11Display_func glfwGetX11Display_impl; +#define glfwGetX11Display glfwGetX11Display_impl + +typedef int32_t (*glfwGetX11Window_func)(GLFWwindow*); +glfwGetX11Window_func glfwGetX11Window_impl; +#define glfwGetX11Window glfwGetX11Window_impl + +typedef void (*glfwSetX11SelectionString_func)(const char*); +glfwSetX11SelectionString_func glfwSetX11SelectionString_impl; +#define glfwSetX11SelectionString glfwSetX11SelectionString_impl + +typedef const char* (*glfwGetX11SelectionString_func)(); +glfwGetX11SelectionString_func glfwGetX11SelectionString_impl; +#define glfwGetX11SelectionString glfwGetX11SelectionString_impl + const char* load_glfw(const char* path); diff --git a/kitty/glfw.c b/kitty/glfw.c index 0b5740c29..43b783f51 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -537,6 +537,45 @@ primary_monitor_content_scale(PyObject UNUSED *self) { return Py_BuildValue("ff", xscale, yscale); } +static PyObject* +x11_display(PyObject UNUSED *self) { + if (glfwGetX11Display) { + return PyLong_FromVoidPtr(glfwGetX11Display()); + } else fprintf(stderr, "Failed to load glfwGetX11Display\n"); + Py_RETURN_NONE; +} + +static PyObject* +x11_window_id(PyObject UNUSED *self, PyObject *os_wid) { + if (glfwGetX11Window) { + id_type os_window_id = PyLong_AsUnsignedLongLong(os_wid); + for (size_t i = 0; i < global_state.num_os_windows; i++) { + OSWindow *w = global_state.os_windows + i; + if (w->id == os_window_id) return Py_BuildValue("l", (long)glfwGetX11Window(w->handle)); + } + } + else { PyErr_SetString(PyExc_RuntimeError, "Failed to load glfwGetX11Window"); return NULL; } + PyErr_SetString(PyExc_ValueError, "No OSWindow with the specified id found"); + return NULL; +} + +static PyObject* +get_primary_selection(PyObject UNUSED *self) { + if (glfwGetX11SelectionString) { + return Py_BuildValue("y", glfwGetX11SelectionString()); + } else fprintf(stderr, "Failed to load glfwGetX11SelectionString\n"); + Py_RETURN_NONE; +} + +static PyObject* +set_primary_selection(PyObject UNUSED *self, PyObject *args) { + char *text; + if (!PyArg_ParseTuple(args, "s", &text)) return NULL; + if (glfwSetX11SelectionString) glfwSetX11SelectionString(text); + else fprintf(stderr, "Failed to load glfwSetX11SelectionString\n"); + Py_RETURN_NONE; +} + static PyObject* os_window_should_close(PyObject UNUSED *self, PyObject *args) { int q = -1001; @@ -584,6 +623,10 @@ static PyMethodDef module_methods[] = { METHODB(glfw_window_hint, METH_VARARGS), METHODB(os_window_should_close, METH_VARARGS), METHODB(os_window_swap_buffers, METH_VARARGS), + METHODB(get_primary_selection, METH_NOARGS), + METHODB(x11_display, METH_NOARGS), + METHODB(x11_window_id, METH_O), + METHODB(set_primary_selection, METH_VARARGS), {"glfw_init", (PyCFunction)glfw_init, METH_VARARGS, ""}, {"glfw_terminate", (PyCFunction)glfw_terminate, METH_NOARGS, ""}, {"glfw_wait_events", (PyCFunction)glfw_wait_events, METH_VARARGS, ""}, diff --git a/kitty/utils.py b/kitty/utils.py index 1d7a31f78..61efd5da3 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -17,13 +17,10 @@ from contextlib import contextmanager from functools import lru_cache from time import monotonic -from .constants import ( - appname, isosx, iswayland, selection_clipboard_funcs, x11_display, - x11_window_id -) +from .constants import appname, isosx, iswayland from .fast_data_types import ( GLSL_VERSION, glfw_get_physical_dpi, glfw_primary_monitor_content_scale, - redirect_std_streams, wcwidth as wcwidth_impl + redirect_std_streams, wcwidth as wcwidth_impl, x11_display, x11_window_id ) from .rgb import Color, to_color @@ -75,43 +72,6 @@ def sanitize_title(x): return re.sub(r'\s+', ' ', re.sub(r'[\0-\x19]', '', x)) -@lru_cache() -def load_libx11(): - import ctypes - from ctypes.util import find_library - libx11 = ctypes.CDLL(find_library('X11')) - - def cdef(name, restype, *argtypes): - f = getattr(libx11, name) - if restype is not None: - f.restype = restype - if argtypes: - f.argtypes = argtypes - return f - - return cdef('XResourceManagerString', ctypes.c_char_p, ctypes.c_void_p) - - -def parse_xrdb(raw): - q = 'Xft.dpi:\t' - for line in raw.decode('utf-8', 'replace').splitlines(): - if line.startswith(q): - return float(line[len(q):]) - - -def x11_dpi(): - if iswayland: - return - XResourceManagerString = load_libx11() - display = x11_display() - if display: - try: - raw = XResourceManagerString(display) - return parse_xrdb(raw) - except Exception: - pass - - def get_logical_dpi(override_dpi=None): # See https://github.com/glfw/glfw/issues/1019 for why we cant use # glfw_get_physical_dpi() @@ -122,14 +82,8 @@ def get_logical_dpi(override_dpi=None): # TODO: Investigate if this needs a different implementation on OS X get_logical_dpi.ans = glfw_get_physical_dpi() else: - try: - xscale, yscale = glfw_primary_monitor_content_scale() - except NotImplementedError: # glfw < 3.3 - xdpi = ydpi = x11_dpi() - if xdpi is None: - xdpi, ydpi = glfw_get_physical_dpi() - else: - xdpi, ydpi = xscale * 96.0, yscale * 96.0 + xscale, yscale = glfw_primary_monitor_content_scale() + xdpi, ydpi = xscale * 96.0, yscale * 96.0 get_logical_dpi.ans = xdpi, ydpi return get_logical_dpi.ans @@ -163,32 +117,19 @@ def parse_color_set(raw): def set_primary_selection(text): - if isosx: - return # There is no primary selection on OS X - if isinstance(text, str): - text = text.encode('utf-8') - s = selection_clipboard_funcs()[1] - if s is None: - p = subprocess.Popen(['xsel', '-i', '-p'], stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - p.stdin.write(text), p.stdin.close() - p.wait() - else: - s(text) + if isosx or iswayland: + return # There is no primary selection + if isinstance(text, bytes): + text = text.decode('utf-8') + from kitty.fast_data_types import set_primary_selection + set_primary_selection(text) def get_primary_selection(): - if isosx: - return '' # There is no primary selection on OS X - g = selection_clipboard_funcs()[0] - if g is None: - # We cannot use check_output as we set a SIGCHLD handler to reap zombies - ans = subprocess.Popen(['xsel', '-p'], stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE).stdout.read().decode('utf-8') - if ans: - # Without this for some reason repeated pastes dont work - set_primary_selection(ans) - else: - ans = (g() or b'').decode('utf-8', 'replace') - return ans + if isosx or iswayland: + return '' # There is no primary selection + from kitty.fast_data_types import get_primary_selection + return (get_primary_selection() or b'').decode('utf-8', 'replace') def base64_encode(