diff --git a/kitty/data-types.h b/kitty/data-types.h index e6bcda186..42287b00e 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -65,9 +65,9 @@ typedef enum MouseTrackingProtocols { NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOC typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape; typedef enum { NONE, MENUBAR, WINDOW, ALL } WindowTitleIn; typedef enum { TILING, SCALED, MIRRORED, CLAMPED } BackgroundImageLayout; -typedef struct { - float x, y; -} BackgroundImageAnchor; +typedef struct ImageAnchorPosition { + float canvas_x, canvas_y, image_x, image_y; +} ImageAnchorPosition; #define MAX_CHILDREN 512 #define BLANK_CHAR 0 diff --git a/kitty/glfw.c b/kitty/glfw.c index 84b545d5a..df0187067 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -541,6 +541,7 @@ make_os_window_context_current(OSWindow *w) { GLFWwindow *current_context = glfwGetCurrentContext(); if (w->handle != current_context) { 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->last_mouse_activity_at = now; w->is_semi_transparent = is_semi_transparent; + global_state.current_opengl_context_id = w->id; if (want_semi_transparent && !w->is_semi_transparent) { static bool warned = false; if (!warned) { diff --git a/kitty/options/to-c.h b/kitty/options/to-c.h index 1208df6bc..d568a1b32 100644 --- a/kitty/options/to-c.h +++ b/kitty/options/to-c.h @@ -78,19 +78,19 @@ bglayout(PyObject *layout_name) { return TILING; } -static BackgroundImageAnchor +static ImageAnchorPosition bganchor(PyObject *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) { - anchor.y = 0.0f; + anchor.canvas_y = 0.f; anchor.image_y = 0.f; } 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) { - anchor.x = 0.0f; + anchor.canvas_x = 0.f; anchor.image_x = 0.f; } else if (strstr(name, "right") != NULL) { - anchor.x = 1.0f; + anchor.canvas_x = 1.f; anchor.image_x = 1.f; } return anchor; } diff --git a/kitty/shaders.c b/kitty/shaders.c index b417eb5aa..ff7e3c0a1 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -9,6 +9,7 @@ #include "gl.h" #include "colors.h" #include +#include "window_logo.h" #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 @@ -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; } +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 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; @@ -652,7 +666,7 @@ draw_visual_bell_flash(GLfloat intensity, GLfloat xstart, GLfloat ystart, GLfloa } 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); BLEND_ONTO_OPAQUE; @@ -667,10 +681,11 @@ draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWind 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( 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 glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 2); 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 -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) { glEnable(GL_BLEND); BLEND_PREMULT; @@ -722,7 +737,8 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen glEnable(GL_BLEND); 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( GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs); 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 ); #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 (screen->grman->count || has_bgimage(os_window)) draw_cells_interleaved_premult( - vao_idx, gvao_idx, screen, os_window, xstart, ystart, w, h); + if (screen->grman->count || has_underlying_image) draw_cells_interleaved_premult( + vao_idx, gvao_idx, screen, os_window, xstart, ystart, w, h, wl); else draw_cells_simple(vao_idx, gvao_idx, screen); } else { - if (screen->grman->num_of_negative_refs || screen->grman->num_of_below_refs || has_bgimage(os_window)) draw_cells_interleaved( - vao_idx, gvao_idx, screen, os_window, xstart, ystart, w, h); + 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, wl); else draw_cells_simple(vao_idx, gvao_idx, screen); } diff --git a/kitty/state.c b/kitty/state.c index 11574e2de..195e05c8e 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -224,12 +224,30 @@ release_gpu_resources_for_window(Window *w) { 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 initialize_window(Window *w, PyObject *title, bool init_gpu_resources) { w->id = ++global_state.window_id_counter; w->visible = true; w->title = title; Py_XINCREF(title); + set_default_window_logo(w); if (init_gpu_resources) create_gpu_resources_for_window(w); else { w->render_data.vao_idx = -1; @@ -290,6 +308,7 @@ destroy_window(Window *w) { Py_CLEAR(w->render_data.screen); Py_CLEAR(w->title); free(w->title_bar_data.buf); w->title_bar_data.buf = NULL; release_gpu_resources_for_window(w); + decref_window_logo(&global_state.all_window_logos, &w->window_logo.instance); } static void diff --git a/kitty/state.h b/kitty/state.h index 42f9c8b4a..b84eb5950 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -8,6 +8,7 @@ #include "data-types.h" #include "screen.h" #include "monotonic.h" +#include "window_logo.h" #define OPT(name) global_state.opts.name @@ -47,7 +48,7 @@ typedef struct { char *background_image, *default_window_logo; BackgroundImageLayout background_image_layout; - BackgroundImageAnchor window_logo_position; + ImageAnchorPosition window_logo_position; bool background_image_linear; float background_tint; @@ -81,6 +82,12 @@ typedef struct { } tab_bar_margin_height; } Options; +typedef struct WindowLogoRenderData { + WindowLogo *instance; + ImageAnchorPosition position; + bool using_default; +} WindowLogoRenderData; + typedef struct { ssize_t vao_idx, gvao_idx; float xstart, ystart, dx, dy; @@ -116,6 +123,7 @@ typedef struct { CursorShape last_cursor_shape; PyObject *title; ScreenRenderData render_data; + WindowLogoRenderData window_logo; MousePosition mouse_pos; struct { unsigned int left, top, right, bottom; @@ -205,7 +213,7 @@ typedef struct { typedef struct { 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; BackgroundImage *bgimage; OSWindow *os_windows; @@ -222,6 +230,7 @@ typedef struct { int active_drag_button; CloseRequest quit_request; bool redirect_mouse_handling; + WindowLogo *all_window_logos; } GlobalState; extern GlobalState global_state; diff --git a/kitty/window_logo.c b/kitty/window_logo.c new file mode 100644 index 000000000..dd0d24e62 --- /dev/null +++ b/kitty/window_logo.c @@ -0,0 +1,67 @@ +/* + * window_logo.c + * Copyright (C) 2021 Kovid Goyal + * + * 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--; } +} diff --git a/kitty/window_logo.h b/kitty/window_logo.h new file mode 100644 index 000000000..686307399 --- /dev/null +++ b/kitty/window_logo.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 Kovid Goyal + * + * 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);