diff --git a/kitty/boss.py b/kitty/boss.py index f9fe61e1b..b955982a5 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -19,11 +19,11 @@ from .constants import ( appname, config_dir, editor, set_boss, supports_primary_selection ) from .fast_data_types import ( - ChildMonitor, create_os_window, current_os_window, destroy_global_data, - destroy_sprite_map, get_clipboard_string, - glfw_post_empty_event, layout_sprite_map, mark_os_window_for_close, - set_clipboard_string, set_dpi_from_os_window, set_in_sequence_mode, - show_window, toggle_fullscreen, viewport_for_window + ChildMonitor, change_background_opacity, create_os_window, + current_os_window, destroy_global_data, destroy_sprite_map, + get_clipboard_string, glfw_post_empty_event, layout_sprite_map, + mark_os_window_for_close, set_clipboard_string, set_dpi_from_os_window, + set_in_sequence_mode, show_window, toggle_fullscreen, viewport_for_window ) from .fonts.render import prerender, resize_fonts, set_font_family from .keys import get_shortcut, shortcut_matches @@ -361,6 +361,9 @@ class Boss: def on_dpi_change(self, os_window_id): self._change_font_size() + def _set_os_window_background_opacity(self, os_window_id, opacity): + change_background_opacity(os_window_id, opacity) + @property def active_tab_manager(self): os_window_id = current_os_window() diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index ea7e802cb..f069dc689 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -614,7 +614,7 @@ static inline void render_os_window(OSWindow *os_window, double now, unsigned int active_window_id, color_type active_window_bg, unsigned int num_visible_windows) { Tab *tab = os_window->tabs + os_window->active_tab; BorderRects *br = &tab->border_rects; - draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, os_window->viewport_width, os_window->viewport_height, active_window_bg, num_visible_windows); + draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, os_window->viewport_width, os_window->viewport_height, active_window_bg, num_visible_windows, os_window); if (TD.screen && os_window->num_tabs > 1) draw_cells(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, os_window, true); for (unsigned int i = 0; i < tab->num_windows; i++) { Window *w = tab->windows + i; diff --git a/kitty/cmds.py b/kitty/cmds.py index bdc9c9b2b..5ed308ea0 100644 --- a/kitty/cmds.py +++ b/kitty/cmds.py @@ -586,6 +586,45 @@ def set_colors(boss, window, payload): # }}} +# set_background_opacity {{{ +@cmd( + 'Set the background_opacity', + 'Set the background opacity for the specified windows. This will only work if you have turned on' + ' dynamic_background_opacity in kitty.conf. The background opacity affects all kitty windows in a' + ' single os_window. For example: kitty @ set-background-opacity 0.5', + options_spec='''\ +--all -a +type=bool-set +By default, colors are only changed for the currently active window. This option will +cause colors to be changed in all windows. + +''' + '\n\n' + MATCH_WINDOW_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t'), + argspec='OPACITY' +) +def cmd_set_background_opacity(global_opts, opts, args): + if len(args) != 1: + raise SystemExit('Must specify exactly one argument, the new opacity') + opacity = max(0.1, min(float(args[0]), 1.0)) + return { + 'opacity': opacity, 'match_window': opts.match, + 'all': opts.all or opts.reset, + } + + +def set_background_opacity(boss, window, payload): + if payload['all']: + windows = tuple(boss.all_windows) + else: + windows = (window or boss.active_window,) + if payload['match_window']: + windows = tuple(boss.match_windows(payload['match_window'])) + if not windows: + raise MatchError(payload['match_window']) + for os_window_id in {w.os_window_id for w in windows}: + boss._set_os_window_background_opacity(os_window_id, payload['opacity']) +# }}} + + cmap = {v.name: v for v in globals().values() if hasattr(v, 'is_cmd')} diff --git a/kitty/config.py b/kitty/config.py index a7291a944..b2762faf5 100644 --- a/kitty/config.py +++ b/kitty/config.py @@ -354,6 +354,7 @@ type_map = { 'macos_option_as_alt': to_bool, 'macos_titlebar_color': macos_titlebar_color, 'box_drawing_scale': box_drawing_scale, + 'dynamic_background_opacity': to_bool, 'background_opacity': unit_float, 'dim_opacity': unit_float, 'tab_separator': tab_separator, diff --git a/kitty/glfw.c b/kitty/glfw.c index 1b77e3194..920d434cc 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -365,7 +365,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) { PyErr_SetString(PyExc_ValueError, "Too many windows"); return NULL; } - bool want_semi_transparent = (1.0 - OPT(background_opacity) >= 0.01) ? true : false; + bool want_semi_transparent = (1.0 - OPT(background_opacity) >= 0.01) || OPT(dynamic_background_opacity); glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, want_semi_transparent); GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL); if (glfw_window == NULL) { diff --git a/kitty/kitty.conf b/kitty/kitty.conf index c63276aa3..4372011c9 100644 --- a/kitty/kitty.conf +++ b/kitty/kitty.conf @@ -257,7 +257,10 @@ background #000000 # in the editor color scheme. Or use the escape codes to set the terminals # default colors in a shell script to launch your editor. # Be aware that using a value less than 1.0 is a (possibly significant) performance hit. +# If you want to dynamically change transparency of windows using the kitty remote control facility, +# set dynamic_background_opacity to yes background_opacity 1.0 +dynamic_background_opacity no # How much to dim text that has the DIM/FAINT attribute set. 1.0 means no dimming and # 0.0 means fully dimmed (i.e. invisible). diff --git a/kitty/shaders.c b/kitty/shaders.c index 111f187a8..c27de825c 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -209,7 +209,7 @@ struct CellUniformData { static struct CellUniformData cell_uniform_data = {0, .prev_inactive_text_alpha=-1}; static inline void -cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, CursorRenderInfo *cursor, bool inverted) { +cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, CursorRenderInfo *cursor, bool inverted, OSWindow *os_window) { struct CellRenderData { GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity; @@ -239,7 +239,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G sprite_tracker_current_layout(&x, &y, &z); rd->sprite_dx = 1.0f / (float)x; rd->sprite_dy = 1.0f / (float)y; rd->inverted = inverted ? 1 : 0; - rd->background_opacity = OPT(background_opacity); + rd->background_opacity = os_window->background_opacity; #define COLOR(name) colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.name, screen->color_profile->configured.name) rd->default_fg = COLOR(default_fg); rd->default_bg = COLOR(default_bg); rd->highlight_fg = COLOR(highlight_fg); rd->highlight_bg = COLOR(highlight_bg); @@ -415,7 +415,7 @@ send_cell_data_to_gpu(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat if (os_window->clear_count < 2) { os_window->clear_count++; #define C(shift) (((GLfloat)((OPT(background) >> shift) & 0xFF)) / 255.0f) - glClearColor(C(16), C(8), C(0), os_window->is_semi_transparent ? OPT(background_opacity) : 1.0f); + glClearColor(C(16), C(8), C(0), os_window->is_semi_transparent ? os_window->background_opacity : 1.0f); #undef C glClear(GL_COLOR_BUFFER_BIT); changed = true; @@ -430,7 +430,7 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat ystart, GL CELL_BUFFERS; bool inverted = screen_invert_colors(screen); - cell_update_uniform_block(vao_idx, screen, uniform_buffer, xstart, ystart, dx, dy, &screen->cursor_render_info, inverted); + cell_update_uniform_block(vao_idx, screen, uniform_buffer, xstart, ystart, dx, dy, &screen->cursor_render_info, inverted, os_window); bind_vao_uniform_buffer(vao_idx, uniform_buffer, cell_program_layouts[CELL_PROGRAM].render_data.index); bind_vertex_array(vao_idx); @@ -522,7 +522,7 @@ create_border_vao() { } void -draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, uint32_t viewport_width, uint32_t viewport_height, color_type active_window_bg, unsigned int num_visible_windows) { +draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, uint32_t viewport_width, uint32_t viewport_height, color_type active_window_bg, unsigned int num_visible_windows, OSWindow *w) { if (num_border_rects) { if (rect_data_is_dirty) { size_t sz = sizeof(GLuint) * 5 * num_border_rects; @@ -535,11 +535,14 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu #define CV3(x) (((float)((x >> 16) & 0xff))/255.f), (((float)((x >> 8) & 0xff))/255.f), (((float)(x & 0xff))/255.f) if (!constants_set) { constants_set = true; - glUniform1f(border_uniform_locations[BORDER_background_opacity], OPT(background_opacity)); + glUniform1f(border_uniform_locations[BORDER_background_opacity], w->background_opacity); glUniform3f(border_uniform_locations[BORDER_active_border_color], CV3(OPT(active_border_color))); glUniform3f(border_uniform_locations[BORDER_inactive_border_color], CV3(OPT(inactive_border_color))); glUniform3f(border_uniform_locations[BORDER_bell_border_color], CV3(OPT(bell_border_color))); } + if (OPT(dynamic_background_opacity)) { + glUniform1f(border_uniform_locations[BORDER_background_opacity], w->background_opacity); + } glUniform2ui(border_uniform_locations[BORDER_viewport], viewport_width, viewport_height); color_type default_bg = num_visible_windows > 1 ? OPT(background) : active_window_bg; glUniform3f(border_uniform_locations[BORDER_default_bg], CV3(default_bg)); diff --git a/kitty/state.c b/kitty/state.c index f8768741f..be00f578c 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -81,6 +81,7 @@ add_os_window() { memset(ans, 0, sizeof(OSWindow)); ans->id = ++global_state.os_window_id_counter; ans->tab_bar_render_data.vao_idx = create_cell_vao(); + ans->background_opacity = OPT(background_opacity); END_WITH_OS_WINDOW_REFS return ans; } @@ -360,6 +361,7 @@ PYWRAP1(set_options) { S(cursor_stop_blinking_after, PyFloat_AsDouble); S(background_opacity, PyFloat_AsDouble); S(dim_opacity, PyFloat_AsDouble); + S(dynamic_background_opacity, PyObject_IsTrue); S(inactive_text_alpha, PyFloat_AsDouble); S(window_padding_width, PyFloat_AsDouble); S(cursor_shape, PyLong_AsLong); @@ -503,6 +505,18 @@ PYWRAP1(mark_tab_bar_dirty) { Py_RETURN_NONE; } +PYWRAP1(change_background_opacity) { + id_type os_window_id; + float opacity; + PA("Kf", &os_window_id, &opacity); + WITH_OS_WINDOW(os_window_id) + os_window->background_opacity = opacity; + os_window->is_damaged = true; + Py_RETURN_TRUE; + END_WITH_OS_WINDOW + Py_RETURN_FALSE; +} + static inline bool fix_window_idx(Tab *tab, id_type window_id, unsigned int *window_idx) { for (id_type fix = 0; fix < tab->num_windows; fix++) { @@ -630,6 +644,7 @@ static PyMethodDef module_methods[] = { MW(mark_os_window_for_close, METH_VARARGS), MW(set_titlebar_color, METH_VARARGS), MW(mark_tab_bar_dirty, METH_O), + MW(change_background_opacity, METH_VARARGS), MW(update_window_visibility, METH_VARARGS), MW(set_boss, METH_O), MW(set_display_state, METH_VARARGS), diff --git a/kitty/state.h b/kitty/state.h index cce0cb217..0f93b6b35 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -27,6 +27,7 @@ typedef struct { int adjust_line_height_px, adjust_column_width_px; float adjust_line_height_frac, adjust_column_width_frac; float background_opacity, dim_opacity; + bool dynamic_background_opacity; float inactive_text_alpha; float window_padding_width; Edge tab_bar_edge; @@ -118,6 +119,7 @@ typedef struct { uint32_t offscreen_texture_id; unsigned int clear_count; color_type last_titlebar_color; + float background_opacity; } OSWindow; @@ -171,7 +173,7 @@ OSWindow* add_os_window(); OSWindow* current_os_window(); void os_window_regions(OSWindow*, Region *main, Region *tab_bar); bool drag_scroll(Window *, OSWindow*); -void draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, uint32_t viewport_width, uint32_t viewport_height, color_type, unsigned int); +void draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, uint32_t viewport_width, uint32_t viewport_height, color_type, unsigned int, OSWindow *w); ssize_t create_cell_vao(); ssize_t create_graphics_vao(); ssize_t create_border_vao();