More work on per window logo

This commit is contained in:
Kovid Goyal
2021-12-03 13:13:37 +05:30
parent e04919098f
commit 3d2877eeb9
8 changed files with 168 additions and 20 deletions

View File

@@ -65,9 +65,9 @@ typedef enum MouseTrackingProtocols { NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOC
typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape; typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
typedef enum { NONE, MENUBAR, WINDOW, ALL } WindowTitleIn; typedef enum { NONE, MENUBAR, WINDOW, ALL } WindowTitleIn;
typedef enum { TILING, SCALED, MIRRORED, CLAMPED } BackgroundImageLayout; typedef enum { TILING, SCALED, MIRRORED, CLAMPED } BackgroundImageLayout;
typedef struct { typedef struct ImageAnchorPosition {
float x, y; float canvas_x, canvas_y, image_x, image_y;
} BackgroundImageAnchor; } ImageAnchorPosition;
#define MAX_CHILDREN 512 #define MAX_CHILDREN 512
#define BLANK_CHAR 0 #define BLANK_CHAR 0

View File

@@ -541,6 +541,7 @@ make_os_window_context_current(OSWindow *w) {
GLFWwindow *current_context = glfwGetCurrentContext(); GLFWwindow *current_context = glfwGetCurrentContext();
if (w->handle != current_context) { if (w->handle != current_context) {
glfwMakeContextCurrent(w->handle); glfwMakeContextCurrent(w->handle);
global_state.current_opengl_context_id = w->id;
} }
} }
@@ -870,6 +871,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
w->cursor_blink_zero_time = now; w->cursor_blink_zero_time = now;
w->last_mouse_activity_at = now; w->last_mouse_activity_at = now;
w->is_semi_transparent = is_semi_transparent; w->is_semi_transparent = is_semi_transparent;
global_state.current_opengl_context_id = w->id;
if (want_semi_transparent && !w->is_semi_transparent) { if (want_semi_transparent && !w->is_semi_transparent) {
static bool warned = false; static bool warned = false;
if (!warned) { if (!warned) {

View File

@@ -78,19 +78,19 @@ bglayout(PyObject *layout_name) {
return TILING; return TILING;
} }
static BackgroundImageAnchor static ImageAnchorPosition
bganchor(PyObject *anchor_name) { bganchor(PyObject *anchor_name) {
const char *name = PyUnicode_AsUTF8(anchor_name); const char *name = PyUnicode_AsUTF8(anchor_name);
BackgroundImageAnchor anchor = { .x = 0.5f, .y = 0.5f }; ImageAnchorPosition anchor = {0.5f, 0.5f, 0.5f, 0.5f};
if (strstr(name, "top") != NULL) { if (strstr(name, "top") != NULL) {
anchor.y = 0.0f; anchor.canvas_y = 0.f; anchor.image_y = 0.f;
} else if (strstr(name, "bottom") != NULL) { } else if (strstr(name, "bottom") != NULL) {
anchor.y = 1.0f; anchor.canvas_y = 1.f; anchor.image_y = 1.f;
} }
if (strstr(name, "left") != NULL) { if (strstr(name, "left") != NULL) {
anchor.x = 0.0f; anchor.canvas_x = 0.f; anchor.image_x = 0.f;
} else if (strstr(name, "right") != NULL) { } else if (strstr(name, "right") != NULL) {
anchor.x = 1.0f; anchor.canvas_x = 1.f; anchor.image_x = 1.f;
} }
return anchor; return anchor;
} }

View File

@@ -9,6 +9,7 @@
#include "gl.h" #include "gl.h"
#include "colors.h" #include "colors.h"
#include <stddef.h> #include <stddef.h>
#include "window_logo.h"
#define BLEND_ONTO_OPAQUE glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // blending onto opaque colors #define BLEND_ONTO_OPAQUE glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // blending onto opaque colors
#define BLEND_PREMULT glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // blending of pre-multiplied colors #define BLEND_PREMULT glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // blending of pre-multiplied colors
@@ -578,6 +579,19 @@ render_window_title(OSWindow *os_window, Screen *screen UNUSED, GLfloat xstart,
return 2.f * (GLfloat)bar_height / (GLfloat)os_window->viewport_height; return 2.f * (GLfloat)bar_height / (GLfloat)os_window->viewport_height;
} }
static void
draw_window_logo(int program, OSWindow *os_window, const WindowLogoRenderData *wl, GLfloat window_left_gl, GLfloat window_top_gl, GLfloat window_width_gl, GLfloat window_height_gl) {
GLfloat logo_width_gl = 2.f * ((float)wl->instance->width) / os_window->viewport_width;
GLfloat logo_height_gl = 2.f * ((float)wl->instance->height) / os_window->viewport_height;
GLfloat logo_left_gl = window_left_gl + window_width_gl * wl->position.canvas_x - logo_width_gl * wl->position.image_x;
GLfloat logo_top_gl = window_top_gl - window_height_gl * wl->position.canvas_y + logo_height_gl * wl->position.image_y;
static ImageRenderData ird = {.group_count=1};
ird.texture_id = wl->instance->texture_id;
gpu_data_for_image(&ird, logo_left_gl, logo_top_gl, logo_left_gl + logo_width_gl, logo_top_gl - logo_height_gl);
send_graphics_data_to_gpu(1, os_window->gvao_idx, &ird);
draw_graphics(program, 0, os_window->gvao_idx, &ird, 0, 1);
}
static void static void
draw_window_number(OSWindow *os_window, Screen *screen, GLfloat xstart, GLfloat ystart, GLfloat width, GLfloat height, Window *window, GLfloat dx, GLfloat dy) { draw_window_number(OSWindow *os_window, Screen *screen, GLfloat xstart, GLfloat ystart, GLfloat width, GLfloat height, Window *window, GLfloat dx, GLfloat dy) {
GLfloat left = os_window->viewport_width * (xstart + 1.f) / 2.f; GLfloat left = os_window->viewport_width * (xstart + 1.f) / 2.f;
@@ -652,7 +666,7 @@ draw_visual_bell_flash(GLfloat intensity, GLfloat xstart, GLfloat ystart, GLfloa
} }
static void static void
draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWindow *w, GLfloat xstart, GLfloat ystart, GLfloat width, GLfloat height) { draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWindow *w, GLfloat xstart, GLfloat ystart, GLfloat width, GLfloat height, const WindowLogoRenderData *wl) {
glEnable(GL_BLEND); glEnable(GL_BLEND);
BLEND_ONTO_OPAQUE; BLEND_ONTO_OPAQUE;
@@ -667,10 +681,11 @@ draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWind
BLEND_ONTO_OPAQUE; BLEND_ONTO_OPAQUE;
} }
if (screen->grman->num_of_below_refs || has_bgimage(w)) { if (screen->grman->num_of_below_refs || has_bgimage(w) || wl) {
if (wl) draw_window_logo(GRAPHICS_PROGRAM, w, wl, xstart, ystart, width, height);
bind_program(CELL_BG_PROGRAM);
if (screen->grman->num_of_below_refs) draw_graphics( if (screen->grman->num_of_below_refs) draw_graphics(
GRAPHICS_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs); GRAPHICS_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs);
bind_program(CELL_BG_PROGRAM);
// draw background for non-default bg cells // draw background for non-default bg cells
glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 2); glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 2);
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns);
@@ -692,7 +707,7 @@ draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWind
} }
static void static void
draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWindow *os_window, GLfloat xstart, GLfloat ystart, GLfloat width, GLfloat height) { draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWindow *os_window, GLfloat xstart, GLfloat ystart, GLfloat width, GLfloat height, const WindowLogoRenderData *wl) {
if (OPT(background_tint) > 0.f) { if (OPT(background_tint) > 0.f) {
glEnable(GL_BLEND); glEnable(GL_BLEND);
BLEND_PREMULT; BLEND_PREMULT;
@@ -722,7 +737,8 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen
glEnable(GL_BLEND); glEnable(GL_BLEND);
BLEND_PREMULT; BLEND_PREMULT;
if (screen->grman->num_of_below_refs || has_bgimage(os_window)) { if (screen->grman->num_of_below_refs || has_bgimage(os_window) || wl) {
if (wl) draw_window_logo(GRAPHICS_PREMULT_PROGRAM, os_window, wl, xstart, ystart, width, height);
if (screen->grman->num_of_below_refs) draw_graphics( if (screen->grman->num_of_below_refs) draw_graphics(
GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs); GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs);
bind_program(CELL_BG_PROGRAM); bind_program(CELL_BG_PROGRAM);
@@ -841,13 +857,19 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat ystart, GL
(GLsizei)roundf(SCALE(height, h / 2.f)) // height (GLsizei)roundf(SCALE(height, h / 2.f)) // height
); );
#undef SCALE #undef SCALE
bool has_underlying_image = has_bgimage(os_window);
WindowLogoRenderData *wl = &window->window_logo;
if (wl->instance && wl->instance->load_from_disk_ok) {
has_underlying_image = true;
set_on_gpu_state(window->window_logo.instance, true);
} else wl = NULL;
if (os_window->is_semi_transparent) { if (os_window->is_semi_transparent) {
if (screen->grman->count || has_bgimage(os_window)) draw_cells_interleaved_premult( if (screen->grman->count || has_underlying_image) draw_cells_interleaved_premult(
vao_idx, gvao_idx, screen, os_window, xstart, ystart, w, h); vao_idx, gvao_idx, screen, os_window, xstart, ystart, w, h, wl);
else draw_cells_simple(vao_idx, gvao_idx, screen); else draw_cells_simple(vao_idx, gvao_idx, screen);
} else { } else {
if (screen->grman->num_of_negative_refs || screen->grman->num_of_below_refs || has_bgimage(os_window)) draw_cells_interleaved( if (screen->grman->num_of_negative_refs || screen->grman->num_of_below_refs || has_underlying_image) draw_cells_interleaved(
vao_idx, gvao_idx, screen, os_window, xstart, ystart, w, h); vao_idx, gvao_idx, screen, os_window, xstart, ystart, w, h, wl);
else draw_cells_simple(vao_idx, gvao_idx, screen); else draw_cells_simple(vao_idx, gvao_idx, screen);
} }

View File

@@ -224,12 +224,30 @@ release_gpu_resources_for_window(Window *w) {
w->render_data.gvao_idx = -1; w->render_data.gvao_idx = -1;
} }
static void
set_default_window_logo(Window *w) {
if (OPT(default_window_logo)) {
WindowLogo *wl = find_or_create_window_logo(&global_state.all_window_logos, OPT(default_window_logo));
if (wl) {
w->window_logo.instance = wl;
w->window_logo.position = OPT(window_logo_position);
} else if (PyErr_Occurred()) {
log_error("Failed to load default window logo: %s", OPT(default_window_logo));
PyErr_Print();
}
} else {
decref_window_logo(&global_state.all_window_logos, &w->window_logo.instance);
}
w->window_logo.using_default = true;
}
static void static void
initialize_window(Window *w, PyObject *title, bool init_gpu_resources) { initialize_window(Window *w, PyObject *title, bool init_gpu_resources) {
w->id = ++global_state.window_id_counter; w->id = ++global_state.window_id_counter;
w->visible = true; w->visible = true;
w->title = title; w->title = title;
Py_XINCREF(title); Py_XINCREF(title);
set_default_window_logo(w);
if (init_gpu_resources) create_gpu_resources_for_window(w); if (init_gpu_resources) create_gpu_resources_for_window(w);
else { else {
w->render_data.vao_idx = -1; w->render_data.vao_idx = -1;
@@ -290,6 +308,7 @@ destroy_window(Window *w) {
Py_CLEAR(w->render_data.screen); Py_CLEAR(w->title); Py_CLEAR(w->render_data.screen); Py_CLEAR(w->title);
free(w->title_bar_data.buf); w->title_bar_data.buf = NULL; free(w->title_bar_data.buf); w->title_bar_data.buf = NULL;
release_gpu_resources_for_window(w); release_gpu_resources_for_window(w);
decref_window_logo(&global_state.all_window_logos, &w->window_logo.instance);
} }
static void static void

View File

@@ -8,6 +8,7 @@
#include "data-types.h" #include "data-types.h"
#include "screen.h" #include "screen.h"
#include "monotonic.h" #include "monotonic.h"
#include "window_logo.h"
#define OPT(name) global_state.opts.name #define OPT(name) global_state.opts.name
@@ -47,7 +48,7 @@ typedef struct {
char *background_image, *default_window_logo; char *background_image, *default_window_logo;
BackgroundImageLayout background_image_layout; BackgroundImageLayout background_image_layout;
BackgroundImageAnchor window_logo_position; ImageAnchorPosition window_logo_position;
bool background_image_linear; bool background_image_linear;
float background_tint; float background_tint;
@@ -81,6 +82,12 @@ typedef struct {
} tab_bar_margin_height; } tab_bar_margin_height;
} Options; } Options;
typedef struct WindowLogoRenderData {
WindowLogo *instance;
ImageAnchorPosition position;
bool using_default;
} WindowLogoRenderData;
typedef struct { typedef struct {
ssize_t vao_idx, gvao_idx; ssize_t vao_idx, gvao_idx;
float xstart, ystart, dx, dy; float xstart, ystart, dx, dy;
@@ -116,6 +123,7 @@ typedef struct {
CursorShape last_cursor_shape; CursorShape last_cursor_shape;
PyObject *title; PyObject *title;
ScreenRenderData render_data; ScreenRenderData render_data;
WindowLogoRenderData window_logo;
MousePosition mouse_pos; MousePosition mouse_pos;
struct { struct {
unsigned int left, top, right, bottom; unsigned int left, top, right, bottom;
@@ -205,7 +213,7 @@ typedef struct {
typedef struct { typedef struct {
Options opts; Options opts;
id_type os_window_id_counter, tab_id_counter, window_id_counter; id_type os_window_id_counter, tab_id_counter, window_id_counter, current_opengl_context_id;
PyObject *boss; PyObject *boss;
BackgroundImage *bgimage; BackgroundImage *bgimage;
OSWindow *os_windows; OSWindow *os_windows;
@@ -222,6 +230,7 @@ typedef struct {
int active_drag_button; int active_drag_button;
CloseRequest quit_request; CloseRequest quit_request;
bool redirect_mouse_handling; bool redirect_mouse_handling;
WindowLogo *all_window_logos;
} GlobalState; } GlobalState;
extern GlobalState global_state; extern GlobalState global_state;

67
kitty/window_logo.c Normal file
View File

@@ -0,0 +1,67 @@
/*
* window_logo.c
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include "window_logo.h"
#include "state.h"
typedef struct WindowLogoItem {
WindowLogoHead
unsigned int refcnt;
char *path;
UT_hash_handle hh;
} WindowLogoItem;
static void
free_window_logo(WindowLogoItem **head, WindowLogoItem **itemref) {
WindowLogoItem *item = *itemref;
free(item->path);
free(item->bitmap);
if (item->texture_id) free_texture(&item->texture_id);
HASH_DEL(*head, item);
free(item); itemref = NULL;
}
static void
send_logo_to_gpu(WindowLogoItem *s) {
send_image_to_gpu(&s->texture_id, s->bitmap, s->width, s->height, false, true, true, REPEAT_CLAMP);
}
void
set_on_gpu_state(WindowLogo *x, bool on_gpu) {
WindowLogoItem *s = (WindowLogoItem*)x;
if (s->load_from_disk_ok) {
if (on_gpu) { if (!s->texture_id) send_logo_to_gpu(s); }
else if (s->texture_id) free_texture(&s->texture_id);
}
}
WindowLogo*
find_or_create_window_logo(WindowLogo **head_, const char *path) {
WindowLogoItem **head = (WindowLogoItem**)head_;
WindowLogoItem *s = NULL;
HASH_FIND_STR(*head, path, s);
if (s) return (WindowLogo*)s;
s = calloc(1, sizeof *s);
size_t size;
if (!s) { PyErr_NoMemory(); return NULL; }
s->path = strdup(path);
if (!s->path) { free(s->bitmap); free(s); PyErr_NoMemory(); return NULL; }
if (png_path_to_bitmap(path, &s->bitmap, &s->width, &s->height, &size)) s->load_from_disk_ok = true;
s->refcnt++;
HASH_ADD_KEYPTR(hh, *head, s->path, strlen(s->path), s);
return (WindowLogo*)s;
}
void
decref_window_logo(WindowLogo **head_, WindowLogo** logo) {
WindowLogoItem **head = (WindowLogoItem**)head_;
WindowLogoItem **s = (WindowLogoItem**)logo;
if (*s) { if ((*s)->refcnt < 2) free_window_logo(head, s); else (*s)->refcnt--; }
}

29
kitty/window_logo.h Normal file
View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#pragma once
#include "kitty-uthash.h"
#define WindowLogoHead \
unsigned int height, width; \
bool load_from_disk_ok; \
uint32_t texture_id; \
uint8_t* bitmap;
typedef struct WindowLogo {
WindowLogoHead
} WindowLogo;
WindowLogo*
find_or_create_window_logo(WindowLogo **head, const char *path);
void
decref_window_logo(WindowLogo **head, WindowLogo** logo);
void
set_on_gpu_state(WindowLogo *logo, bool on_gpu);