Start work on full panel support in X11

This commit is contained in:
Kovid Goyal 2025-05-01 15:44:08 +05:30
parent 04fbf63eab
commit f04fc251bf
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
16 changed files with 261 additions and 309 deletions

View file

@ -3252,6 +3252,7 @@ GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialize
return _glfwPlatformGetNativeKeyForKey(glfw_key);
}
GLFWAPI bool glfwIsLayerShellSupported(void) { return true; }
//////////////////////////////////////////////////////////////////////////
////// GLFW internal API //////

View file

@ -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))

1
glfw/glfw3.h vendored
View file

@ -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.
*

5
glfw/internal.h vendored
View file

@ -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);

9
glfw/wl_window.c vendored
View file

@ -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; }

10
glfw/x11_init.c vendored
View file

@ -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 =

101
glfw/x11_monitor.c vendored
View file

@ -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)

9
glfw/x11_platform.h vendored
View file

@ -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;

266
glfw/x11_window.c vendored
View file

@ -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);
}

View file

@ -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 @@
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)

View file

@ -458,7 +458,7 @@ def add_os_window(
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

View file

@ -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: ...

12
kitty/glfw-wrapper.c generated
View file

@ -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;
}

16
kitty/glfw-wrapper.h generated
View file

@ -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);

View file

@ -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),

View file

@ -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))