diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 97ef49140..f308b842f 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -615,7 +615,7 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int * } static void -render_os_window(OSWindow *os_window, monotonic_t now, unsigned int active_window_id, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg) { +render_os_window(OSWindow *os_window, unsigned int active_window_id, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg) { // ensure all pixels are cleared to background color at least once in every buffer if (os_window->clear_count++ < 3) blank_os_window(os_window); Tab *tab = os_window->tabs + os_window->active_tab; @@ -637,8 +637,7 @@ render_os_window(OSWindow *os_window, monotonic_t now, unsigned int active_windo bool is_active_window = i == tab->active_window; draw_cells(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx * x_ratio, WD.dy * y_ratio, WD.screen, os_window, is_active_window, true); if (WD.screen->start_visual_bell_at != 0) { - monotonic_t bell_left = OPT(visual_bell_duration) - (now - WD.screen->start_visual_bell_at); - set_maximum_wait(bell_left); + set_maximum_wait(OPT(repaint_delay)); } w->cursor_visible_at_last_render = WD.screen->cursor_render_info.is_visible; w->last_cursor_x = WD.screen->cursor_render_info.x; w->last_cursor_y = WD.screen->cursor_render_info.y; w->last_cursor_shape = WD.screen->cursor_render_info.shape; } @@ -726,7 +725,7 @@ render(monotonic_t now, bool input_read) { if (prepare_to_render_os_window(w, now, &active_window_id, &active_window_bg, &num_visible_windows, &all_windows_have_same_bg, scan_for_animated_images)) needs_render = true; if (w->last_active_window_id != active_window_id || w->last_active_tab != w->active_tab || w->focused_at_last_render != w->is_focused) needs_render = true; if (w->render_calls < 3 && w->bgimage && w->bgimage->texture_id) needs_render = true; - if (needs_render) render_os_window(w, now, active_window_id, active_window_bg, num_visible_windows, all_windows_have_same_bg); + if (needs_render) render_os_window(w, active_window_id, active_window_bg, num_visible_windows, all_windows_have_same_bg); if (w->is_focused) change_menubar_title(w->window_title); } last_render_at = now; diff --git a/kitty/screen.c b/kitty/screen.c index 9b2876941..fa9740107 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -1512,12 +1512,43 @@ screen_use_latin1(Screen *self, bool on) { bool screen_invert_colors(Screen *self) { bool inverted = false; + if (self->modes.mDECSCNM) inverted = true; + return inverted; +} + +static inline float +ease_out_cubic(float phase) { + return 1.0f - powf(1.0f - phase, 3.0f); +} + +static inline float +ease_in_out_cubic(float phase) { + return phase < 0.5f ? + 4.0f * powf(phase, 3.0f) : + 1.0f - powf(-2.0f * phase + 2.0f, 3.0f) / 2.0f; +} + +static inline float +visual_bell_intensity(float phase) { + float peak = 0.2f; + float fade = 1.0f - peak; + if (phase < peak) + return ease_out_cubic(phase / peak); + else + return ease_in_out_cubic((1.0f - phase) / fade); +} + +float +screen_visual_bell_intensity(Screen *self) { if (self->start_visual_bell_at > 0) { - if (monotonic() - self->start_visual_bell_at <= OPT(visual_bell_duration)) inverted = true; + monotonic_t progress = monotonic() - self->start_visual_bell_at; + monotonic_t duration = OPT(visual_bell_duration); + if (progress <= duration) { + return visual_bell_intensity((float)progress / duration); + } else self->start_visual_bell_at = 0; } - if (self->modes.mDECSCNM) inverted = inverted ? false : true; - return inverted; + return 0.0f; } void diff --git a/kitty/screen.h b/kitty/screen.h index c1c089065..be2304be6 100644 --- a/kitty/screen.h +++ b/kitty/screen.h @@ -207,6 +207,7 @@ void screen_apply_selection(Screen *self, void *address, size_t size); bool screen_is_selection_dirty(Screen *self); bool screen_has_selection(Screen*); bool screen_invert_colors(Screen *self); +float screen_visual_bell_intensity(Screen *self); void screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE, bool cursor_has_moved); bool screen_is_cursor_visible(Screen *self); bool screen_selection_range_for_line(Screen *self, index_type y, index_type *start, index_type *end); diff --git a/kitty/shaders.c b/kitty/shaders.c index c5027fd0b..45b1dfb4a 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -458,6 +458,23 @@ draw_tint(bool premult, Screen *screen, GLfloat xstart, GLfloat ystart, GLfloat glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } +void +draw_visual_bell_flash(GLfloat intensity, GLfloat xstart, GLfloat ystart, GLfloat w, GLfloat h, Screen *screen) { + glEnable(GL_BLEND); + // BLEND_PREMULT + glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); + bind_program(TINT_PROGRAM); + // TODO config option worth? + GLfloat attenuation = 0.4f; + color_type flash = colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.highlight_bg, screen->color_profile->configured.highlight_bg); +#define C(shift) ((((GLfloat)((flash >> shift) & 0xFF)) / 255.0f) * intensity * attenuation) + glUniform4f(tint_program_layout.tint_color_location, C(16), C(8), C(0), intensity * attenuation); +#undef C + glUniform4f(tint_program_layout.edges_location, xstart, ystart - h, xstart + w, ystart); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDisable(GL_BLEND); +} + 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) { glEnable(GL_BLEND); @@ -650,6 +667,11 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat ystart, GL vao_idx, gvao_idx, screen, os_window, xstart, ystart, w, h); else draw_cells_simple(vao_idx, gvao_idx, screen); } + + float intensity = screen_visual_bell_intensity(screen); + if (intensity > 0.0f) { + draw_visual_bell_flash((GLfloat)intensity, xstart, ystart, w, h, screen); + } } // }}}