From 2946ff18daab0829139f8664d7dd167fdfc31a66 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 4 Aug 2025 08:40:05 +0530 Subject: [PATCH] Port visual bell to new layer framework Use a separate layer for UI like visual bell, scroll indicator, etc. --- kitty/cell_fragment.glsl | 24 +++-- kitty/gl.c | 40 ++++++++- kitty/gl.h | 2 + kitty/glfw.c | 1 - kitty/linear2srgb.glsl | 4 + kitty/screen.c | 6 +- kitty/screen.h | 8 +- kitty/shaders.c | 186 ++++++++++++++++++++++++++------------- kitty/tint_fragment.glsl | 2 +- 9 files changed, 192 insertions(+), 81 deletions(-) diff --git a/kitty/cell_fragment.glsl b/kitty/cell_fragment.glsl index 8175e02bd..d2358c6bc 100644 --- a/kitty/cell_fragment.glsl +++ b/kitty/cell_fragment.glsl @@ -11,6 +11,7 @@ uniform sampler2DArray sprites; uniform sampler2D under_bg_layer; uniform sampler2D under_fg_layer; uniform sampler2D over_fg_layer; +uniform sampler2D ui_layer; in vec4 background; in float effective_text_alpha; @@ -90,28 +91,33 @@ vec4 adjust_foreground_contrast_with_background(vec4 text_fg, vec3 bg) { } vec4 layer_color(sampler2D s) { -#ifdef HAS_LAYERS - vec2 pos = gl_FragCoord.xy / vec2(textureSize(s, 0)); - return texture(s, pos); -#else - return vec4(0, 0, 0, 0); -#endif + // we need to clamp the co-ordinates as the blank texture has size 1x1 + ivec2 pos = clamp(ivec2(gl_FragCoord.xy), ivec2(0), textureSize(s, 0)-1); + return texelFetch(s, pos, 0); } +#ifdef HAS_LAYERS +#define blend_layer(code) code +#else +#define blend_layer(code) +#endif + void main() { vec4 ans_premul = vec4_premul(background); // if its a default background cell and there is an under_bg layer, use the // under_bg layer as the background - ans_premul = if_one_then(has_under_bg * cell_has_default_bg, layer_color(under_bg_layer), ans_premul); + blend_layer(ans_premul = if_one_then(has_under_bg * cell_has_default_bg, layer_color(under_bg_layer), ans_premul)); // blend in the under_fg layer - ans_premul = alpha_blend_premul(layer_color(under_fg_layer), ans_premul); + blend_layer(ans_premul = alpha_blend_premul(layer_color(under_fg_layer), ans_premul)); // blend in the foreground color vec4 text_fg = load_text_foreground_color(); text_fg = adjust_foreground_contrast_with_background(text_fg, background.rgb); vec4 text_fg_premul = calculate_premul_foreground_from_sprites(text_fg); ans_premul = alpha_blend_premul(text_fg_premul, ans_premul); // blend in the over_fg layer - ans_premul = alpha_blend_premul(layer_color(over_fg_layer), ans_premul); + blend_layer(ans_premul = alpha_blend_premul(layer_color(over_fg_layer), ans_premul)); + // blend in the UI layer + blend_layer(ans_premul = alpha_blend_premul(layer_color(ui_layer), ans_premul)); #ifdef IS_OPAQUE ans_premul.rgb /= ans_premul.a; ans_premul.a = 1.; diff --git a/kitty/gl.c b/kitty/gl.c index 8c96cd944..5d665a642 100644 --- a/kitty/gl.c +++ b/kitty/gl.c @@ -85,6 +85,28 @@ update_surface_size(int w, int h, GLuint offscreen_texture_id) { } } +static const char* +check_framebuffer_status(void) { + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + switch (status) { + case GL_FRAMEBUFFER_COMPLETE: return NULL; + case GL_FRAMEBUFFER_UNDEFINED: return("GL_FRAMEBUFFER_UNDEFINED"); + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: return("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"); + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: return("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"); + case GL_FRAMEBUFFER_UNSUPPORTED: return("GL_FRAMEBUFFER_UNSUPPORTED"); + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: return("GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"); + default: return("Unknown error"); + } +} + +void +check_framebuffer_status_or_die(void) { + const char *err = check_framebuffer_status(); + if (err != NULL) fatal("Framebuffer not complete with error: %s", err); +} + void free_texture(GLuint *tex_id) { glDeleteTextures(1, tex_id); @@ -97,7 +119,18 @@ free_framebuffer(GLuint *fb_id) { *fb_id = 0; } -static GLsizei saved_viewport[4] = {0}; +static struct { + GLsizei items[16][4]; + size_t used; +} saved_viewports; + +void +save_viewport_using_bottom_left_origin(GLsizei newx, GLsizei newy, GLsizei width, GLsizei height) { + if (saved_viewports.used >= arraysz(saved_viewports.items)) fatal("Too many nested saved viewports"); + GLsizei *saved_viewport = saved_viewports.items[saved_viewports.used++]; + glGetIntegerv(GL_VIEWPORT, saved_viewport); + glViewport(newx, newy, width, height); +} void save_viewport_using_top_left_origin(GLsizei newx, GLsizei newy, GLsizei width, GLsizei height) { @@ -106,6 +139,8 @@ save_viewport_using_top_left_origin(GLsizei newx, GLsizei newy, GLsizei width, G // OpenGL viewport co-ord system with origin at bottom left. Assumes that // the current viewport is the full window. Use restore_viewport() to // restore the viewport to what it was before. + if (saved_viewports.used >= arraysz(saved_viewports.items)) fatal("Too many nested saved viewports"); + GLsizei *saved_viewport = saved_viewports.items[saved_viewports.used++]; glGetIntegerv(GL_VIEWPORT, saved_viewport); newy = saved_viewport[3] - (newy + height); glViewport(newx, newy, width, height); @@ -113,8 +148,9 @@ save_viewport_using_top_left_origin(GLsizei newx, GLsizei newy, GLsizei width, G void restore_viewport(void) { + if (!saved_viewports.used) fatal("Trying to restore a viewport when none is saved"); + GLsizei *saved_viewport = saved_viewports.items[--saved_viewports.used]; glViewport(saved_viewport[0], saved_viewport[1], saved_viewport[2], saved_viewport[3]); - memset(saved_viewport, 0, sizeof(saved_viewport)); } // }}} diff --git a/kitty/gl.h b/kitty/gl.h index 71ab097df..af81f2d98 100644 --- a/kitty/gl.h +++ b/kitty/gl.h @@ -58,4 +58,6 @@ void unbind_vertex_array(void); void unbind_program(void); GLuint compile_shaders(GLenum shader_type, GLsizei count, const GLchar * const * string); void save_viewport_using_top_left_origin(GLsizei x, GLsizei y, GLsizei width, GLsizei height); +void save_viewport_using_bottom_left_origin(GLsizei x, GLsizei y, GLsizei width, GLsizei height); +void check_framebuffer_status_or_die(void); void restore_viewport(void); diff --git a/kitty/glfw.c b/kitty/glfw.c index 21f5808d2..43652a04d 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -1460,7 +1460,6 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) { glfwMakeContextCurrent(glfw_window); if (is_first_window) gl_init(); // Will make the GPU automatically apply SRGB gamma curve on the resulting framebuffer - glEnable(GL_FRAMEBUFFER_SRGB); bool is_semi_transparent = glfwGetWindowAttrib(glfw_window, GLFW_TRANSPARENT_FRAMEBUFFER); // blank the window once so that there is no initial flash of color // changing, in case the background color is not black diff --git a/kitty/linear2srgb.glsl b/kitty/linear2srgb.glsl index b2662a195..637632d55 100644 --- a/kitty/linear2srgb.glsl +++ b/kitty/linear2srgb.glsl @@ -17,3 +17,7 @@ float linear2srgb(float x) { vec3 linear2srgb(vec3 c) { return vec3(linear2srgb(c.r), linear2srgb(c.g), linear2srgb(c.b)); } + +vec3 srgb2linear(vec3 c) { + return vec3(srgb2linear(c.r), srgb2linear(c.g), srgb2linear(c.b)); +} diff --git a/kitty/screen.c b/kitty/screen.c index 59e14c68e..9d38a6568 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -674,9 +674,9 @@ dealloc(Screen* self) { free(self->as_ansi_buf.buf); free(self->last_rendered_window_char.canvas); if (self->lc) { cleanup_list_of_chars(self->lc); free(self->lc); self->lc = NULL; } - if (self->textures.under_bg.id) free_texture(&self->textures.under_bg.id); - if (self->textures.under_fg.id) free_texture(&self->textures.under_fg.id); - if (self->textures.over_fg.id) free_texture(&self->textures.over_fg.id); +#define w(which) if (self->textures.which.id) { free_texture(&self->textures.which.id); } if (self->textures.which.framebuffer_id) free_framebuffer(&self->textures.which.framebuffer_id) + w(under_bg); w(under_fg); w(over_fg); w(ui); +#undef w Py_TYPE(self)->tp_free((PyObject*)self); } // }}} diff --git a/kitty/screen.h b/kitty/screen.h index 19193deba..3e7f0c4ca 100644 --- a/kitty/screen.h +++ b/kitty/screen.h @@ -100,6 +100,10 @@ typedef struct { unsigned int cursor_x, cursor_y, scrolled_by; index_type lines, columns; color_type cursor_bg; + struct { + struct { float intensity; color_type color; } visual_bell; + struct { float frac, alpha; color_type color; unsigned cell_height; } scroll_bar; + } ui_layer; } last_rendered; bool is_dirty, scroll_changed, reload_all_gpu_data; Cursor *cursor; @@ -176,8 +180,8 @@ typedef struct { monotonic_t parsing_at; struct { struct { - uint32_t id, width, height; - } under_bg, under_fg, over_fg; + uint32_t id, width, height, framebuffer_id; + } under_bg, under_fg, over_fg, ui; } textures; } Screen; diff --git a/kitty/shaders.c b/kitty/shaders.c index b48f89b1f..d79049ec2 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -26,6 +26,7 @@ * test that window numbering and URL hover rendering both still work * test cursor trail and scroll indicator rendering * remove startx, starty, dx, dy from WindowRenderData + * fix rendering during live resize */ #define BLEND_ONTO_OPAQUE glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // blending onto opaque colors #define BLEND_ONTO_OPAQUE_WITH_OPAQUE_OUTPUT glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); // blending onto opaque colors with final color having alpha 1 @@ -40,7 +41,7 @@ enum { TRAIL_PROGRAM, NUM_PROGRAMS }; -enum { SPRITE_MAP_UNIT, GRAPHICS_UNIT, BGIMAGE_UNIT, SPRITE_DECORATIONS_MAP_UNIT, UNDER_BG_LAYER_UNIT, UNDER_FG_LAYER_UNIT, OVER_FG_LAYER_UNIT }; +enum { SPRITE_MAP_UNIT, GRAPHICS_UNIT, BGIMAGE_UNIT, SPRITE_DECORATIONS_MAP_UNIT, UNDER_BG_LAYER_UNIT, UNDER_FG_LAYER_UNIT, OVER_FG_LAYER_UNIT, UI_LAYER_UNIT }; // Sprites {{{ typedef struct { @@ -660,9 +661,92 @@ ensure_blank_texture(void) { return texture_id; } +static Animation *default_visual_bell_animation = NULL; + +static float +get_visual_bell_intensity(Screen *screen) { + if (screen->start_visual_bell_at > 0) { + if (!default_visual_bell_animation) { + default_visual_bell_animation = alloc_animation(); + if (!default_visual_bell_animation) fatal("Out of memory"); + add_cubic_bezier_animation(default_visual_bell_animation, 0, 1, EASE_IN_OUT); + add_cubic_bezier_animation(default_visual_bell_animation, 1, 0, EASE_IN_OUT); + } + const monotonic_t progress = monotonic() - screen->start_visual_bell_at; + const monotonic_t duration = OPT(visual_bell_duration) / 2; + if (progress <= duration) { + Animation *a = animation_is_valid(OPT(animation.visual_bell)) ? OPT(animation.visual_bell) : default_visual_bell_animation; + return (float)apply_easing_curve(a, progress / (double)duration, duration); + } + screen->start_visual_bell_at = 0; + } + return 0.0f; +} + +typedef struct UIRenderData { + unsigned screen_width, screen_height, cell_width, cell_height; + +} UIRenderData; + +static void +draw_visual_bell_flash(GLfloat intensity, const color_type flash) { + glEnable(GL_BLEND); + BLEND_PREMULT; + bind_program(TINT_PROGRAM); + GLfloat attenuation = 0.4f; +#define C(shift) srgb_color((flash >> shift) & 0xFF) + const GLfloat r = C(16), g = C(8), b = C(0); + const GLfloat max_channel = r > g ? (r > b ? r : b) : (g > b ? g : b); +#undef C +#define C(x) (x * intensity * attenuation) + if (max_channel > 0.45) attenuation = 0.6f; // light color + glUniform4f(tint_program_layout.uniforms.tint_color, C(r), C(g), C(b), C(1)); +#undef C + glUniform4f(tint_program_layout.uniforms.edges, -1, 1, 1, -1); + glDisable(GL_FRAMEBUFFER_SRGB); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDisable(GL_BLEND); +} + + +static bool +update_ui_layer(Screen *screen, UIRenderData ui, bool visual_bell_drawn, bool scrollback_indicator_drawn) { +#define last_ui screen->last_rendered.ui_layer + GLfloat intensity = 0; color_type flash = 0; + if (visual_bell_drawn) { + intensity = get_visual_bell_intensity(screen); +#define COLOR(name, fallback) colorprofile_to_color_with_fallback(screen->color_profile, screen->color_profile->overridden.name, screen->color_profile->configured.name, screen->color_profile->overridden.fallback, screen->color_profile->configured.fallback) + flash = !IS_SPECIAL_COLOR(highlight_bg) ? COLOR(visual_bell_color, highlight_bg) : COLOR(visual_bell_color, default_fg); +#undef COLOR + } + bool visual_bell_needs_redraw = intensity != last_ui.visual_bell.intensity || flash != last_ui.visual_bell.color; + last_ui.visual_bell.intensity = intensity; + last_ui.visual_bell.color = flash; + color_type bar_color = 0; + GLfloat bar_alpha = OPT(scrollback_indicator_opacity); + float bar_frac = 0; + if (scrollback_indicator_drawn) { + bar_color = colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.highlight_bg, screen->color_profile->configured.highlight_bg).rgb; + bar_frac = (float)screen->scrolled_by / (float)screen->historybuf->count; + } + bool scrollbar_needs_redraw = (last_ui.scroll_bar.frac != bar_frac || last_ui.scroll_bar.color != bar_color || + last_ui.scroll_bar.cell_height != ui.cell_height || last_ui.scroll_bar.alpha != bar_alpha); + last_ui.scroll_bar.alpha = bar_alpha; + last_ui.scroll_bar.cell_height = ui.cell_height; + last_ui.scroll_bar.color = bar_color; + last_ui.scroll_bar.frac = bar_frac; + if (!scrollbar_needs_redraw && !visual_bell_needs_redraw) return false; + blank_canvas(0, 0); // clear the framebuffer + save_viewport_using_bottom_left_origin(0, 0, ui.screen_width, ui.screen_height); + if (visual_bell_drawn && intensity > 0) draw_visual_bell_flash(last_ui.visual_bell.intensity, last_ui.visual_bell.color); + restore_viewport(); + return true; +#undef last_ui +} + static void draw_cells_with_layers( - bool for_final_output, OSWindow *os_window, Screen *screen, bool is_semi_transparent, GraphicsRenderData grd, WindowLogoRenderData *wl + bool for_final_output, OSWindow *os_window, Screen *screen, UIRenderData ui, bool is_semi_transparent, GraphicsRenderData grd, WindowLogoRenderData *wl ) { bool has_layers = false; bool has_background_image = has_bgimage(os_window); @@ -670,13 +754,34 @@ draw_cells_with_layers( GLuint blank_texture = ensure_blank_texture(); #define USE_BLANK(which) { if (screen->textures.which.id) { free_texture(&screen->textures.which.id); } glBindTexture(GL_TEXTURE_2D, blank_texture); } -#define ENSURE_TEXTURE(which) has_layers = true; if (!screen->textures.which.id) glGenTextures(1, &screen->textures.which.id) +#define ENSURE_TEXTURE(which) \ + has_layers = true; \ + if (screen->textures.which.width != ui.screen_width || screen->textures.which.height != ui.screen_height) { \ + if (screen->textures.which.id) free_texture(&screen->textures.which.id); \ + if (screen->textures.which.framebuffer_id) free_framebuffer(&screen->textures.which.framebuffer_id); \ + } \ + if (!screen->textures.which.id) { \ + glGenTextures(1, &screen->textures.which.id); \ + glGenFramebuffers(1, &screen->textures.which.framebuffer_id); \ + screen->textures.which.width = ui.screen_width; screen->textures.which.height = ui.screen_height; \ + glBindTexture(GL_TEXTURE_2D, screen->textures.which.id); \ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); \ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); \ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); \ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); \ + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, screen->textures.which.width, screen->textures.which.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); \ + glBindFramebuffer(GL_FRAMEBUFFER, screen->textures.which.framebuffer_id); \ + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screen->textures.which.id, 0); \ + check_framebuffer_status_or_die(); \ + } \ + glBindTexture(GL_TEXTURE_2D, screen->textures.which.id); \ + glBindFramebuffer(GL_FRAMEBUFFER, screen->textures.which.framebuffer_id); \ + glActiveTexture(GL_TEXTURE0 + UNDER_BG_LAYER_UNIT); if (grd.num_of_below_refs || has_background_image) { ENSURE_TEXTURE(under_bg); has_under_bg = 1.; } else USE_BLANK(under_bg); - glUniform1f(cell_program_layouts[CELL_PROGRAM].uniforms.has_under_bg, has_under_bg); glActiveTexture(GL_TEXTURE0 + UNDER_FG_LAYER_UNIT); if (grd.num_of_below_refs || wl) { @@ -687,8 +792,17 @@ draw_cells_with_layers( if (grd.num_of_positive_refs) { ENSURE_TEXTURE(over_fg); } else USE_BLANK(over_fg); + + glActiveTexture(GL_TEXTURE0 + UI_LAYER_UNIT); + bool visual_bell_drawn = screen->start_visual_bell_at > 0; + bool scrollback_indicator_drawn = !(OPT(scrollback_indicator_opacity) <= 0 || screen->linebuf != screen->main_linebuf || !screen->scrolled_by); + if (visual_bell_drawn || scrollback_indicator_drawn) { + ENSURE_TEXTURE(ui); + update_ui_layer(screen, ui, visual_bell_drawn, scrollback_indicator_drawn); + } else USE_BLANK(ui); #undef ENSURE_TEXTURE #undef USE_BLANK + glBindFramebuffer(GL_FRAMEBUFFER, 0); if (is_semi_transparent) { bind_program(has_layers ? CELL_LAYERS_TRANSPARENT_PROGRAM : CELL_TRANSPARENT_PROGRAM); @@ -705,13 +819,12 @@ draw_cells_with_layers( glDisable(GL_BLEND); if (for_final_output) glEnable(GL_FRAMEBUFFER_SRGB); else glDisable(GL_FRAMEBUFFER_SRGB); } + glUniform1f(cell_program_layouts[CELL_PROGRAM].uniforms.has_under_bg, has_under_bg); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); - glEnable(GL_FRAMEBUFFER_SRGB); } static bool draw_scroll_indicator(bool premult, Screen *screen, const CellRenderData *crd) { - if (OPT(scrollback_indicator_opacity) <= 0 || screen->linebuf != screen->main_linebuf || !screen->scrolled_by) return false; glEnable(GL_BLEND); if (premult) { BLEND_PREMULT } else { BLEND_ONTO_OPAQUE } bind_program(TINT_PROGRAM); @@ -752,6 +865,7 @@ set_cell_uniforms(float current_inactive_text_alpha, bool force) { glUniform1i(cu->under_bg_layer, UNDER_BG_LAYER_UNIT); glUniform1i(cu->under_fg_layer, UNDER_FG_LAYER_UNIT); glUniform1i(cu->over_fg_layer, OVER_FG_LAYER_UNIT); + glUniform1i(cu->ui_layer, UI_LAYER_UNIT); glUniform1i(cu->sprite_decorations_map, SPRITE_DECORATIONS_MAP_UNIT); glUniform1f(cu->dim_opacity, OPT(dim_opacity)); glUniform1f(cu->text_contrast, text_contrast); @@ -891,29 +1005,6 @@ draw_window_number(OSWindow *os_window, Screen *screen, const CellRenderData *cr glDisable(GL_BLEND); } -static void -draw_visual_bell_flash(GLfloat intensity, const CellRenderData *crd, Screen *screen) { - glEnable(GL_BLEND); - // BLEND_PREMULT - glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); - bind_program(TINT_PROGRAM); - GLfloat attenuation = 0.4f; -#define COLOR(name, fallback) colorprofile_to_color_with_fallback(screen->color_profile, screen->color_profile->overridden.name, screen->color_profile->configured.name, screen->color_profile->overridden.fallback, screen->color_profile->configured.fallback) - const color_type flash = !IS_SPECIAL_COLOR(highlight_bg) ? COLOR(visual_bell_color, highlight_bg) : COLOR(visual_bell_color, default_fg); -#undef COLOR -#define C(shift) srgb_color((flash >> shift) & 0xFF) - const GLfloat r = C(16), g = C(8), b = C(0); - const GLfloat max_channel = r > g ? (r > b ? r : b) : (g > b ? g : b); -#undef C -#define C(x) (x * intensity * attenuation) - if (max_channel > 0.45) attenuation = 0.6f; // light color - glUniform4f(tint_program_layout.uniforms.tint_color, C(r), C(g), C(b), C(1)); -#undef C - glUniform4f(tint_program_layout.uniforms.edges, crd->gl.xstart, crd->gl.ystart - crd->gl.height, crd->gl.xstart + crd->gl.width, crd->gl.ystart); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glDisable(GL_BLEND); -} - void blank_canvas(float background_opacity, color_type color) { // See https://github.com/glfw/glfw/issues/1538 for why we use pre-multiplied alpha @@ -932,28 +1023,6 @@ send_cell_data_to_gpu(ssize_t vao_idx, GLfloat xstart, GLfloat ystart, GLfloat d return changed; } -static Animation *default_visual_bell_animation = NULL; - -static float -get_visual_bell_intensity(Screen *screen) { - if (screen->start_visual_bell_at > 0) { - if (!default_visual_bell_animation) { - default_visual_bell_animation = alloc_animation(); - if (!default_visual_bell_animation) fatal("Out of memory"); - add_cubic_bezier_animation(default_visual_bell_animation, 0, 1, EASE_IN_OUT); - add_cubic_bezier_animation(default_visual_bell_animation, 1, 0, EASE_IN_OUT); - } - const monotonic_t progress = monotonic() - screen->start_visual_bell_at; - const monotonic_t duration = OPT(visual_bell_duration) / 2; - if (progress <= duration) { - Animation *a = animation_is_valid(OPT(animation.visual_bell)) ? OPT(animation.visual_bell) : default_visual_bell_animation; - return (float)apply_easing_curve(a, progress / (double)duration, duration); - } - screen->start_visual_bell_at = 0; - } - return 0.0f; -} - void draw_cells(bool for_final_output, const WindowRenderData *srd, OSWindow *os_window, bool is_active_window, bool is_tab_bar, bool is_single_window, Window *window) { float x_ratio = 1., y_ratio = 1.; @@ -1002,18 +1071,10 @@ draw_cells(bool for_final_output, const WindowRenderData *srd, OSWindow *os_wind has_underlying_image |= grd.num_of_below_refs > 0 || grd.num_of_negative_refs > 0; (void)has_underlying_image; bool is_semi_transparent = os_window->is_semi_transparent && min_bg_opacity < 1.; - save_viewport_using_top_left_origin( - srd->geometry.left, srd->geometry.top, srd->geometry.right - srd->geometry.left, - srd->geometry.bottom - srd->geometry.top - ); - draw_cells_with_layers(for_final_output, os_window, screen, is_semi_transparent, grd, wl); + UIRenderData ui = {srd->geometry.right - srd->geometry.left, srd->geometry.bottom - srd->geometry.top, os_window->fonts_data->fcm.cell_width, os_window->fonts_data->fcm.cell_height}; + save_viewport_using_top_left_origin(srd->geometry.left, srd->geometry.top, ui.screen_width, ui.screen_height); + draw_cells_with_layers(for_final_output, os_window, screen, ui, is_semi_transparent, grd, wl); draw_scroll_indicator(is_semi_transparent, screen, &crd); - - if (screen->start_visual_bell_at) { - GLfloat intensity = get_visual_bell_intensity(screen); - if (intensity > 0.0f) draw_visual_bell_flash(intensity, &crd, screen); - } - if (window && screen->display_window_char) draw_window_number(os_window, screen, &crd, window); if (OPT(show_hyperlink_targets) && window && screen->current_hyperlink_under_mouse.id && !is_mouse_hidden(os_window)) draw_hyperlink_target(os_window, screen, &crd, window); free(scaled_render_data); @@ -1072,7 +1133,6 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu glDisable(GL_BLEND); glDisable(GL_FRAMEBUFFER_SRGB); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, num_border_rects); - glEnable(GL_FRAMEBUFFER_SRGB); unbind_program(); } unbind_vertex_array(); diff --git a/kitty/tint_fragment.glsl b/kitty/tint_fragment.glsl index 3f82c857e..c9ee9c1b0 100644 --- a/kitty/tint_fragment.glsl +++ b/kitty/tint_fragment.glsl @@ -1,5 +1,5 @@ uniform vec4 tint_color; -out vec4 color; +out vec4 color; // must be in linear space void main() { color = tint_color;