Cleanup previous PR

Fixes #8601
This commit is contained in:
Kovid Goyal 2025-05-12 22:17:33 +05:30
parent 5c49e2aab4
commit 4c8f9f9e26
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
12 changed files with 73 additions and 69 deletions

View file

@ -117,6 +117,9 @@ Detailed list of changes
- Quick access terminal: Allow toggling the window to full screen and map using the standard kitty :sc:`toggle_fullscreen` shortcut (:iss:`8626`)
- A new setting :opt:`remember_window_position` to optionally use the position of the last closed kitty OS Window as the position of the first kitty OS Window when running a new kitty instance (:pull:`8601`)
0.42.0 [2025-05-11]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -85,8 +85,6 @@
get_boss,
get_options,
get_os_window_size,
get_os_window_pos,
glfw_primary_monitor_size,
glfw_get_monitor_workarea,
global_font_size,
is_layer_shell_supported,
@ -1845,9 +1843,14 @@ def handle_close_os_window_confirmation(self, confirmed: bool, os_window_id: int
else:
self.mark_os_window_for_close(os_window_id, NO_CLOSE_REQUESTED)
def on_os_window_closed(self, os_window_id: int, viewport_width: int, viewport_height: int) -> None:
self.cached_values['window-size'] = viewport_width, viewport_height
def on_os_window_closed(self, os_window_id: int, x: int, y: int, viewport_width: int, viewport_height: int, is_layer_shell: bool) -> None:
tm = self.os_window_map.pop(os_window_id, None)
opts = get_options()
if not is_layer_shell:
if opts.remember_window_position and not is_wayland() and not self.os_window_map:
self.cached_values['window-pos'] = x, y
self.cached_values['monitor-workarea'] = glfw_get_monitor_workarea()
self.cached_values['window-size'] = viewport_width, viewport_height
if tm is not None:
tm.destroy()
for window_id in tuple(w.id for w in self.window_id_map.values() if getattr(w, 'os_window_id', None) == os_window_id):
@ -1867,10 +1870,6 @@ def quit(self, *args: Any) -> None:
for qt in q:
windows += list(qt)
active_window = self.active_window
x, y = get_os_window_pos(active_window.id)
monitorGeometryX, monitorGeometryY = glfw_primary_monitor_size()
self.cached_values['window-pos'] = x, y
self.cached_values['monitor-workarea'] = glfw_get_monitor_workarea()
msg, num_active_windows = self.close_windows_with_confirmation_msg(windows, active_window)
x = get_options().confirm_os_window_close[0]
num = num_active_windows if x < 0 else len(windows)

View file

@ -1114,8 +1114,11 @@ close_os_window(ChildMonitor *self, OSWindow *os_window) {
if (os_window->before_fullscreen.is_set && is_os_window_fullscreen(os_window)) {
w = os_window->before_fullscreen.w; h = os_window->before_fullscreen.h;
}
int x = 0, y = 0;
if (os_window->handle && !global_state.is_wayland) glfwGetWindowPos(os_window->handle, &x, &y);
bool is_layer_shell = os_window->is_layer_shell;
destroy_os_window(os_window);
call_boss(on_os_window_closed, "Kii", os_window->id, w, h);
call_boss(on_os_window_closed, "KiiiiO", os_window->id, x, y, w, h, is_layer_shell ? Py_True : Py_False);
for (size_t t=0; t < os_window->num_tabs; t++) {
Tab *tab = os_window->tabs + t;
for (size_t w = 0; w < tab->num_windows; w++) mark_child_for_close(self, tab->windows[w].id);

View file

@ -602,7 +602,6 @@ def apply_preparsed_cli_flags(preparsed_from_c: PreparsedCLIFlags, ans: Any, cre
def parse_cmdline(oc: Options, disabled: OptionSpecSeq, ans: Any, args: list[str] | None = None) -> list[str]:
names_map = oc.names_map.copy()
values_map = oc.values_map.copy()
if 'help' not in names_map:

View file

@ -645,10 +645,10 @@ def get_options() -> Options:
pass
def glfw_primary_monitor_size() -> Tuple[int, int]:
def glfw_primary_monitor_size() -> tuple[int, int]:
pass
def glfw_get_monitor_workarea() -> [Tuple[int, int, int, int]]:
def glfw_get_monitor_workarea() -> tuple[tuple[int, int, int, int], ...]:
pass

View file

@ -2127,40 +2127,20 @@ primary_monitor_size(PYNOARG) {
return Py_BuildValue("ii", mode->width, mode->height);
}
static PyObject *
get_monitor_workarea(PYNOARG)
{
static PyObject*
get_monitor_workarea(PYNOARG) {
int count = 0;
GLFWmonitor **monitors = glfwGetMonitors(&count);
if (count == 0)
{
PyErr_SetString(PyExc_ValueError, "Failed to retrieve monitors");
}
PyObject *result = PyList_New(count);
if (!result)
{
return NULL;
}
for (int i = 0; i < count; i++)
{
if (count <= 0 || !monitors) return PyTuple_New(0);
RAII_PyObject(result, PyTuple_New(count)); if (!result) return NULL;
for (int i = 0; i < count; i++) {
int xpos, ypos, width, height;
GLFWmonitor *monitor = monitors[i];
glfwGetMonitorWorkarea(monitor, &xpos, &ypos, &width, &height);
glfwGetMonitorWorkarea(monitors[i], &xpos, &ypos, &width, &height);
PyObject *monitor_workarea = Py_BuildValue("iiii", xpos, ypos, width, height);
if (!monitor_workarea)
{
Py_DECREF(monitor_workarea);
return NULL;
}
PyList_SET_ITEM(result, i, monitor_workarea);
if (!monitor_workarea) return NULL;
PyTuple_SET_ITEM(result, i, monitor_workarea);
}
return result;
return Py_NewRef(result);
}
static PyObject*

View file

@ -38,10 +38,9 @@
SingleKey,
create_os_window,
free_font_data,
glfw_get_monitor_workarea,
glfw_init,
glfw_terminate,
glfw_primary_monitor_size,
glfw_get_monitor_workarea,
is_layer_shell_supported,
load_png_data,
mask_kitty_signals_process_wide,
@ -234,8 +233,15 @@ def _run_app(opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine] = (),
set_window_icon()
if _is_panel_kitten and not is_layer_shell_supported():
raise SystemExit('Cannot create panels as the window manager/compositor does not support the neccessary protocols')
pos_x, pos_y = None, None
with cached_values_for(run_app.cached_values_name) as cached_values:
if not _is_panel_kitten and not is_wayland():
if opts.remember_window_position:
cached_workarea = cached_values.get('monitor-workarea', ())
if cached_workarea and glfw_get_monitor_workarea() == tuple(cached_workarea):
pos_x, pos_y = cached_values.get('window-pos', (None, None))
if args.position:
pos_x, pos_y = map(int, args.position.lower().partition('x')[::2])
startup_sessions = tuple(create_sessions(opts, args, default_session=opts.startup_session))
wincls = (startup_sessions[0].os_window_class if startup_sessions else '') or args.cls or appname
winname = (startup_sessions[0].os_window_name if startup_sessions else '') or args.name or wincls or appname
@ -243,19 +249,13 @@ def _run_app(opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine] = (),
getattr(startup_sessions[0], 'os_window_state', None) if startup_sessions else None
)
wstate = parse_os_window_state(window_state) if window_state is not None else None
posX, posY = None, None
if args.position:
monitor_workarea = glfw_get_monitor_workarea()
cached_workarea = cached_values.get('monitor-workarea', None)
if cached_workarea and len(monitor_workarea) == len(cached_workarea):
if all([tuple(cached_workarea[i]) == monitor_workarea[i] for i in range(len(cached_workarea))]):
posX, posY = cached_values.get('window-pos', (None, None))
with startup_notification_handler(extra_callback=run_app.first_window_callback) as pre_show_callback:
window_id = create_os_window(
run_app.initial_window_size_func(get_os_window_sizing_data(opts, startup_sessions[0] if startup_sessions else None), cached_values),
pre_show_callback,
args.title or appname, winname,
wincls, wstate, load_all_shaders, disallow_override_title=bool(args.title), layer_shell_config=run_app.layer_shell_config, x=posX, y=posY)
wincls, wstate, load_all_shaders, disallow_override_title=bool(args.title), layer_shell_config=run_app.layer_shell_config, x=pos_x, y=pos_y)
boss = Boss(opts, args, cached_values, global_shortcuts, talk_fd)
boss.start(window_id, startup_sessions)
if args.debug_font_fallback:

View file

@ -1079,6 +1079,18 @@
option_type='window_size',
)
opt('remember_window_position', 'no',
option_type='to_bool',
long_text='''
If enabled, the :term:`OS Window <os_window>` position will be remembered so that
new instances of kitty will have the same position as the previous instance.
If disabled, the :term:`OS Window <os_window>` will be placed by the window manager.
Note that remembering of position only works if the underlying desktop environment/window
manager supports it. It never works on Wayland. See also :option:`kitty --position` to
specify the position when launching kitty.
'''
)
opt('enabled_layouts', '*',
option_type='to_layout_names',
long_text='''

View file

@ -1171,6 +1171,9 @@ def pointer_shape_when_grabbed(self, val: str, ans: dict[str, typing.Any]) -> No
choices_for_pointer_shape_when_grabbed = choices_for_default_pointer_shape
def remember_window_position(self, val: str, ans: dict[str, typing.Any]) -> None:
ans['remember_window_position'] = to_bool(val)
def remember_window_size(self, val: str, ans: dict[str, typing.Any]) -> None:
ans['remember_window_size'] = to_bool(val)

View file

@ -889,6 +889,19 @@ convert_from_opts_background_blur(PyObject *py_opts, Options *opts) {
Py_DECREF(ret);
}
static void
convert_from_python_dynamic_background_opacity(PyObject *val, Options *opts) {
opts->dynamic_background_opacity = PyObject_IsTrue(val);
}
static void
convert_from_opts_dynamic_background_opacity(PyObject *py_opts, Options *opts) {
PyObject *ret = PyObject_GetAttrString(py_opts, "dynamic_background_opacity");
if (ret == NULL) return;
convert_from_python_dynamic_background_opacity(ret, opts);
Py_DECREF(ret);
}
static void
convert_from_python_background_image(PyObject *val, Options *opts) {
background_image(val, opts);
@ -928,19 +941,6 @@ convert_from_opts_background_image_linear(PyObject *py_opts, Options *opts) {
Py_DECREF(ret);
}
static void
convert_from_python_dynamic_background_opacity(PyObject *val, Options *opts) {
opts->dynamic_background_opacity = PyObject_IsTrue(val);
}
static void
convert_from_opts_dynamic_background_opacity(PyObject *py_opts, Options *opts) {
PyObject *ret = PyObject_GetAttrString(py_opts, "dynamic_background_opacity");
if (ret == NULL) return;
convert_from_python_dynamic_background_opacity(ret, opts);
Py_DECREF(ret);
}
static void
convert_from_python_background_tint(PyObject *val, Options *opts) {
opts->background_tint = PyFloat_AsFloat(val);
@ -1313,14 +1313,14 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) {
if (PyErr_Occurred()) return false;
convert_from_opts_background_blur(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_dynamic_background_opacity(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_background_image(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_background_image_layout(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_background_image_linear(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_dynamic_background_opacity(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_background_tint(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_background_tint_gaps(py_opts, opts);

View file

@ -403,6 +403,7 @@
'placement_strategy',
'pointer_shape_when_dragging',
'pointer_shape_when_grabbed',
'remember_window_position',
'remember_window_size',
'remote_control_password',
'repaint_delay',
@ -575,6 +576,7 @@ class Options:
placement_strategy: choices_for_placement_strategy = 'center'
pointer_shape_when_dragging: tuple[str, str] = ('beam', 'crosshair')
pointer_shape_when_grabbed: choices_for_pointer_shape_when_grabbed = 'arrow'
remember_window_position: bool = False
remember_window_size: bool = True
repaint_delay: int = 10
resize_debounce_time: tuple[float, float] = (0.1, 0.5)

View file

@ -477,8 +477,11 @@ def kitty_options_spec() -> str:
--position
type=bool-set
Restores the last session's window position. Does not work on Wayland.
The position, for example 10x20, on screen at which to place the first kitty OS Window.
This may or may not work depending on the policies of the desktop
environment/window manager. It never works on Wayland.
See also :opt:`remember_window_position` to have kitty automatically try
to restore the previous window position.
# Debugging options