Port visual bell to new layer framework

Use a separate layer for UI like visual bell, scroll indicator, etc.
This commit is contained in:
Kovid Goyal
2025-08-04 08:40:05 +05:30
parent ec42f5c2b9
commit 2946ff18da
9 changed files with 192 additions and 81 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
uniform vec4 tint_color;
out vec4 color;
out vec4 color; // must be in linear space
void main() {
color = tint_color;