parent
5c49e2aab4
commit
4c8f9f9e26
12 changed files with 73 additions and 69 deletions
|
|
@ -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]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
38
kitty/glfw.c
38
kitty/glfw.c
|
|
@ -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*
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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='''
|
||||
|
|
|
|||
3
kitty/options/parse.py
generated
3
kitty/options/parse.py
generated
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
30
kitty/options/to-c-generated.h
generated
30
kitty/options/to-c-generated.h
generated
|
|
@ -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);
|
||||
|
|
|
|||
2
kitty/options/types.py
generated
2
kitty/options/types.py
generated
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue