mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-08 22:28:24 +02:00
Panel kitten: Add option to hide panel on focus loss
This commit is contained in:
2
glfw/glfw3.h
vendored
2
glfw/glfw3.h
vendored
@@ -1314,7 +1314,7 @@ typedef struct GLFWLayerShellConfig {
|
||||
unsigned x_size_in_cells, x_size_in_pixels;
|
||||
unsigned y_size_in_cells, y_size_in_pixels;
|
||||
int requested_top_margin, requested_left_margin, requested_bottom_margin, requested_right_margin;
|
||||
int requested_exclusive_zone;
|
||||
int requested_exclusive_zone, hide_on_focus_loss;
|
||||
unsigned override_exclusive_zone;
|
||||
void (*size_callback)(GLFWwindow *window, float xscale, float yscale, unsigned *cell_width, unsigned *cell_height, double *left_edge_spacing, double *top_edge_spacing, double *right_edge_spacing, double *bottom_edge_spacing);
|
||||
struct { float xscale, yscale; } expected;
|
||||
|
||||
@@ -152,6 +152,8 @@ def layer_shell_config(opts: PanelCLIOptions) -> LayerShellConfig:
|
||||
focus_policy = {
|
||||
'not-allowed': GLFW_FOCUS_NOT_ALLOWED, 'exclusive': GLFW_FOCUS_EXCLUSIVE, 'on-demand': GLFW_FOCUS_ON_DEMAND
|
||||
}.get(opts.focus_policy, GLFW_FOCUS_NOT_ALLOWED)
|
||||
if opts.hide_on_focus_loss:
|
||||
focus_policy = GLFW_FOCUS_ON_DEMAND
|
||||
x, y = dual_distance(opts.columns, min_cell_value_if_no_pixels=1), dual_distance(opts.lines, min_cell_value_if_no_pixels=1)
|
||||
return LayerShellConfig(type=ltype,
|
||||
edge=edge,
|
||||
@@ -164,6 +166,7 @@ def layer_shell_config(opts: PanelCLIOptions) -> LayerShellConfig:
|
||||
focus_policy=focus_policy,
|
||||
requested_exclusive_zone=opts.exclusive_zone,
|
||||
override_exclusive_zone=opts.override_exclusive_zone,
|
||||
hide_on_focus_loss=opts.hide_on_focus_loss,
|
||||
output_name=opts.output_name or '')
|
||||
|
||||
|
||||
|
||||
@@ -71,6 +71,9 @@ func main(cmd *cli.Command, opts *Options, args []string) (rc int, err error) {
|
||||
if conf.Start_as_hidden {
|
||||
argv = append(argv, `--start-as-hidden`)
|
||||
}
|
||||
if conf.Hide_on_focus_loss {
|
||||
argv = append(argv, `--hide-on-focus-loss`)
|
||||
}
|
||||
if opts.Detach {
|
||||
argv = append(argv, `--detach`)
|
||||
}
|
||||
|
||||
@@ -40,6 +40,11 @@ option of the same name, it is present here as it has a different
|
||||
default value for the quick access terminal.
|
||||
''')
|
||||
|
||||
opt('hide_on_focus_loss', 'no', option_type='to_bool', long_text='''
|
||||
Hide the window when it loses keyboard focus automatically. Using this option
|
||||
will force :opt:`focus_policy` to :code:`on-demand`.
|
||||
''')
|
||||
|
||||
opt('margin_left', '0', option_type='int',
|
||||
long_text='Set the left margin for the window, in pixels. Has no effect for windows on the right edge of the screen.')
|
||||
|
||||
@@ -67,8 +72,14 @@ opt('app_id', f'{appname}-quick-access',
|
||||
opt('start_as_hidden', 'no', option_type='to_bool',
|
||||
long_text='Whether to start the quick access terminal hidden. Useful if you are starting it as part of system startup.')
|
||||
|
||||
opt('focus_policy', 'exclusive', choices=('exclusive', 'on-demand'),
|
||||
long_text='How to manage window focus.')
|
||||
opt('focus_policy', 'exclusive', choices=('exclusive', 'on-demand'), long_text='''
|
||||
How to manage window focus. A value of :code:`exclusive` means prevent other windows from getting focus.
|
||||
However, whether this works is entirely dependent on the compositor/desktop environment.
|
||||
It does not have any effect on macOS and KDE, for example. Note that on sway using :code:`on-demand` means
|
||||
the compositor will not focus the window when it appears until you click on it, which is why the default is set
|
||||
to :code:`exclusive`.
|
||||
''')
|
||||
|
||||
|
||||
|
||||
def options_spec() -> str:
|
||||
|
||||
@@ -1740,7 +1740,6 @@ def set_redirect_keys_to_overlay(os_window_id: int, tab_id: int, window_id: int,
|
||||
def buffer_keys_in_window(os_window_id: int, tab_id: int, window_id: int, enabled: bool = True) -> bool: ...
|
||||
def sprite_idx_to_pos(idx: int, xnum: int, ynum: int) -> tuple[int, int, int]: ...
|
||||
def render_box_char(ch: int, width: int, height: int, scale: float = 1.0, dpi_x: float = 96.0, dpi_y: float = 96.0) -> bytes: ...
|
||||
def set_os_window_hide_on_focus_lost(os_window_id: int, hide: bool = True) -> bool: ...
|
||||
def run_at_exit_cleanup_functions() -> None: ...
|
||||
DecorationTypes = Literal[
|
||||
'curl', 'dashed', 'dotted', 'double', 'straight', 'strikethrough', 'beam_cursor', 'underline_cursor', 'hollow_cursor', 'missing']
|
||||
|
||||
2
kitty/glfw-wrapper.h
generated
2
kitty/glfw-wrapper.h
generated
@@ -1052,7 +1052,7 @@ typedef struct GLFWLayerShellConfig {
|
||||
unsigned x_size_in_cells, x_size_in_pixels;
|
||||
unsigned y_size_in_cells, y_size_in_pixels;
|
||||
int requested_top_margin, requested_left_margin, requested_bottom_margin, requested_right_margin;
|
||||
int requested_exclusive_zone;
|
||||
int requested_exclusive_zone, hide_on_focus_loss;
|
||||
unsigned override_exclusive_zone;
|
||||
void (*size_callback)(GLFWwindow *window, float xscale, float yscale, unsigned *cell_width, unsigned *cell_height, double *left_edge_spacing, double *top_edge_spacing, double *right_edge_spacing, double *bottom_edge_spacing);
|
||||
struct { float xscale, yscale; } expected;
|
||||
|
||||
29
kitty/glfw.c
29
kitty/glfw.c
@@ -135,6 +135,7 @@ set_layer_shell_config_for(OSWindow *w, GLFWLayerShellConfig *lsc) {
|
||||
lsc->related.background_opacity = w->background_opacity;
|
||||
lsc->related.background_blur = OPT(background_blur);
|
||||
lsc->related.color_space = OPT(macos_colorspace);
|
||||
w->hide_on_focus_loss = lsc->hide_on_focus_loss;
|
||||
}
|
||||
return glfwSetLayerShellConfig(w->handle, lsc);
|
||||
}
|
||||
@@ -561,11 +562,18 @@ set_os_window_visibility(OSWindow *w, int set_visible) {
|
||||
} else glfwHideWindow(w->handle);
|
||||
}
|
||||
|
||||
static void
|
||||
update_os_window_visibility_based_on_focus(id_type timer_id UNUSED, void*d) {
|
||||
OSWindow * osw = os_window_for_id((uintptr_t)d);
|
||||
if (osw && osw->hide_on_focus_loss && !osw->is_focused) set_os_window_visibility(osw, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
window_focus_callback(GLFWwindow *w, int focused) {
|
||||
if (!set_callback_window(w)) return;
|
||||
#define osw global_state.callback_os_window
|
||||
debug_input("\x1b[35mon_focus_change\x1b[m: window id: 0x%llu focused: %d\n", osw->id, focused);
|
||||
bool focus_changed = osw->is_focused != focused;
|
||||
osw->is_focused = focused ? true : false;
|
||||
monotonic_t now = monotonic();
|
||||
id_type wid = osw->id;
|
||||
@@ -591,8 +599,8 @@ window_focus_callback(GLFWwindow *w, int focused) {
|
||||
}
|
||||
}
|
||||
request_tick_callback();
|
||||
if (osw && osw->hide_on_focus_lost && osw->handle) {
|
||||
if (glfwGetWindowAttrib(osw->handle, GLFW_VISIBLE)) set_os_window_visibility(osw, 0);
|
||||
if (osw && osw->handle && !focused && focus_changed && osw->hide_on_focus_loss && glfwGetWindowAttrib(osw->handle, GLFW_VISIBLE)) {
|
||||
add_main_loop_timer(0, false, update_os_window_visibility_based_on_focus, (void*)(uintptr_t)osw->id, NULL);
|
||||
}
|
||||
osw = NULL;
|
||||
#undef osw
|
||||
@@ -1216,6 +1224,7 @@ layer_shell_config_from_python(PyObject *p, GLFWLayerShellConfig *ans) {
|
||||
A(requested_right_margin, PyLong_Check, PyLong_AsLong);
|
||||
A(requested_exclusive_zone, PyLong_Check, PyLong_AsLong);
|
||||
A(override_exclusive_zone, PyBool_Check, PyLong_AsLong);
|
||||
A(hide_on_focus_loss, PyBool_Check, PyLong_AsLong);
|
||||
#undef A
|
||||
#define A(attr) { \
|
||||
RAII_PyObject(attr, PyObject_GetAttrString(p, #attr)); if (attr == NULL) return false; \
|
||||
@@ -1413,7 +1422,10 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
|
||||
OSWindow *w = add_os_window();
|
||||
w->handle = glfw_window;
|
||||
w->disallow_title_changes = disallow_override_title;
|
||||
w->is_layer_shell = lsc != NULL;
|
||||
if (lsc != NULL) {
|
||||
w->is_layer_shell = true;
|
||||
w->hide_on_focus_loss = lsc->hide_on_focus_loss;
|
||||
}
|
||||
update_os_window_references();
|
||||
if (!w->is_layer_shell || (global_state.is_apple && w->is_layer_shell && lsc->focus_policy == GLFW_FOCUS_EXCLUSIVE)) {
|
||||
for (size_t i = 0; i < global_state.num_os_windows; i++) {
|
||||
@@ -2513,23 +2525,12 @@ set_layer_shell_config(PyObject *self UNUSED, PyObject *args) {
|
||||
return Py_NewRef(set_layer_shell_config_for(window, &lsc) ? Py_True : Py_False);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
set_os_window_hide_on_focus_lost(PyObject *self UNUSED, PyObject *args) {
|
||||
unsigned long long wid; int val = 1;
|
||||
if (!PyArg_ParseTuple(args, "K|p", &wid, &val)) return NULL;
|
||||
OSWindow *window = os_window_for_id(wid);
|
||||
if (!window) Py_RETURN_FALSE;
|
||||
window->hide_on_focus_lost = val;
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
|
||||
|
||||
// Boilerplate {{{
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
METHODB(set_custom_cursor, METH_VARARGS),
|
||||
METHODB(is_css_pointer_name_valid, METH_O),
|
||||
METHODB(set_os_window_hide_on_focus_lost, METH_O),
|
||||
METHODB(toggle_os_window_visibility, METH_VARARGS),
|
||||
METHODB(layer_shell_config_for_os_window, METH_O),
|
||||
METHODB(set_layer_shell_config, METH_VARARGS),
|
||||
|
||||
@@ -654,9 +654,17 @@ choices=not-allowed,exclusive,on-demand
|
||||
default={focus_policy}
|
||||
On a Wayland compositor that supports the wlr layer shell protocol, specify the focus policy for keyboard
|
||||
interactivity with the panel. Please refer to the wlr layer shell protocol documentation for more details.
|
||||
Note that different Wayland compositors behave very differently with :code:`exclusive`, your mileage may vary.
|
||||
On macOS, :code:`exclusive` and :code:`on-demand` are currently the same. Ignored on X11.
|
||||
|
||||
|
||||
--hide-on-focus-loss
|
||||
type=bool-set
|
||||
Automatically hide the panel window when it loses focus. Using this option will force :option:`--focus-policy`
|
||||
to :code:`on-demand`. Note that on Wayland, depending on the compositor, this can result in the window never
|
||||
becoming visible.
|
||||
|
||||
|
||||
--exclusive-zone
|
||||
type=int
|
||||
default={exclusive_zone}
|
||||
|
||||
@@ -313,8 +313,7 @@ typedef struct {
|
||||
uint64_t render_calls;
|
||||
id_type last_focused_counter;
|
||||
CloseRequest close_request;
|
||||
bool is_layer_shell;
|
||||
bool hide_on_focus_lost;
|
||||
bool is_layer_shell, hide_on_focus_loss;
|
||||
} OSWindow;
|
||||
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ class LayerShellConfig(NamedTuple):
|
||||
requested_right_margin: int = 0
|
||||
requested_exclusive_zone: int = -1
|
||||
override_exclusive_zone: bool = False
|
||||
hide_on_focus_loss: bool = False
|
||||
|
||||
|
||||
def mod_to_names(mods: int, has_kitty_mod: bool = False, kitty_mod: int = 0) -> Iterator[str]:
|
||||
|
||||
Reference in New Issue
Block a user