diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 6d322a932..720ec5dfa 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -3252,6 +3252,7 @@ glfwGetCocoaKeyEquivalent(uint32_t glfw_key, int glfw_mods, int *cocoa_mods) { return _glfwPlatformGetNativeKeyForKey(glfw_key); } +GLFWAPI bool glfwIsLayerShellSupported(void) { return true; } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// diff --git a/glfw/glfw.py b/glfw/glfw.py index ab00fcf01..e8564c128 100755 --- a/glfw/glfw.py +++ b/glfw/glfw.py @@ -323,7 +323,6 @@ def generate_wrappers(glfw_header: str) -> None: void glfwWaylandRunWithActivationToken(GLFWwindow *handle, GLFWactivationcallback cb, void *cb_data) bool glfwWaylandSetTitlebarColor(GLFWwindow *handle, uint32_t color, bool use_system_color) void glfwWaylandRedrawCSDWindowTitle(GLFWwindow *handle) - bool glfwWaylandIsLayerShellSupported(void) bool glfwWaylandIsWindowFullyCreated(GLFWwindow *handle) bool glfwWaylandBeep(GLFWwindow *handle) GLFWLayerShellConfig* glfwWaylandLayerShellConfig(GLFWwindow *handle) @@ -331,8 +330,6 @@ def generate_wrappers(glfw_header: str) -> None: unsigned long long glfwDBusUserNotify(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun callback, void *data) void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler) int glfwSetX11LaunchCommand(GLFWwindow *handle, char **argv, int argc) - void glfwSetX11WindowAsDock(int32_t x11_window_id) - void glfwSetX11WindowStrut(int32_t x11_window_id, uint32_t dimensions[12]) '''.splitlines(): if line: functions.append(Function(line.strip(), check_fail=False)) diff --git a/glfw/glfw3.h b/glfw/glfw3.h index e3ac36d4f..a13341668 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1977,6 +1977,7 @@ GLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun function); GLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselectionfun callback); GLFWAPI GLFWhascurrentselectionfun glfwSetHasCurrentSelectionCallback(GLFWhascurrentselectionfun callback); GLFWAPI GLFWimecursorpositionfun glfwSetIMECursorPositionCallback(GLFWimecursorpositionfun callback); +GLFWAPI bool glfwIsLayerShellSupported(void); /*! @brief Terminates the GLFW library. * diff --git a/glfw/internal.h b/glfw/internal.h index 267cc6eeb..543d81734 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -668,6 +668,10 @@ struct _GLFWlibrary // extern _GLFWlibrary _glfw; +typedef struct GeometryRect { int x, y, width, height; } GeometryRect; +typedef struct MonitorGeometry { + GeometryRect full, workarea; +} MonitorGeometry; ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -878,6 +882,7 @@ unsigned long long _glfwPlatformAddTimer(monotonic_t interval, bool repeats, GLF void _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled); void _glfwPlatformRemoveTimer(unsigned long long timer_id); int _glfwPlatformSetWindowBlur(_GLFWwindow* handle, int value); +MonitorGeometry _glfwPlatformGetMonitorGeometry(_GLFWmonitor* monitor); char* _glfw_strdup(const char* source); diff --git a/glfw/wl_window.c b/glfw/wl_window.c index e7101ca32..a0bf8e4cc 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -1054,8 +1054,10 @@ calculate_layer_size(_GLFWwindow *window, uint32_t *width, uint32_t *height) { const GLFWLayerShellConfig *config = &window->wl.layer_shell.config; GLFWvidmode m = {0}; if (window->wl.monitorsCount) _glfwPlatformGetVideoMode(window->wl.monitors[0], &m); + int monitor_width = m.width, monitor_height = m.height; const int y_margin = config->requested_bottom_margin + config->requested_top_margin, x_margin = config->requested_left_margin + config->requested_right_margin; - const int monitor_width = MAX(0, m.width - x_margin), monitor_height = MAX(0, m.height - y_margin); + monitor_width = monitor_width > x_margin ? monitor_width - x_margin : 0; + monitor_height = monitor_height > y_margin ? monitor_height - y_margin : 0; float xscale = (float)config->expected.xscale, yscale = (float)config->expected.yscale; if (window->wl.window_fully_created) _glfwPlatformGetWindowContentScale(window, &xscale, &yscale); unsigned cell_width, cell_height; double left_edge_spacing, top_edge_spacing, right_edge_spacing, bottom_edge_spacing; @@ -1089,6 +1091,7 @@ calculate_layer_size(_GLFWwindow *window, uint32_t *width, uint32_t *height) { *width = (uint32_t)(1. + spacing_x); *height = (uint32_t)(1. + spacing_y); } + } static void @@ -1101,8 +1104,6 @@ layer_surface_handle_configure(void* data, struct zwlr_layer_surface_v1* surface window->wl.once.surface_configured = true; update_fully_created_on_configure(window); } - GLFWvidmode m = {0}; - if (window->wl.monitorsCount) _glfwPlatformGetVideoMode(window->wl.monitors[0], &m); calculate_layer_size(window, &width, &height); zwlr_layer_surface_v1_ack_configure(surface, serial); if ((int)width != window->wl.width || (int)height != window->wl.height) { @@ -2895,7 +2896,7 @@ GLFWAPI GLFWLayerShellConfig* glfwWaylandLayerShellConfig(GLFWwindow *handle) { return &((_GLFWwindow*)handle)->wl.layer_shell.config; } -GLFWAPI bool glfwWaylandIsLayerShellSupported(void) { return _glfw.wl.zwlr_layer_shell_v1 != NULL; } +GLFWAPI bool glfwIsLayerShellSupported(void) { return _glfw.wl.zwlr_layer_shell_v1 != NULL; } GLFWAPI bool glfwWaylandIsWindowFullyCreated(GLFWwindow *handle) { return handle != NULL && ((_GLFWwindow*)handle)->wl.window_fully_created; } diff --git a/glfw/x11_init.c b/glfw/x11_init.c index 2e4ae7d2c..afe03bc02 100644 --- a/glfw/x11_init.c +++ b/glfw/x11_init.c @@ -124,6 +124,8 @@ static void detectEWMH(void) getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE"); _glfw.x11.NET_WM_STATE_ABOVE = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE"); + _glfw.x11.NET_WM_STATE_BELOW = + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_BELOW"); _glfw.x11.NET_WM_STATE_FULLSCREEN = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN"); _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT = @@ -132,6 +134,12 @@ static void detectEWMH(void) getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ"); _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_DEMANDS_ATTENTION"); + _glfw.x11.NET_WM_STATE_SKIP_TASKBAR = + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_SKIP_TASKBAR"); + _glfw.x11.NET_WM_STATE_SKIP_PAGER = + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_SKIP_PAGER"); + _glfw.x11.NET_WM_STATE_STICKY = + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_STICKY"); _glfw.x11.NET_WM_FULLSCREEN_MONITORS = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS"); _glfw.x11.NET_WM_WINDOW_TYPE = @@ -140,6 +148,8 @@ static void detectEWMH(void) getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL"); _glfw.x11.NET_WM_WINDOW_TYPE_DOCK = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_DOCK"); + _glfw.x11.NET_WM_WINDOW_TYPE_DESKTOP = + getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_DESKTOP"); _glfw.x11.NET_WORKAREA = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WORKAREA"); _glfw.x11.NET_CURRENT_DESKTOP = diff --git a/glfw/x11_monitor.c b/glfw/x11_monitor.c index 6c594fbe8..4c1a67d1e 100644 --- a/glfw/x11_monitor.c +++ b/glfw/x11_monitor.c @@ -350,96 +350,79 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor UNUSED, *yscale = _glfw.x11.contentScaleY; } -void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) -{ - int areaX = 0, areaY = 0, areaWidth = 0, areaHeight = 0; - - if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) - { +MonitorGeometry +_glfwPlatformGetMonitorGeometry(_GLFWmonitor *monitor) { + MonitorGeometry ans = {0}; + if (!monitor) return ans; + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) { XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); - areaX = ci->x; - areaY = ci->y; + ans.full.x = ci->x; + ans.full.y = ci->y; const XRRModeInfo* mi = getModeInfo(sr, ci->mode); - if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) - { - areaWidth = mi->height; - areaHeight = mi->width; - } - else - { - areaWidth = mi->width; - areaHeight = mi->height; + if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) { + ans.full.width = mi->height; + ans.full.height = mi->width; + } else { + ans.full.width = mi->width; + ans.full.height = mi->height; } XRRFreeCrtcInfo(ci); XRRFreeScreenResources(sr); + } else { + ans.full.width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); + ans.full.height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); } - else - { - areaWidth = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); - areaHeight = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); - } + ans.workarea = *(&ans.full); - if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP) - { + if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP) { Atom* extents = NULL; Atom* desktop = NULL; - const unsigned long extentCount = - _glfwGetWindowPropertyX11(_glfw.x11.root, - _glfw.x11.NET_WORKAREA, - XA_CARDINAL, - (unsigned char**) &extents); + const unsigned long extentCount = _glfwGetWindowPropertyX11( + _glfw.x11.root, _glfw.x11.NET_WORKAREA, XA_CARDINAL, (unsigned char**) &extents); - if (_glfwGetWindowPropertyX11(_glfw.x11.root, - _glfw.x11.NET_CURRENT_DESKTOP, - XA_CARDINAL, - (unsigned char**) &desktop) > 0) - { - if (extentCount >= 4 && *desktop < extentCount / 4) - { + if (_glfwGetWindowPropertyX11(_glfw.x11.root, _glfw.x11.NET_CURRENT_DESKTOP, XA_CARDINAL, (unsigned char**) &desktop) > 0) { + if (extentCount >= 4 && *desktop < extentCount / 4) { const int globalX = extents[*desktop * 4 + 0]; const int globalY = extents[*desktop * 4 + 1]; const int globalWidth = extents[*desktop * 4 + 2]; const int globalHeight = extents[*desktop * 4 + 3]; - if (areaX < globalX) - { - areaWidth -= globalX - areaX; - areaX = globalX; + if (ans.workarea.x < globalX) { + ans.workarea.width -= globalX - ans.workarea.x; + ans.workarea.x = globalX; } - - if (areaY < globalY) - { - areaHeight -= globalY - areaY; - areaY = globalY; + if (ans.workarea.y < globalY) { + ans.workarea.height -= globalY - ans.workarea.y; + ans.workarea.y = globalY; } - - if (areaX + areaWidth > globalX + globalWidth) - areaWidth = globalX - areaX + globalWidth; - if (areaY + areaHeight > globalY + globalHeight) - areaHeight = globalY - areaY + globalHeight; + if (ans.workarea.x + ans.workarea.width > globalX + globalWidth) + ans.workarea.width = globalX - ans.workarea.x + globalWidth; + if (ans.workarea.y + ans.workarea.height > globalY + globalHeight) + ans.workarea.height = globalY - ans.workarea.y + globalHeight; } } - - if (extents) - XFree(extents); - if (desktop) - XFree(desktop); + if (extents) XFree(extents); + if (desktop) XFree(desktop); } + return ans; +} +void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) { + MonitorGeometry ans = _glfwPlatformGetMonitorGeometry(monitor); if (xpos) - *xpos = areaX; + *xpos = ans.workarea.x; if (ypos) - *ypos = areaY; + *ypos = ans.workarea.y; if (width) - *width = areaWidth; + *width = ans.workarea.width; if (height) - *height = areaHeight; + *height = ans.workarea.height; } GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) diff --git a/glfw/x11_platform.h b/glfw/x11_platform.h index 24ca14fce..b62bf67f3 100644 --- a/glfw/x11_platform.h +++ b/glfw/x11_platform.h @@ -205,6 +205,10 @@ typedef struct _GLFWwindowX11 // The last position the cursor was warped to by GLFW int warpCursorPosX, warpCursorPosY; + struct { + bool is_active; + GLFWLayerShellConfig config; + } layer_shell; } _GLFWwindowX11; typedef struct MimeAtom { @@ -254,12 +258,17 @@ typedef struct _GLFWlibraryX11 Atom NET_WM_WINDOW_TYPE; Atom NET_WM_WINDOW_TYPE_NORMAL; Atom NET_WM_WINDOW_TYPE_DOCK; + Atom NET_WM_WINDOW_TYPE_DESKTOP; Atom NET_WM_STATE; Atom NET_WM_STATE_ABOVE; + Atom NET_WM_STATE_BELOW; Atom NET_WM_STATE_FULLSCREEN; Atom NET_WM_STATE_MAXIMIZED_VERT; Atom NET_WM_STATE_MAXIMIZED_HORZ; Atom NET_WM_STATE_DEMANDS_ATTENTION; + Atom NET_WM_STATE_SKIP_TASKBAR; + Atom NET_WM_STATE_SKIP_PAGER; + Atom NET_WM_STATE_STICKY; Atom NET_WM_BYPASS_COMPOSITOR; Atom NET_WM_FULLSCREEN_MONITORS; Atom NET_WM_WINDOW_OPACITY; diff --git a/glfw/x11_window.c b/glfw/x11_window.c index 50c9d0165..d0b63cd45 100644 --- a/glfw/x11_window.c +++ b/glfw/x11_window.c @@ -530,19 +530,172 @@ static void enableCursor(_GLFWwindow* window) updateCursorImage(window); } +typedef struct WindowGeometry { + int x, y, width, height; + bool needs_strut; + uint32_t struts[12]; +} WindowGeometry; + +static WindowGeometry +calculate_layer_geometry(_GLFWwindow *window) { + MonitorGeometry mg = _glfwPlatformGetMonitorGeometry((_GLFWmonitor*)glfwGetPrimaryMonitor()); + WindowGeometry ans = {0}; + ans.width = mg.full.width; ans.height = mg.full.height; + ans.x = mg.full.x; ans.y = mg.full.y; +#define config (window->x11.layer_shell.config) + ans.needs_strut = config.type == GLFW_LAYER_SHELL_PANEL; + if (config.type == GLFW_LAYER_SHELL_BACKGROUND) return ans; + float xscale = (float)config.expected.xscale, yscale = (float)config.expected.yscale; + _glfwPlatformGetWindowContentScale(window, &xscale, &yscale); + unsigned cell_width, cell_height; double left_edge_spacing, top_edge_spacing, right_edge_spacing, bottom_edge_spacing; + config.size_callback((GLFWwindow*)window, xscale, yscale, &cell_width, &cell_height, &left_edge_spacing, &top_edge_spacing, &right_edge_spacing, &bottom_edge_spacing); + double spacing_x = left_edge_spacing + right_edge_spacing; + double spacing_y = top_edge_spacing + bottom_edge_spacing; + double xsz = config.x_size_in_pixels ? (unsigned)(config.x_size_in_pixels * xscale) : (cell_width * config.x_size_in_cells); + double ysz = config.y_size_in_pixels ? (unsigned)(config.y_size_in_pixels * yscale) : (cell_height * config.y_size_in_cells); + xsz /= xscale; ysz /= yscale; + ans.width = (int)(1. + spacing_x + xsz); ans.height = (int)(1. + spacing_y + ysz); + GeometryRect m = config.type == GLFW_LAYER_SHELL_TOP || config.type == GLFW_LAYER_SHELL_OVERLAY ? mg.workarea : mg.full; + static const struct { + uint32_t left, right, top, bottom, left_start_y, left_end_y, right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x, bottom_end_x; + } s = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + + switch (config.edge) { + case GLFW_EDGE_LEFT: + ans.x = m.x + config.requested_left_margin; + ans.y = m.y + config.requested_top_margin; + ans.height = m.height - config.requested_bottom_margin - config.requested_top_margin; + ans.struts[s.left] = ans.width; ans.struts[s.left_end_y] = ans.height; + break; + case GLFW_EDGE_RIGHT: + ans.x = m.x + m.width - config.requested_right_margin - ans.width; + ans.y = m.y + config.requested_top_margin; + ans.height = m.height - config.requested_bottom_margin - config.requested_top_margin; + ans.struts[s.right] = ans.width; ans.struts[s.right_end_y] = ans.height; + break; + case GLFW_EDGE_TOP: + ans.x = m.x + config.requested_left_margin; + ans.y = m.y + config.requested_top_margin; + ans.width = m.width - config.requested_right_margin - config.requested_left_margin; + ans.struts[s.top] = ans.height; ans.struts[s.top_end_x] = ans.width; + break; + case GLFW_EDGE_BOTTOM: + ans.x = m.x + config.requested_left_margin; + ans.y = m.height - config.requested_bottom_margin - ans.height; + ans.width = m.width - config.requested_right_margin - config.requested_left_margin; + ans.struts[s.bottom] = ans.height; ans.struts[s.bottom_end_x] = ans.width; + break; + default: + ans.needs_strut = false; + ans.x = m.x + config.requested_left_margin; + ans.y = m.y + config.requested_top_margin; + ans.height = m.height - config.requested_bottom_margin - config.requested_top_margin; + ans.width = m.width - config.requested_right_margin - config.requested_left_margin; + break; + } +#undef config + return ans; +} + +GLFWAPI bool glfwIsLayerShellSupported(void) { return _glfw.x11.NET_WM_WINDOW_TYPE != 0 && _glfw.x11.NET_WM_STATE != 0; } + +static void +glfwSetX11WindowStrut(_GLFWwindow *window, const uint32_t dimensions[12]) { + XChangeProperty( + _glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_STRUT_PARTIAL, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) dimensions, 12); +} + +static bool +update_wm_hints(_GLFWwindow *window, const WindowGeometry *wg, const _GLFWwndconfig *wndconfig) { + XWMHints* hints = XAllocWMHints(); + bool is_layer_shell = window->x11.layer_shell.is_active; + bool ok = false; + if (hints) { + ok = true; + hints->flags = StateHint | InputHint; + hints->initial_state = NormalState; + hints->input = true; + if (is_layer_shell && window->x11.layer_shell.config.type == GLFW_LAYER_SHELL_BACKGROUND) hints->input = false; + XSetWMHints(_glfw.x11.display, window->x11.handle, hints); + XFree(hints); + } else _glfwInputError(GLFW_OUT_OF_MEMORY, "X11: Failed to allocate WM hints"); + if (_glfw.x11.NET_WM_WINDOW_TYPE) { + Atom type = 0; + if (window->x11.layer_shell.is_active) { + const char *name = NULL; +#define S(which) type = _glfw.x11.NET_WM_WINDOW_TYPE_DESKTOP; name = #which + switch (window->x11.layer_shell.config.type) { + case GLFW_LAYER_SHELL_BACKGROUND: S(NET_WM_WINDOW_TYPE_DESKTOP); break; + case GLFW_LAYER_SHELL_PANEL: S(NET_WM_WINDOW_TYPE_DOCK); break; + default: S(NET_WM_WINDOW_TYPE_NORMAL); break; + } +#undef S + if (!type) { + _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Window manager does not support _%s", name); + ok = false; + } + } else if (_glfw.x11.NET_WM_WINDOW_TYPE_NORMAL) type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL; + if (type) XChangeProperty( + _glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char*) &type, 1); + } else if (is_layer_shell) { + _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Window manager does not support _NET_WM_WINDOW_TYPE"); + ok = false; + } + if (is_layer_shell) { + if (_glfw.x11.NET_WM_STRUT_PARTIAL) { + glfwSetX11WindowStrut(window, wg->needs_strut ? wg->struts : (uint32_t[12]){0}); + } else if (wg->needs_strut) { + _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Window manager does not support _NET_WM_STRUT_PARTIAL"); + ok = false; + } + } + if (ok) { + updateNormalHints(window, wg->width, wg->height); + Atom states[8]; unsigned count = 0; + if (is_layer_shell) { + _glfwPlatformSetWindowDecorated(window, false); + if (_glfw.x11.NET_WM_STATE_STICKY) states[count++] = _glfw.x11.NET_WM_STATE_STICKY; + if (_glfw.x11.NET_WM_STATE_SKIP_PAGER) states[count++] = _glfw.x11.NET_WM_STATE_SKIP_PAGER; + if (_glfw.x11.NET_WM_STATE_SKIP_TASKBAR) states[count++] = _glfw.x11.NET_WM_STATE_SKIP_TASKBAR; +#define S(x) if (_glfw.x11.x) { states[count++] = _glfw.x11.x; } else { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Window manager does not support _%s", #x); ok = false; } + switch (window->x11.layer_shell.config.type) { + case GLFW_LAYER_SHELL_NONE: break; + case GLFW_LAYER_SHELL_BACKGROUND: case GLFW_LAYER_SHELL_PANEL: S(NET_WM_STATE_BELOW); break; + case GLFW_LAYER_SHELL_TOP: case GLFW_LAYER_SHELL_OVERLAY: S(NET_WM_STATE_ABOVE); break; + } +#undef S + } else if (wndconfig) { + if (!wndconfig->decorated) _glfwPlatformSetWindowDecorated(window, false); + if (_glfw.x11.NET_WM_STATE && !window->monitor) { + if (wndconfig->floating) { + if (_glfw.x11.NET_WM_STATE_ABOVE) states[count++] = _glfw.x11.NET_WM_STATE_ABOVE; + } + if (wndconfig->maximized) { + if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) { + states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT; + states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ; + window->x11.maximized = true; + } + } + } + } + if (count && _glfw.x11.NET_WM_STATE) XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_STATE, + XA_ATOM, 32, PropModeReplace, (unsigned char*) states, count); + } + if (!wndconfig && ok) _glfwPlatformSetWindowPos(window, wg->x, wg->y); + return ok; +} + // Create the X11 window (and its colormap) // static bool createNativeWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, Visual* visual, int depth) { - int width = wndconfig->width; - int height = wndconfig->height; - - if (wndconfig->scaleToMonitor) - { - width *= (int)_glfw.x11.contentScaleX; - height *= (int)_glfw.x11.contentScaleY; + WindowGeometry wg = {.width=wndconfig->width, .height=wndconfig->height}; + if (window->x11.layer_shell.is_active) { + wg = calculate_layer_geometry(window); + window->resizable = false; } // Create a colormap based on the visual used by the current context @@ -565,8 +718,8 @@ static bool createNativeWindow(_GLFWwindow* window, window->x11.parent = _glfw.x11.root; window->x11.handle = XCreateWindow(_glfw.x11.display, _glfw.x11.root, - 0, 0, // Position - width, height, + wg.x, wg.y, // Position + wg.width, wg.height, 0, // Border width depth, // Color depth InputOutput, @@ -588,39 +741,6 @@ static bool createNativeWindow(_GLFWwindow* window, _glfw.x11.context, (XPointer) window); - if (!wndconfig->decorated) - _glfwPlatformSetWindowDecorated(window, false); - - if (_glfw.x11.NET_WM_STATE && !window->monitor) - { - Atom states[3]; - int count = 0; - - if (wndconfig->floating) - { - if (_glfw.x11.NET_WM_STATE_ABOVE) - states[count++] = _glfw.x11.NET_WM_STATE_ABOVE; - } - - if (wndconfig->maximized) - { - if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && - _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) - { - states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT; - states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ; - window->x11.maximized = true; - } - } - - if (count) - { - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_STATE, XA_ATOM, 32, - PropModeReplace, (unsigned char*) states, count); - } - } - // Declare the WM protocols supported by GLFW { Atom protocols[] = @@ -643,32 +763,7 @@ static bool createNativeWindow(_GLFWwindow* window, (unsigned char*) &pid, 1); } - if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL) - { - Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL; - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32, - PropModeReplace, (unsigned char*) &type, 1); - } - - // Set ICCCM WM_HINTS property - { - XWMHints* hints = XAllocWMHints(); - if (!hints) - { - _glfwInputError(GLFW_OUT_OF_MEMORY, - "X11: Failed to allocate WM hints"); - return false; - } - - hints->flags = StateHint; - hints->initial_state = NormalState; - - XSetWMHints(_glfw.x11.display, window->x11.handle, hints); - XFree(hints); - } - - updateNormalHints(window, width, height); + if (!update_wm_hints(window, &wg, wndconfig)) return false; // Set ICCCM WM_CLASS property { @@ -1869,9 +1964,12 @@ void _glfwPushSelectionToManagerX11(void) int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, const GLFWLayerShellConfig *lsc) { - (void)lsc; Visual* visual = NULL; int depth; + if (lsc) { + window->x11.layer_shell.is_active = true; + window->x11.layer_shell.config = *lsc; + } else window->x11.layer_shell.is_active = false; if (ctxconfig->client != GLFW_NO_API) { @@ -1964,7 +2062,9 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) } bool _glfwPlatformSetLayerShellConfig(_GLFWwindow* window, const GLFWLayerShellConfig *value) { - (void)window; (void)value; + if (value) window->x11.layer_shell.config = *value; + WindowGeometry wg = calculate_layer_geometry(window); + update_wm_hints(window, &wg, NULL); return false; } @@ -3273,30 +3373,4 @@ GLFWAPI int glfwSetX11LaunchCommand(GLFWwindow *handle, char **argv, int argc) return XSetCommand(_glfw.x11.display, window->x11.handle, argv, argc); } -GLFWAPI void glfwSetX11WindowAsDock(int32_t x11_window_id) { - _GLFW_REQUIRE_INIT(); - Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_DOCK; - if (!type) { - _glfwInputError(GLFW_PLATFORM_ERROR, "The X11 window manager does not support the NET_WM_WINDOW_TYPE_DOCK Atom, cannot make panels into docks\n"); - return ; - } - if (!_glfw.x11.NET_WM_WINDOW_TYPE) { - _glfwInputError(GLFW_PLATFORM_ERROR, "The X11 window manager does not support the NET_WM_WINDOW_TYPE Atom, cannot make panels into docks\n"); - return ; - } - XChangeProperty(_glfw.x11.display, x11_window_id, - _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32, - PropModeReplace, (unsigned char*) &type, 1); -} - -GLFWAPI void glfwSetX11WindowStrut(int32_t x11_window_id, uint32_t dimensions[12]) { - _GLFW_REQUIRE_INIT(); - if (!_glfw.x11.NET_WM_STRUT_PARTIAL) { - _glfwInputError(GLFW_PLATFORM_ERROR, "The X11 window manager does not support the NET_WM_STRUT_PARTIAL Atom, cannot make panels into docks\n"); - return ; - } - XChangeProperty(_glfw.x11.display, x11_window_id, - _glfw.x11.NET_WM_STRUT_PARTIAL, XA_CARDINAL, 32, - PropModeReplace, (unsigned char*) dimensions, 12); -} diff --git a/kittens/panel/main.py b/kittens/panel/main.py index d659996cd..c491ef967 100644 --- a/kittens/panel/main.py +++ b/kittens/panel/main.py @@ -3,14 +3,13 @@ import os import sys -from collections.abc import Callable from contextlib import suppress from functools import partial -from typing import Any, Iterable, Mapping, Sequence +from typing import Iterable, Mapping, Sequence from kitty.cli import parse_args from kitty.cli_stub import PanelCLIOptions -from kitty.constants import is_macos, is_wayland, kitten_exe +from kitty.constants import is_macos, kitten_exe from kitty.fast_data_types import ( GLFW_EDGE_BOTTOM, GLFW_EDGE_CENTER, @@ -25,15 +24,12 @@ from kitty.fast_data_types import ( GLFW_LAYER_SHELL_OVERLAY, GLFW_LAYER_SHELL_PANEL, GLFW_LAYER_SHELL_TOP, - glfw_primary_monitor_size, - make_x11_window_a_dock_window, set_layer_shell_config, toggle_os_window_visibility, ) -from kitty.os_window_size import WindowSizeData, edge_spacing from kitty.simple_cli_definitions import build_panel_cli_spec from kitty.types import LayerShellConfig -from kitty.typing_compat import BossType, EdgeLiteral +from kitty.typing_compat import BossType from kitty.utils import log_error args = PanelCLIOptions() @@ -51,84 +47,6 @@ def parse_panel_args(args: list[str]) -> tuple[PanelCLIOptions, list[str]]: return parse_args(args, panel_kitten_options_spec, usage, help_text, 'kitty +kitten panel', result_class=PanelCLIOptions) -Strut = tuple[int, int, int, int, int, int, int, int, int, int, int, int] - - -def create_strut( - win_id: int, - left: int = 0, right: int = 0, top: int = 0, bottom: int = 0, left_start_y: int = 0, left_end_y: int = 0, - right_start_y: int = 0, right_end_y: int = 0, top_start_x: int = 0, top_end_x: int = 0, - bottom_start_x: int = 0, bottom_end_x: int = 0 -) -> Strut: - return left, right, top, bottom, left_start_y, left_end_y, right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x, bottom_end_x - - -def create_top_strut(win_id: int, width: int, height: int) -> Strut: - return create_strut(win_id, top=height, top_end_x=width) - - -def create_bottom_strut(win_id: int, width: int, height: int) -> Strut: - return create_strut(win_id, bottom=height, bottom_end_x=width) - - -def create_left_strut(win_id: int, width: int, height: int) -> Strut: - return create_strut(win_id, left=width, left_end_y=height) - - -def create_right_strut(win_id: int, width: int, height: int) -> Strut: - return create_strut(win_id, right=width, right_end_y=height) - - -window_width = window_height = 0 - - -def setup_x11_window(win_id: int) -> None: - if is_wayland(): - return - try: - func = globals()[f'create_{args.edge}_strut'] - except KeyError: - raise SystemExit(f'The value {args.edge} is not supported for --edge on X11') - strut = func(win_id, window_width, window_height) - make_x11_window_a_dock_window(win_id, strut) - - -def initial_window_size_func(opts: WindowSizeData, cached_values: dict[str, Any]) -> Callable[[int, int, float, float, float, float], tuple[int, int]]: - - def es(which: EdgeLiteral) -> float: - return edge_spacing(which, opts) - - def initial_window_size(cell_width: int, cell_height: int, dpi_x: float, dpi_y: float, xscale: float, yscale: float) -> tuple[int, int]: - if not is_macos and not is_wayland(): - # Not sure what the deal with scaling on X11 is - xscale = yscale = 1 - global window_width, window_height - monitor_width, monitor_height = glfw_primary_monitor_size() - x = dual_distance(args.columns, min_cell_value_if_no_pixels=1) - rwidth = x[1] if x[1] else (x[0] * cell_width / xscale) - x = dual_distance(args.lines, min_cell_value_if_no_pixels=1) - rheight = x[1] if x[1] else (x[0] * cell_width / yscale) - - if args.edge in {'left', 'right'}: - spacing = es('left') + es('right') - window_width = int(rwidth + (dpi_x / 72) * spacing + 1) - window_height = monitor_height - elif args.edge in {'top', 'bottom'}: - spacing = es('top') + es('bottom') - window_height = int(rheight + (dpi_y / 72) * spacing + 1) - window_width = monitor_width - elif args.edge in {'background', 'center'}: - window_width, window_height = monitor_width, monitor_height - else: - x_spacing = es('left') + es('right') - window_width = int(rwidth + (dpi_x / 72) * x_spacing + 1) - y_spacing = es('top') + es('bottom') - window_height = int(rheight + (dpi_y / 72) * y_spacing + 1) - return window_width, window_height - - return initial_window_size - - def dual_distance(spec: str, min_cell_value_if_no_pixels: int = 0) -> tuple[int, int]: with suppress(Exception): return int(spec), 0 @@ -259,9 +177,6 @@ def actual_main(sys_args: list[str]) -> None: from kitty.main import run_app run_app.cached_values_name = 'panel' run_app.layer_shell_config = layer_shell_config(args) - if not is_macos: - run_app.first_window_callback = setup_x11_window - run_app.initial_window_size_func = initial_window_size_func real_main(called_from_panel=True) diff --git a/kitty/boss.py b/kitty/boss.py index a4c2bf4fe..0d4d37869 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -458,7 +458,7 @@ class Boss: return os_window_id def add_os_panel(self, cfg: LayerShellConfig, wclass: str | None = appname, wname: str | None = appname) -> int: - if is_macos or not is_wayland() or not is_layer_shell_supported(): + if not is_layer_shell_supported(): raise RuntimeError('Creating desktop panels is not supported on this platform') wclass = wclass or appname wname = wname or appname diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 9f2505689..6651c1bf8 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -1703,7 +1703,6 @@ def get_docs_ref_map() -> bytes: ... 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: ... def run_with_activation_token(func: Callable[[str], None]) -> bool: ... -def make_x11_window_a_dock_window(x11_window_id: int, strut: Tuple[int, int, int, int, int, int, int, int, int, int, int, int]) -> None: ... def toggle_os_window_visibility(os_window_id: int, visible: Literal[True, False] = ...) -> bool: ... def parse_cli_from_spec(args: list[str], names_map: dict[str, OptionDict], defval_map: dict[str, Any]) -> tuple[dict[str, tuple[Any, bool]], list[str]]: ... def layer_shell_config_for_os_window(os_window_id: int) -> dict[str, Any] | None: ... diff --git a/kitty/glfw-wrapper.c b/kitty/glfw-wrapper.c index a99621d11..84e0a2696 100644 --- a/kitty/glfw-wrapper.c +++ b/kitty/glfw-wrapper.c @@ -47,6 +47,9 @@ load_glfw(const char* path) { *(void **) (&glfwSetIMECursorPositionCallback_impl) = dlsym(handle, "glfwSetIMECursorPositionCallback"); if (glfwSetIMECursorPositionCallback_impl == NULL) fail("Failed to load glfw function glfwSetIMECursorPositionCallback with error: %s", dlerror()); + *(void **) (&glfwIsLayerShellSupported_impl) = dlsym(handle, "glfwIsLayerShellSupported"); + if (glfwIsLayerShellSupported_impl == NULL) fail("Failed to load glfw function glfwIsLayerShellSupported 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()); @@ -488,9 +491,6 @@ load_glfw(const char* path) { *(void **) (&glfwWaylandRedrawCSDWindowTitle_impl) = dlsym(handle, "glfwWaylandRedrawCSDWindowTitle"); if (glfwWaylandRedrawCSDWindowTitle_impl == NULL) dlerror(); // clear error indicator - *(void **) (&glfwWaylandIsLayerShellSupported_impl) = dlsym(handle, "glfwWaylandIsLayerShellSupported"); - if (glfwWaylandIsLayerShellSupported_impl == NULL) dlerror(); // clear error indicator - *(void **) (&glfwWaylandIsWindowFullyCreated_impl) = dlsym(handle, "glfwWaylandIsWindowFullyCreated"); if (glfwWaylandIsWindowFullyCreated_impl == NULL) dlerror(); // clear error indicator @@ -512,12 +512,6 @@ load_glfw(const char* path) { *(void **) (&glfwSetX11LaunchCommand_impl) = dlsym(handle, "glfwSetX11LaunchCommand"); if (glfwSetX11LaunchCommand_impl == NULL) dlerror(); // clear error indicator - *(void **) (&glfwSetX11WindowAsDock_impl) = dlsym(handle, "glfwSetX11WindowAsDock"); - if (glfwSetX11WindowAsDock_impl == NULL) dlerror(); // clear error indicator - - *(void **) (&glfwSetX11WindowStrut_impl) = dlsym(handle, "glfwSetX11WindowStrut"); - if (glfwSetX11WindowStrut_impl == NULL) dlerror(); // clear error indicator - return NULL; } diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index fc87512b5..a80096d47 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1756,6 +1756,10 @@ typedef GLFWimecursorpositionfun (*glfwSetIMECursorPositionCallback_func)(GLFWim GFW_EXTERN glfwSetIMECursorPositionCallback_func glfwSetIMECursorPositionCallback_impl; #define glfwSetIMECursorPositionCallback glfwSetIMECursorPositionCallback_impl +typedef bool (*glfwIsLayerShellSupported_func)(void); +GFW_EXTERN glfwIsLayerShellSupported_func glfwIsLayerShellSupported_impl; +#define glfwIsLayerShellSupported glfwIsLayerShellSupported_impl + typedef void (*glfwTerminate_func)(void); GFW_EXTERN glfwTerminate_func glfwTerminate_impl; #define glfwTerminate glfwTerminate_impl @@ -2344,10 +2348,6 @@ typedef void (*glfwWaylandRedrawCSDWindowTitle_func)(GLFWwindow*); GFW_EXTERN glfwWaylandRedrawCSDWindowTitle_func glfwWaylandRedrawCSDWindowTitle_impl; #define glfwWaylandRedrawCSDWindowTitle glfwWaylandRedrawCSDWindowTitle_impl -typedef bool (*glfwWaylandIsLayerShellSupported_func)(void); -GFW_EXTERN glfwWaylandIsLayerShellSupported_func glfwWaylandIsLayerShellSupported_impl; -#define glfwWaylandIsLayerShellSupported glfwWaylandIsLayerShellSupported_impl - typedef bool (*glfwWaylandIsWindowFullyCreated_func)(GLFWwindow*); GFW_EXTERN glfwWaylandIsWindowFullyCreated_func glfwWaylandIsWindowFullyCreated_impl; #define glfwWaylandIsWindowFullyCreated glfwWaylandIsWindowFullyCreated_impl @@ -2376,12 +2376,4 @@ typedef int (*glfwSetX11LaunchCommand_func)(GLFWwindow*, char**, int); GFW_EXTERN glfwSetX11LaunchCommand_func glfwSetX11LaunchCommand_impl; #define glfwSetX11LaunchCommand glfwSetX11LaunchCommand_impl -typedef void (*glfwSetX11WindowAsDock_func)(int32_t); -GFW_EXTERN glfwSetX11WindowAsDock_func glfwSetX11WindowAsDock_impl; -#define glfwSetX11WindowAsDock glfwSetX11WindowAsDock_impl - -typedef void (*glfwSetX11WindowStrut_func)(int32_t, uint32_t[12]); -GFW_EXTERN glfwSetX11WindowStrut_func glfwSetX11WindowStrut_impl; -#define glfwSetX11WindowStrut glfwSetX11WindowStrut_impl - const char* load_glfw(const char* path); diff --git a/kitty/glfw.c b/kitty/glfw.c index 761def363..7d5d2e455 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -1264,17 +1264,11 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) { window_state = (int) PyLong_AsLong(optional_window_state); } if (layer_shell_config && layer_shell_config != Py_None ) { -#ifdef __APPLE__ - lsc = &lsc_stack; -#else - if (global_state.is_wayland) { - if (!glfwWaylandIsLayerShellSupported()) { - PyErr_SetString(PyExc_RuntimeError, "The Wayland compositor does not support the layer shell protocol."); - return NULL; - } - lsc = &lsc_stack; + if (!glfwIsLayerShellSupported()) { + PyErr_SetString(PyExc_RuntimeError, "The window manager/compositor does not support the primitives needed to make panels."); + return NULL; } -#endif + lsc = &lsc_stack; } else { if (optional_x && optional_x != Py_None) { if (!PyLong_Check(optional_x)) { PyErr_SetString(PyExc_TypeError, "x must be an int"); return NULL;} x = (int)PyLong_AsLong(optional_x); } if (optional_y && optional_y != Py_None) { if (!PyLong_Check(optional_y)) { PyErr_SetString(PyExc_TypeError, "y must be an int"); return NULL;} y = (int)PyLong_AsLong(optional_y); } @@ -2458,29 +2452,9 @@ get_clipboard_mime(PyObject *self UNUSED, PyObject *args) { Py_RETURN_NONE; } -static PyObject* -make_x11_window_a_dock_window(PyObject *self UNUSED, PyObject *args UNUSED) { - int x11_window_id; - PyObject *dims; - if (!PyArg_ParseTuple(args, "iO!", &x11_window_id, &PyTuple_Type, &dims)) return NULL; - if (PyTuple_GET_SIZE(dims) != 12 ) { PyErr_SetString(PyExc_TypeError, "dimensions must be a tuple of length 12"); return NULL; } - if (!glfwSetX11WindowAsDock) { PyErr_SetString(PyExc_RuntimeError, "Failed to load glfwGetX11Window"); return NULL; } - uint32_t dimensions[12]; - for (Py_ssize_t i = 0; i < 12; i++) dimensions[i] = PyLong_AsUnsignedLong(PyTuple_GET_ITEM(dims, i)); - if (PyErr_Occurred()) return NULL; - glfwSetX11WindowAsDock(x11_window_id); - glfwSetX11WindowStrut(x11_window_id, dimensions); - Py_RETURN_NONE; -} - static PyObject* is_layer_shell_supported(PyObject *self UNUSED, PyObject *args UNUSED) { -#ifdef __APPLE__ - Py_RETURN_FALSE; -#else - if (!global_state.is_wayland) Py_RETURN_FALSE; - return Py_NewRef(glfwWaylandIsLayerShellSupported() ? Py_True : Py_False); -#endif + return Py_NewRef(glfwIsLayerShellSupported() ? Py_True : Py_False); } static PyObject* @@ -2552,7 +2526,6 @@ static PyMethodDef module_methods[] = { METHODB(get_click_interval, METH_NOARGS), METHODB(is_layer_shell_supported, METH_NOARGS), METHODB(x11_window_id, METH_O), - METHODB(make_x11_window_a_dock_window, METH_VARARGS), METHODB(strip_csi, METH_O), #ifndef __APPLE__ METHODB(dbus_close_notification, METH_VARARGS), diff --git a/kitty/main.py b/kitty/main.py index d0d3e2d5e..01ff71a7b 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -230,10 +230,8 @@ def _run_app(opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine] = (), else: global_shortcuts = {} set_window_icon() - if _is_panel_kitten and is_wayland() and not is_layer_shell_supported(): - from .debug_config import compositor_name - raise SystemExit( - f'Cannot create panels as the layer shell protocol is not supported by the compositor: {compositor_name()}') + if _is_panel_kitten and not is_layer_shell_supported(): + raise SystemExit('Cannot create panels as the window manager/compositor does not support the neccessary protocols') with cached_values_for(run_app.cached_values_name) as cached_values: startup_sessions = tuple(create_sessions(opts, args, default_session=opts.startup_session))