Implement rendering of window control buttons in CSD

They still need to be wired up
This commit is contained in:
Kovid Goyal 2024-04-06 08:23:07 +05:30
parent 416d52bdac
commit 4f6faddbab
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
9 changed files with 93 additions and 20 deletions

2
glfw/glfw3.h vendored
View file

@ -1785,7 +1785,7 @@ typedef void (* GLFWjoystickfun)(int,int);
typedef void (* GLFWuserdatafun)(unsigned long long, void*);
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 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, bool is_single_glyph);
typedef char* (* GLFWcurrentselectionfun)(void);
typedef bool (* GLFWhascurrentselectionfun)(void);
typedef void (* GLFWclipboarddatafreefun)(void* data);

View file

@ -164,9 +164,10 @@ create_shadow_tile(_GLFWwindow *window) {
static void
render_title_bar(_GLFWwindow *window, bool to_front_buffer) {
const bool is_focused = window->id == _glfw.focusedWindowId;
uint32_t light_fg = is_focused ? 0xff444444 : 0xff888888, light_bg = is_focused ? 0xffdddad6 : 0xffeeeeee;
uint32_t dark_fg = is_focused ? 0xffffffff : 0xffcccccc, dark_bg = is_focused ? 0xff303030 : 0xff242424;
uint32_t bg_color = light_bg, fg_color = light_fg;
const uint32_t light_fg = is_focused ? 0xff444444 : 0xff888888, light_bg = is_focused ? 0xffdddad6 : 0xffeeeeee;
const uint32_t dark_fg = is_focused ? 0xffffffff : 0xffcccccc, dark_bg = is_focused ? 0xff303030 : 0xff242424;
static const uint32_t hover_dark_bg = 0xff444444, hover_light_bg = 0xffcccccc;
uint32_t bg_color = light_bg, fg_color = light_fg, hover_bg = hover_light_bg;
GLFWColorScheme appearance = glfwGetCurrentSystemColorTheme();
if (decs.use_custom_titlebar_color || appearance == GLFW_COLOR_SCHEME_NO_PREFERENCE) {
bg_color = 0xff000000 | (decs.titlebar_color & 0xffffff);
@ -174,7 +175,7 @@ render_title_bar(_GLFWwindow *window, bool to_front_buffer) {
double green = ((bg_color >> 8) & 0xFF) / 255.0;
double blue = (bg_color & 0xFF) / 255.0;
double luma = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
if (luma < 0.5) fg_color = dark_fg;
if (luma < 0.5) { fg_color = dark_fg; hover_bg = hover_dark_bg; }
if (!decs.use_custom_titlebar_color) bg_color = luma < 0.5 ? dark_bg : light_bg;
} else if (appearance == GLFW_COLOR_SCHEME_DARK) { bg_color = dark_bg; fg_color = dark_fg; }
uint8_t *output = to_front_buffer ? decs.top.buffer.data.front : decs.top.buffer.data.back;
@ -198,12 +199,30 @@ render_title_bar(_GLFWwindow *window, bool to_front_buffer) {
}
// render text part
int button_size = (int)roundf(decs.metrics.visible_titlebar_height * decs.for_window_state.fscale);
int num_buttons = 1;
if (window->wl.wm_capabilities.maximize) num_buttons++;
if (window->wl.wm_capabilities.minimize) num_buttons++;
output += decs.top.buffer.stride * margin;
if (window->wl.title && window->wl.title[0] && _glfw.callbacks.draw_text) {
if (_glfw.callbacks.draw_text((GLFWwindow*)window, window->wl.title, fg_color, bg_color, output, decs.top.buffer.width, decs.top.buffer.height - margin, 0, 0, 0)) return;
if (_glfw.callbacks.draw_text((GLFWwindow*)window, window->wl.title, fg_color, bg_color, output, decs.top.buffer.width, decs.top.buffer.height - margin, 0, 0, num_buttons * button_size, false)) goto render_buttons;
}
// rendering of text failed, blank the buffer
for (uint32_t *px = (uint32_t*)output, *end = (uint32_t*)(output + decs.top.buffer.size_in_bytes); px < end; px++) *px = bg_color;
render_buttons:
decs.maximize.width = 0; decs.minimize.width = 0; decs.close.width = 0;
if (!button_size) return;
int left = decs.top.buffer.width - num_buttons * button_size;
#define draw(which, text, hover_bg) { \
_glfw.callbacks.draw_text((GLFWwindow*)window, text, fg_color, decs.which.hovered ? hover_bg : bg_color, output, decs.top.buffer.width, \
decs.top.buffer.height - margin, /*x=*/left, /*y=*/0, /*right_margin=*/decs.top.buffer.width - left - button_size, true); \
decs.which.left = left; decs.which.width = button_size; left += button_size; \
}
if (window->wl.wm_capabilities.minimize) draw(minimize, "🗕", hover_bg);
if (window->wl.wm_capabilities.maximize) draw(maximize, "🗖", hover_bg);
draw(close, "🗙", 0xffff0000);
#undef draw
}
static void

6
glfw/wl_init.c vendored
View file

@ -675,8 +675,10 @@ static void registryHandleGlobal(void* data UNUSED,
}
else if (is(xdg_wm_base))
{
_glfw.wl.wmBase =
wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION
version = min(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION, version);
#endif
_glfw.wl.wmBase = wl_registry_bind(registry, name, &xdg_wm_base_interface, version);
xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, NULL);
}
else if (is(zxdg_decoration_manager_v1))

10
glfw/wl_platform.h vendored
View file

@ -166,6 +166,7 @@ typedef struct _GLFWwindowWayland
struct xdg_surface* surface;
struct xdg_toplevel* toplevel;
struct zxdg_toplevel_decoration_v1* decoration;
struct { int width, height; } top_level_bounds;
} xdg;
struct wp_fractional_scale_v1 *wp_fractional_scale_v1;
struct wp_viewport *wp_viewport;
@ -224,6 +225,10 @@ typedef struct _GLFWwindowWayland
int32_t x, y, width, height;
} geometry;
struct {
bool hovered;
int width, left;
} minimize, maximize, close;
struct {
uint32_t *data;
size_t for_decoration_size, stride, segments, corner_size;
@ -244,6 +249,11 @@ typedef struct _GLFWwindowWayland
int32_t width, height;
} user_requested_content_size;
struct {
bool minimize, maximize, fullscreen, window_menu;
} wm_capabilities;
bool maximize_on_first_show;
// counters for ignoring axis events following axis_discrete events in the
// same frame along the same axis

37
glfw/wl_window.c vendored
View file

@ -696,9 +696,42 @@ static void xdgToplevelHandleClose(void* data,
_glfwInputWindowCloseRequest(window);
}
#if defined(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)
static void
xdg_toplevel_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel UNUSED, struct wl_array *caps) {
_GLFWwindow *window = data;
#define c (window->wl.wm_capabilities)
memset(&c, 0, sizeof(c));
enum xdg_toplevel_wm_capabilities *cap;
wl_array_for_each(cap, caps) {
switch (*cap) {
case XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE: c.maximize = true; break;
case XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE: c.minimize = true; break;
case XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU: c.window_menu = true; break;
case XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN: c.fullscreen = true; break;
}
}
debug("Compositor top-level capabilities: maximize=%d minimize=%d window_menu=%d fullscreen=%d\n",
c.maximize, c.minimize, c.window_menu, c.fullscreen);
#undef c
}
#endif
static void
xdg_toplevel_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel UNUSED, int32_t width, int32_t height) {
_GLFWwindow *window = data;
window->wl.xdg.top_level_bounds.width = width;
window->wl.xdg.top_level_bounds.height = height;
}
static const struct xdg_toplevel_listener xdgToplevelListener = {
xdgToplevelHandleConfigure,
xdgToplevelHandleClose
.configure = xdgToplevelHandleConfigure,
.close = xdgToplevelHandleClose,
#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION
.configure_bounds = xdg_toplevel_configure_bounds,
.wm_capabilities = xdg_toplevel_wm_capabilities,
#endif
};
static void

View file

@ -148,11 +148,12 @@ typedef struct RenderState {
pixel *output;
size_t output_width, output_height, stride;
Face *current_face;
float x, y;
float x, y, start_pos_for_current_run;
int y_offset;
Region src, dest;
unsigned sz_px;
bool truncated;
bool horizontally_center;
} RenderState;
static void
@ -162,6 +163,10 @@ setup_regions(ProcessedBitmap *bm, RenderState *rs, int baseline) {
int xoff = (int)(rs->x + bm->bitmap_left);
if (xoff < 0) rs->src.left += -xoff;
else rs->dest.left = xoff;
if (rs->horizontally_center) {
int run_width = (int)(rs->output_width - rs->start_pos_for_current_run);
rs->dest.left = (int)rs->start_pos_for_current_run + (run_width > (int)bm->width ? (run_width - bm->width)/2 : 0);
}
int yoff = (int)(rs->y + bm->bitmap_top);
if ((yoff > 0 && yoff > baseline)) {
rs->dest.top = 0;
@ -325,6 +330,7 @@ render_run(RenderCtx *ctx, RenderState *rs) {
rs->truncated = true;
}
rs->start_pos_for_current_run = rs->x;
for (unsigned int i = 0; i < limit; i++) {
rs->x += (float)positions[i].x_offset / 64.0f;
rs->y += (float)positions[i].y_offset / 64.0f;
@ -427,7 +433,7 @@ process_codepoint(RenderCtx *ctx, RenderState *rs, char_type codep, char_type ne
}
bool
render_single_line(FreeTypeRenderCtx ctx_, const char *text, unsigned sz_px, pixel fg, pixel bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin) {
render_single_line(FreeTypeRenderCtx ctx_, const char *text, unsigned sz_px, pixel fg, pixel bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin, bool horizontally_center_runs) {
RenderCtx *ctx = (RenderCtx*)ctx_;
if (!ctx->created) return false;
size_t output_width = right_margin <= width ? width - right_margin : 0;
@ -449,7 +455,7 @@ render_single_line(FreeTypeRenderCtx ctx_, const char *text, unsigned sz_px, pix
set_pixel_size(ctx, &main_face, sz_px, true);
unsigned text_height = font_units_to_pixels_y(main_face.freetype, main_face.freetype->height);
RenderState rs = {
.current_face = &main_face, .fg = fg, .bg = bg,
.current_face = &main_face, .fg = fg, .bg = bg, .horizontally_center = horizontally_center_runs,
.output_width = output_width, .output_height = height, .stride = width,
.output = (pixel*)output_buf, .x = x_offset, .y = y_offset, .sz_px = sz_px
};
@ -584,7 +590,7 @@ render_line(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
uint8_t *buffer = (uint8_t*) PyBytes_AS_STRING(ans);
RenderCtx *ctx = (RenderCtx*)create_freetype_render_context(family, bold, italic);
if (!ctx) return NULL;
if (!render_single_line((FreeTypeRenderCtx)ctx, text, 3 * height / 4, 0, 0xffffffff, buffer, width, height, x_offset, y_offset, right_margin)) {
if (!render_single_line((FreeTypeRenderCtx)ctx, text, 3 * height / 4, 0, 0xffffffff, buffer, width, height, x_offset, y_offset, right_margin, false)) {
Py_CLEAR(ans);
if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError, "Unknown error while rendering text");
ans = NULL;

View file

@ -13,7 +13,7 @@ typedef struct {bool created;} *FreeTypeRenderCtx;
FreeTypeRenderCtx create_freetype_render_context(const char *family, bool bold, bool italic);
void set_main_face_family(FreeTypeRenderCtx ctx, const char *family, bool bold, bool italic);
bool render_single_line(FreeTypeRenderCtx ctx, const char *text, unsigned sz_px, 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);
bool render_single_line(FreeTypeRenderCtx ctx, const char *text, unsigned sz_px, 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, bool horizontally_center_runs);
uint8_t* render_single_ascii_char_as_mask(FreeTypeRenderCtx ctx_, const char ch, size_t *result_width, size_t *result_height);
void release_freetype_render_context(FreeTypeRenderCtx ctx);

2
kitty/glfw-wrapper.h generated
View file

@ -1523,7 +1523,7 @@ typedef void (* GLFWjoystickfun)(int,int);
typedef void (* GLFWuserdatafun)(unsigned long long, void*);
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 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, bool is_single_glyph);
typedef char* (* GLFWcurrentselectionfun)(void);
typedef bool (* GLFWhascurrentselectionfun)(void);
typedef void (* GLFWclipboarddatafreefun)(void* data);

View file

@ -663,7 +663,7 @@ ensure_csd_title_render_ctx(void) {
}
static bool
draw_text_callback(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) {
draw_text_callback(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, bool is_single_glyph) {
if (!set_callback_window(window)) return false;
if (!ensure_csd_title_render_ctx()) return false;
double xdpi, ydpi;
@ -671,8 +671,11 @@ draw_text_callback(GLFWwindow *window, const char *text, uint32_t fg, uint32_t b
unsigned px_sz = (unsigned)(global_state.callback_os_window->fonts_data->font_sz_in_pts * ydpi / 72.);
px_sz = MIN(px_sz, 3 * height / 4);
static char title[2048];
snprintf(title, sizeof(title), " ❭ %s", text);
bool ok = render_single_line(csd_title_render_ctx, title, px_sz, fg, bg, output_buf, width, height, x_offset, y_offset, right_margin);
if (!is_single_glyph) {
snprintf(title, sizeof(title), " ❭ %s", text);
text = title;
}
bool ok = render_single_line(csd_title_render_ctx, text, px_sz, fg, bg, output_buf, width, height, x_offset, y_offset, right_margin, is_single_glyph);
if (!ok && PyErr_Occurred()) PyErr_Print();
return ok;
}
@ -685,7 +688,7 @@ draw_window_title(OSWindow *window, const char *text, color_type fg, color_type
unsigned px_sz = (unsigned)(window->fonts_data->font_sz_in_pts * window->fonts_data->logical_dpi_y / 72.);
px_sz = MIN(px_sz, 3 * height / 4);
#define RGB2BGR(x) (x & 0xFF000000) | ((x & 0xFF0000) >> 16) | (x & 0x00FF00) | ((x & 0x0000FF) << 16)
bool ok = render_single_line(csd_title_render_ctx, buf, px_sz, RGB2BGR(fg), RGB2BGR(bg), output_buf, width, height, 0, 0, 0);
bool ok = render_single_line(csd_title_render_ctx, buf, px_sz, RGB2BGR(fg), RGB2BGR(bg), output_buf, width, height, 0, 0, 0, false);
#undef RGB2BGR
if (!ok && PyErr_Occurred()) PyErr_Print();
return ok;