mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-17 14:07:44 +02:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d826c37b9 | ||
|
|
e3fa7fc60e | ||
|
|
77d2159d3f | ||
|
|
da753c3f5a | ||
|
|
d8889a2f08 | ||
|
|
14ff29426d | ||
|
|
a93a6af025 | ||
|
|
d5398f84d8 | ||
|
|
08d90db4c4 |
@@ -93,6 +93,7 @@ from .fast_data_types import (
|
|||||||
os_window_focus_counters,
|
os_window_focus_counters,
|
||||||
os_window_font_size,
|
os_window_font_size,
|
||||||
redirect_mouse_handling,
|
redirect_mouse_handling,
|
||||||
|
remove_floating_window,
|
||||||
ring_bell,
|
ring_bell,
|
||||||
run_with_activation_token,
|
run_with_activation_token,
|
||||||
safe_pipe,
|
safe_pipe,
|
||||||
@@ -360,6 +361,7 @@ class Boss:
|
|||||||
self.clipboard = Clipboard()
|
self.clipboard = Clipboard()
|
||||||
self.window_for_dispatch: Window | None = None
|
self.window_for_dispatch: Window | None = None
|
||||||
self.primary_selection = Clipboard(ClipboardType.primary_selection)
|
self.primary_selection = Clipboard(ClipboardType.primary_selection)
|
||||||
|
self.window_floats_map: dict[int, list[int]] = {}
|
||||||
self.update_check_started = False
|
self.update_check_started = False
|
||||||
self.peer_data_map: dict[int, dict[str, Sequence[str]] | None] = {}
|
self.peer_data_map: dict[int, dict[str, Sequence[str]] | None] = {}
|
||||||
self.background_process_death_notify_map: dict[int, Callable[[int, Exception | None], None]] = {}
|
self.background_process_death_notify_map: dict[int, Callable[[int, Exception | None], None]] = {}
|
||||||
@@ -907,6 +909,11 @@ class Boss:
|
|||||||
for w, val in changes.items():
|
for w, val in changes.items():
|
||||||
w.ignore_focus_changes = val
|
w.ignore_focus_changes = val
|
||||||
|
|
||||||
|
def descendant_window_ids(self, parent_id: int) -> Iterator[int]:
|
||||||
|
for cid in self.window_floats_map.get(parent_id, ()):
|
||||||
|
yield cid
|
||||||
|
yield from self.descendant_window_ids(cid)
|
||||||
|
|
||||||
def on_child_death(self, window_id: int) -> None:
|
def on_child_death(self, window_id: int) -> None:
|
||||||
prev_active_window = self.active_window
|
prev_active_window = self.active_window
|
||||||
window = self.window_id_map.pop(window_id, None)
|
window = self.window_id_map.pop(window_id, None)
|
||||||
@@ -921,16 +928,25 @@ class Boss:
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
os_window_id = window.os_window_id
|
os_window_id = window.os_window_id
|
||||||
window.destroy()
|
window.destroy()
|
||||||
tm = self.os_window_map.get(os_window_id)
|
for cid in tuple(self.descendant_window_ids(window.id)):
|
||||||
tab = None
|
self.window_floats_map.pop(cid, None)
|
||||||
if tm is not None:
|
cw = self.window_id_map.pop(cid, None)
|
||||||
for q in tm:
|
if cw is not None:
|
||||||
if window in q:
|
self.child_monitor.mark_for_close(cw.id)
|
||||||
tab = q
|
self.window_floats_map.pop(window.id, None)
|
||||||
break
|
if window.floating_in:
|
||||||
if tab is not None:
|
remove_floating_window(window.os_window_id, window.tab_id, window.floating_in, window.id)
|
||||||
tab.remove_window(window)
|
else:
|
||||||
self._cleanup_tab_after_window_removal(tab)
|
tm = self.os_window_map.get(os_window_id)
|
||||||
|
tab = None
|
||||||
|
if tm is not None:
|
||||||
|
for q in tm:
|
||||||
|
if window in q:
|
||||||
|
tab = q
|
||||||
|
break
|
||||||
|
if tab is not None:
|
||||||
|
tab.remove_window(window)
|
||||||
|
self._cleanup_tab_after_window_removal(tab)
|
||||||
for removal_action in window.actions_on_removal:
|
for removal_action in window.actions_on_removal:
|
||||||
try:
|
try:
|
||||||
removal_action(window)
|
removal_action(window)
|
||||||
@@ -1801,11 +1817,15 @@ class Boss:
|
|||||||
|
|
||||||
def on_os_window_closed(self, os_window_id: int, viewport_width: int, viewport_height: int) -> None:
|
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
|
self.cached_values['window-size'] = viewport_width, viewport_height
|
||||||
|
windows_in_os_window = {w.id: w for w in self.window_id_map.values() if w.os_window_id == os_window_id}
|
||||||
tm = self.os_window_map.pop(os_window_id, None)
|
tm = self.os_window_map.pop(os_window_id, None)
|
||||||
if tm is not None:
|
if tm is not None:
|
||||||
tm.destroy()
|
tm.destroy() # this will call destroy on all windows
|
||||||
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):
|
for window_id, w in windows_in_os_window.items():
|
||||||
|
self.child_monitor.mark_for_close(window_id)
|
||||||
self.window_id_map.pop(window_id, None)
|
self.window_id_map.pop(window_id, None)
|
||||||
|
self.window_floats_map.pop(window_id, None)
|
||||||
|
w.destroy() # in case tm was None
|
||||||
if not self.os_window_map and is_macos:
|
if not self.os_window_map and is_macos:
|
||||||
cocoa_set_menubar_title('')
|
cocoa_set_menubar_title('')
|
||||||
action = self.os_window_death_actions.pop(os_window_id, None)
|
action = self.os_window_death_actions.pop(os_window_id, None)
|
||||||
|
|||||||
@@ -565,7 +565,6 @@ mark_child_for_close(ChildMonitor *self, id_type window_id) {
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
mark_for_close(ChildMonitor *self, PyObject *args) {
|
mark_for_close(ChildMonitor *self, PyObject *args) {
|
||||||
#define mark_for_close_doc "Mark a child to be removed from the child monitor"
|
#define mark_for_close_doc "Mark a child to be removed from the child monitor"
|
||||||
@@ -705,8 +704,63 @@ change_menubar_title(PyObject *title UNUSED) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define WD w->render_data
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
prepare_to_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, bool scan_for_animated_images) {
|
prepare_to_render_window(OSWindow *os_window, Tab *tab, Window *w, bool is_active_non_floating_window, monotonic_t now, bool scan_for_animated_images, id_type *active_window_id) {
|
||||||
|
bool needs_render = false;
|
||||||
|
if (w->last_drag_scroll_at > 0) {
|
||||||
|
if (now - w->last_drag_scroll_at >= ms_to_monotonic_t(20ll)) {
|
||||||
|
if (drag_scroll(w, os_window)) {
|
||||||
|
w->last_drag_scroll_at = now;
|
||||||
|
set_maximum_wait(ms_to_monotonic_t(20ll));
|
||||||
|
needs_render = true;
|
||||||
|
} else w->last_drag_scroll_at = 0;
|
||||||
|
} else set_maximum_wait(now - w->last_drag_scroll_at);
|
||||||
|
}
|
||||||
|
const bool is_floating = w->floating.is_floating;
|
||||||
|
const bool is_active = is_active_non_floating_window && ((is_floating && w->floating.is_key) || (!is_floating && !w->floating.child_count));
|
||||||
|
if (is_active) {
|
||||||
|
*active_window_id = w->id;
|
||||||
|
if (collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window)) needs_render = true;
|
||||||
|
WD.screen->cursor_render_info.is_focused = os_window->is_focused;
|
||||||
|
set_os_window_title_from_window(w, os_window);
|
||||||
|
if (OPT(cursor_trail)) {
|
||||||
|
if (update_cursor_trail(&tab->cursor_trail, w, now, os_window)) {
|
||||||
|
needs_render = true;
|
||||||
|
// A max wait of zero causes key input processing to be
|
||||||
|
// slow so handle the case of OPT(repaint_delay) == 0, see https://github.com/kovidgoyal/kitty/pull/8066
|
||||||
|
set_maximum_wait(MAX(OPT(repaint_delay), ms_to_monotonic_t(1ll)));
|
||||||
|
} else if (OPT(cursor_trail) > now - WD.screen->cursor->position_changed_by_client_at) {
|
||||||
|
// If update_cursor_trail failed due to time threshold, the trail animation
|
||||||
|
// should be evaluated again shortly. Schedule next update when enough time
|
||||||
|
// has passed since the cursor was last moved.
|
||||||
|
set_maximum_wait(OPT(cursor_trail) - now + WD.screen->cursor->position_changed_by_client_at);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (WD.screen->cursor_render_info.render_even_when_unfocused) {
|
||||||
|
if (collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window)) needs_render = true;
|
||||||
|
WD.screen->cursor_render_info.is_focused = false;
|
||||||
|
} else {
|
||||||
|
WD.screen->cursor_render_info.opacity = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (scan_for_animated_images) {
|
||||||
|
monotonic_t min_gap;
|
||||||
|
if (scan_active_animations(WD.screen->grman, now, &min_gap, true)) needs_render = true;
|
||||||
|
if (min_gap < MONOTONIC_T_MAX) {
|
||||||
|
global_state.check_for_active_animated_images = true;
|
||||||
|
set_maximum_wait(min_gap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (send_cell_data_to_gpu(WD.vao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, os_window)) needs_render = true;
|
||||||
|
if (WD.screen->start_visual_bell_at != 0) needs_render = true;
|
||||||
|
return needs_render;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, id_type *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows, bool *all_windows_have_same_bg, bool scan_for_animated_images) {
|
||||||
#define TD os_window->tab_bar_render_data
|
#define TD os_window->tab_bar_render_data
|
||||||
bool needs_render = os_window->needs_render;
|
bool needs_render = os_window->needs_render;
|
||||||
os_window->needs_render = false;
|
os_window->needs_render = false;
|
||||||
@@ -728,61 +782,19 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *
|
|||||||
color_type first_window_bg = 0;
|
color_type first_window_bg = 0;
|
||||||
for (unsigned int i = 0; i < tab->num_windows; i++) {
|
for (unsigned int i = 0; i < tab->num_windows; i++) {
|
||||||
Window *w = tab->windows + i;
|
Window *w = tab->windows + i;
|
||||||
#define WD w->render_data
|
|
||||||
if (w->visible && WD.screen) {
|
if (w->visible && WD.screen) {
|
||||||
screen_check_pause_rendering(WD.screen, now);
|
screen_check_pause_rendering(WD.screen, now);
|
||||||
*num_visible_windows += 1;
|
*num_visible_windows += 1;
|
||||||
color_type window_bg = colorprofile_to_color(WD.screen->color_profile, WD.screen->color_profile->overridden.default_bg, WD.screen->color_profile->configured.default_bg).rgb;
|
color_type window_bg = colorprofile_to_color(WD.screen->color_profile, WD.screen->color_profile->overridden.default_bg, WD.screen->color_profile->configured.default_bg).rgb;
|
||||||
|
const bool is_active_non_floating_window = i == tab->active_window;
|
||||||
if (*num_visible_windows == 1) first_window_bg = window_bg;
|
if (*num_visible_windows == 1) first_window_bg = window_bg;
|
||||||
if (first_window_bg != window_bg) *all_windows_have_same_bg = false;
|
if (first_window_bg != window_bg) *all_windows_have_same_bg = false;
|
||||||
if (w->last_drag_scroll_at > 0) {
|
if (is_active_non_floating_window) *active_window_bg = window_bg;
|
||||||
if (now - w->last_drag_scroll_at >= ms_to_monotonic_t(20ll)) {
|
needs_render |= prepare_to_render_window(os_window, tab, w, is_active_non_floating_window, now, scan_for_animated_images, active_window_id);
|
||||||
if (drag_scroll(w, os_window)) {
|
for (size_t i = 0; i < w->floating.child_count; i++) {
|
||||||
w->last_drag_scroll_at = now;
|
Window *cw = w->floating.children + i;
|
||||||
set_maximum_wait(ms_to_monotonic_t(20ll));
|
if (cw->render_data.screen) needs_render |= prepare_to_render_window(os_window, tab, cw, is_active_non_floating_window, now, scan_for_animated_images, active_window_id);
|
||||||
needs_render = true;
|
|
||||||
} else w->last_drag_scroll_at = 0;
|
|
||||||
} else set_maximum_wait(now - w->last_drag_scroll_at);
|
|
||||||
}
|
}
|
||||||
bool is_active_window = i == tab->active_window;
|
|
||||||
if (is_active_window) {
|
|
||||||
*active_window_id = w->id;
|
|
||||||
if (collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window)) needs_render = true;
|
|
||||||
WD.screen->cursor_render_info.is_focused = os_window->is_focused;
|
|
||||||
set_os_window_title_from_window(w, os_window);
|
|
||||||
*active_window_bg = window_bg;
|
|
||||||
if (OPT(cursor_trail)) {
|
|
||||||
if (update_cursor_trail(&tab->cursor_trail, w, now, os_window)) {
|
|
||||||
needs_render = true;
|
|
||||||
// A max wait of zero causes key input processing to be
|
|
||||||
// slow so handle the case of OPT(repaint_delay) == 0, see https://github.com/kovidgoyal/kitty/pull/8066
|
|
||||||
set_maximum_wait(MAX(OPT(repaint_delay), ms_to_monotonic_t(1ll)));
|
|
||||||
} else if (OPT(cursor_trail) > now - WD.screen->cursor->position_changed_by_client_at) {
|
|
||||||
// If update_cursor_trail failed due to time threshold, the trail animation
|
|
||||||
// should be evaluated again shortly. Schedule next update when enough time
|
|
||||||
// has passed since the cursor was last moved.
|
|
||||||
set_maximum_wait(OPT(cursor_trail) - now + WD.screen->cursor->position_changed_by_client_at);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (WD.screen->cursor_render_info.render_even_when_unfocused) {
|
|
||||||
if (collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window)) needs_render = true;
|
|
||||||
WD.screen->cursor_render_info.is_focused = false;
|
|
||||||
} else {
|
|
||||||
WD.screen->cursor_render_info.opacity = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (scan_for_animated_images) {
|
|
||||||
monotonic_t min_gap;
|
|
||||||
if (scan_active_animations(WD.screen->grman, now, &min_gap, true)) needs_render = true;
|
|
||||||
if (min_gap < MONOTONIC_T_MAX) {
|
|
||||||
global_state.check_for_active_animated_images = true;
|
|
||||||
set_maximum_wait(min_gap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (send_cell_data_to_gpu(WD.vao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, os_window)) needs_render = true;
|
|
||||||
if (WD.screen->start_visual_bell_at != 0) needs_render = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return needs_render;
|
return needs_render;
|
||||||
@@ -802,6 +814,16 @@ draw_resizing_text(OSWindow *w) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
draw_window(OSWindow *os_window, Window *w, bool is_active_non_floating_window, unsigned num_of_visible_windows) {
|
||||||
|
const bool is_floating = w->floating.is_floating, is_active_window = (
|
||||||
|
is_active_non_floating_window && ((!is_floating && !w->floating.child_count) || (is_floating && w->floating.is_key)));
|
||||||
|
draw_cells(WD.vao_idx, &WD, os_window, is_active_window, false, num_of_visible_windows == 1, w);
|
||||||
|
if (WD.screen->start_visual_bell_at != 0) set_maximum_wait(ANIMATION_SAMPLE_WAIT);
|
||||||
|
w->cursor_opacity_at_last_render = WD.screen->cursor_render_info.opacity; w->last_cursor_shape = WD.screen->cursor_render_info.shape;
|
||||||
|
return is_active_window;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
render_prepared_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) {
|
render_prepared_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
|
// ensure all pixels are cleared to background color at least once in every buffer
|
||||||
@@ -811,17 +833,17 @@ render_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, co
|
|||||||
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, all_windows_have_same_bg, os_window);
|
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, all_windows_have_same_bg, os_window);
|
||||||
br->is_dirty = false;
|
br->is_dirty = false;
|
||||||
if (TD.screen && os_window->num_tabs >= OPT(tab_bar_min_tabs)) draw_cells(TD.vao_idx, &TD, os_window, true, true, false, NULL);
|
if (TD.screen && os_window->num_tabs >= OPT(tab_bar_min_tabs)) draw_cells(TD.vao_idx, &TD, os_window, true, true, false, NULL);
|
||||||
unsigned int num_of_visible_windows = 0;
|
unsigned num_of_visible_windows = 0;
|
||||||
Window *active_window = NULL;
|
Window *active_window = NULL;
|
||||||
for (unsigned int i = 0; i < tab->num_windows; i++) { if (tab->windows[i].visible) num_of_visible_windows++; }
|
for (unsigned int i = 0; i < tab->num_windows; i++) { if (tab->windows[i].visible) num_of_visible_windows++; }
|
||||||
for (unsigned int i = 0; i < tab->num_windows; i++) {
|
for (unsigned int i = 0; i < tab->num_windows; i++) {
|
||||||
Window *w = tab->windows + i;
|
Window *w = tab->windows + i;
|
||||||
if (w->visible && WD.screen) {
|
if (w->visible && WD.screen) {
|
||||||
bool is_active_window = i == tab->active_window;
|
if (draw_window(os_window, w, i == tab->active_window, num_of_visible_windows)) active_window = w;
|
||||||
if (is_active_window) active_window = w;
|
for (size_t f = 0; f < w->floating.child_count; f++) {
|
||||||
draw_cells(WD.vao_idx, &WD, os_window, is_active_window, false, num_of_visible_windows == 1, w);
|
Window *cw = w->floating.children + f;
|
||||||
if (WD.screen->start_visual_bell_at != 0) set_maximum_wait(ANIMATION_SAMPLE_WAIT);
|
if (cw->render_data.screen && draw_window(os_window, cw, i == tab->active_window, num_of_visible_windows)) active_window = cw;
|
||||||
w->cursor_opacity_at_last_render = WD.screen->cursor_render_info.opacity; w->last_cursor_shape = WD.screen->cursor_render_info.shape;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (OPT(cursor_trail) && tab->cursor_trail.needs_render) draw_cursor_trail(&tab->cursor_trail, active_window);
|
if (OPT(cursor_trail) && tab->cursor_trail.needs_render) draw_cursor_trail(&tab->cursor_trail, active_window);
|
||||||
@@ -874,7 +896,7 @@ render_os_window(OSWindow *w, monotonic_t now, bool ignore_render_frames, bool s
|
|||||||
w->viewport_size_dirty = false;
|
w->viewport_size_dirty = false;
|
||||||
needs_render = true;
|
needs_render = true;
|
||||||
}
|
}
|
||||||
unsigned int active_window_id = 0, num_visible_windows = 0;
|
id_type active_window_id = 0; unsigned num_visible_windows = 0;
|
||||||
bool all_windows_have_same_bg;
|
bool all_windows_have_same_bg;
|
||||||
color_type active_window_bg = 0;
|
color_type active_window_bg = 0;
|
||||||
if (!w->fonts_data) { log_error("No fonts data found for window id: %llu", w->id); return false; }
|
if (!w->fonts_data) { log_error("No fonts data found for window id: %llu", w->id); return false; }
|
||||||
@@ -1102,22 +1124,18 @@ process_pending_resizes(monotonic_t now) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
close_os_window(ChildMonitor *self, OSWindow *os_window) {
|
close_os_window(OSWindow *os_window) {
|
||||||
int w = os_window->window_width, h = os_window->window_height;
|
int w = os_window->window_width, h = os_window->window_height;
|
||||||
if (os_window->before_fullscreen.is_set && is_os_window_fullscreen(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;
|
w = os_window->before_fullscreen.w; h = os_window->before_fullscreen.h;
|
||||||
}
|
}
|
||||||
destroy_os_window(os_window);
|
destroy_os_window(os_window);
|
||||||
call_boss(on_os_window_closed, "Kii", os_window->id, w, h);
|
call_boss(on_os_window_closed, "Kii", os_window->id, w, h);
|
||||||
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);
|
|
||||||
}
|
|
||||||
remove_os_window(os_window->id);
|
remove_os_window(os_window->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
process_pending_closes(ChildMonitor *self) {
|
process_pending_closes(void) {
|
||||||
if (global_state.quit_request == CONFIRMABLE_CLOSE_REQUESTED) {
|
if (global_state.quit_request == CONFIRMABLE_CLOSE_REQUESTED) {
|
||||||
call_boss(quit, "");
|
call_boss(quit, "");
|
||||||
}
|
}
|
||||||
@@ -1135,14 +1153,14 @@ process_pending_closes(ChildMonitor *self) {
|
|||||||
os_window->close_request = CLOSE_BEING_CONFIRMED;
|
os_window->close_request = CLOSE_BEING_CONFIRMED;
|
||||||
call_boss(confirm_os_window_close, "K", os_window->id);
|
call_boss(confirm_os_window_close, "K", os_window->id);
|
||||||
if (os_window->close_request == IMPERATIVE_CLOSE_REQUESTED) {
|
if (os_window->close_request == IMPERATIVE_CLOSE_REQUESTED) {
|
||||||
close_os_window(self, os_window);
|
close_os_window(os_window);
|
||||||
} else has_open_windows = true;
|
} else has_open_windows = true;
|
||||||
break;
|
break;
|
||||||
case CLOSE_BEING_CONFIRMED:
|
case CLOSE_BEING_CONFIRMED:
|
||||||
has_open_windows = true;
|
has_open_windows = true;
|
||||||
break;
|
break;
|
||||||
case IMPERATIVE_CLOSE_REQUESTED:
|
case IMPERATIVE_CLOSE_REQUESTED:
|
||||||
close_os_window(self, os_window);
|
close_os_window(os_window);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1297,7 +1315,7 @@ process_global_state(void *data) {
|
|||||||
#endif
|
#endif
|
||||||
report_reaped_pids();
|
report_reaped_pids();
|
||||||
bool should_quit = false;
|
bool should_quit = false;
|
||||||
if (global_state.has_pending_closes) should_quit = process_pending_closes(self);
|
if (global_state.has_pending_closes) should_quit = process_pending_closes();
|
||||||
if (should_quit) {
|
if (should_quit) {
|
||||||
stop_main_loop();
|
stop_main_loop();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1740,6 +1740,8 @@ def render_decoration(
|
|||||||
which: DecorationTypes, cell_width: int, cell_height: int, underline_position: int, underline_thickness: int, dpi: float = 96.0
|
which: DecorationTypes, cell_width: int, cell_height: int, underline_position: int, underline_thickness: int, dpi: float = 96.0
|
||||||
) -> bytes: ...
|
) -> bytes: ...
|
||||||
def os_window_is_invisible(os_window_id: int) -> bool: ...
|
def os_window_is_invisible(os_window_id: int) -> bool: ...
|
||||||
|
def remove_floating_window(os_window_id: int, tab_id: int, non_floating_window_id: int, window_id: int) -> None: ...
|
||||||
|
def add_floating_window(os_window_id: int, tab_id: int, floating_in: int) -> tuple[int, int]: ...
|
||||||
|
|
||||||
class MousePosition(TypedDict):
|
class MousePosition(TypedDict):
|
||||||
cell_x: int
|
cell_x: int
|
||||||
|
|||||||
10
kitty/floats.py
Normal file
10
kitty/floats.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# License: GPLv3 Copyright: 2025, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
from .types import WindowGeometry
|
||||||
|
from .typing import WindowType
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_geometry_for_float(child: WindowType, parent_geometry: WindowGeometry) -> WindowGeometry:
|
||||||
|
# TODO: Implement this
|
||||||
|
return parent_geometry
|
||||||
@@ -81,7 +81,7 @@ of the active window in the tab is used as the tab title. The special value
|
|||||||
--type
|
--type
|
||||||
type=choices
|
type=choices
|
||||||
default=window
|
default=window
|
||||||
choices=window,tab,os-window,overlay,overlay-main,background,clipboard,primary
|
choices=window,tab,os-window,overlay,overlay-main,background,clipboard,primary,float
|
||||||
Where to launch the child process:
|
Where to launch the child process:
|
||||||
|
|
||||||
:code:`window`
|
:code:`window`
|
||||||
@@ -105,6 +105,9 @@ Where to launch the child process:
|
|||||||
directory, the input text for kittens, launch commands, etc. Useful if this overlay is
|
directory, the input text for kittens, launch commands, etc. Useful if this overlay is
|
||||||
intended to run for a long time as a primary window.
|
intended to run for a long time as a primary window.
|
||||||
|
|
||||||
|
:code:`float`
|
||||||
|
A floating window displayed on top of the parent window.
|
||||||
|
|
||||||
:code:`background`
|
:code:`background`
|
||||||
The process will be run in the :italic:`background`, without a kitty
|
The process will be run in the :italic:`background`, without a kitty
|
||||||
window. Note that if :option:`kitten @ launch --allow-remote-control` is
|
window. Note that if :option:`kitten @ launch --allow-remote-control` is
|
||||||
@@ -122,7 +125,8 @@ Where to launch the child process:
|
|||||||
--keep-focus --dont-take-focus
|
--keep-focus --dont-take-focus
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Keep the focus on the currently active window instead of switching to the newly
|
Keep the focus on the currently active window instead of switching to the newly
|
||||||
opened window.
|
opened window. Note that floating windows always take focus from their parent
|
||||||
|
non-floating window.
|
||||||
|
|
||||||
|
|
||||||
--cwd
|
--cwd
|
||||||
@@ -722,8 +726,8 @@ def _launch(
|
|||||||
tab = tab_for_window(boss, opts, target_tab, next_to)
|
tab = tab_for_window(boss, opts, target_tab, next_to)
|
||||||
watchers = load_watch_modules(opts.watcher)
|
watchers = load_watch_modules(opts.watcher)
|
||||||
with Window.set_ignore_focus_changes_for_new_windows(opts.keep_focus):
|
with Window.set_ignore_focus_changes_for_new_windows(opts.keep_focus):
|
||||||
new_window: Window = tab.new_window(
|
new_window = tab.new_window(
|
||||||
env=env or None, watchers=watchers or None, is_clone_launch=is_clone_launch, next_to=next_to, **kw)
|
env=env or None, watchers=watchers or None, is_clone_launch=is_clone_launch, next_to=next_to, is_float=opts.type == 'float', **kw)
|
||||||
if spacing:
|
if spacing:
|
||||||
patch_window_edges(new_window, spacing)
|
patch_window_edges(new_window, spacing)
|
||||||
tab.relayout()
|
tab.relayout()
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
GlobalState global_state = {{0}};
|
GlobalState global_state = {{0}};
|
||||||
|
|
||||||
#define REMOVER(array, qid, count, destroy, capacity) { \
|
#define REMOVER(array, qid, count, destroy) { \
|
||||||
for (size_t i = 0; i < count; i++) { \
|
for (size_t i = 0; i < count; i++) { \
|
||||||
if (array[i].id == qid) { \
|
if (array[i].id == qid) { \
|
||||||
destroy(array + i); \
|
destroy(array + i); \
|
||||||
@@ -315,9 +315,31 @@ add_window(id_type os_window_id, id_type tab_id, PyObject *title) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Window*
|
||||||
|
add_floating_window(id_type os_window_id, id_type tab_id, id_type floating_in) {
|
||||||
|
WITH_TAB(os_window_id, tab_id);
|
||||||
|
for (size_t i = 0; i < tab->num_windows; i++) {
|
||||||
|
Window *window = tab->windows + i;
|
||||||
|
if (window->id != floating_in) window = find_child_window(window, floating_in);
|
||||||
|
if (window) {
|
||||||
|
make_os_window_context_current(osw);
|
||||||
|
ensure_space_for(&window->floating, children, Window, window->floating.child_count + 1, child_capacity, 1, true);
|
||||||
|
zero_at_i(window->floating.children, window->floating.child_count);
|
||||||
|
initialize_window(window->floating.children + window->floating.child_count, NULL, true);
|
||||||
|
Window *ans = &window->floating.children[window->floating.child_count++];
|
||||||
|
ans->floating.is_floating = true;
|
||||||
|
ans->floating.parent = floating_in;
|
||||||
|
ans->floating.non_floating_ancestor = tab->windows[i].id;
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END_WITH_TAB;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
update_window_title(id_type os_window_id, id_type tab_id, id_type window_id, PyObject *title) {
|
update_window_title(id_type os_window_id, id_type tab_id, id_type window_id, PyObject *title) {
|
||||||
WITH_WINDOW(os_window_id, tab_id, window_id)
|
WITH_WINDOW(os_window_id, tab_id, window_id);
|
||||||
Py_CLEAR(window->title);
|
Py_CLEAR(window->title);
|
||||||
window->title = title;
|
window->title = title;
|
||||||
Py_XINCREF(window->title);
|
Py_XINCREF(window->title);
|
||||||
@@ -360,13 +382,15 @@ destroy_window(Window *w) {
|
|||||||
decref_window_logo(global_state.all_window_logos, w->window_logo.id);
|
decref_window_logo(global_state.all_window_logos, w->window_logo.id);
|
||||||
w->window_logo.id = 0;
|
w->window_logo.id = 0;
|
||||||
}
|
}
|
||||||
|
for (size_t i = 0; i < w->floating.child_count; i++) destroy_window(&w->floating.children[i]);
|
||||||
|
free(w->floating.children); w->floating.children = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
remove_window_inner(Tab *tab, id_type id) {
|
remove_window_inner(Tab *tab, id_type id) {
|
||||||
id_type active_window_id = 0;
|
id_type active_window_id = 0;
|
||||||
if (tab->active_window < tab->num_windows) active_window_id = tab->windows[tab->active_window].id;
|
if (tab->active_window < tab->num_windows) active_window_id = tab->windows[tab->active_window].id;
|
||||||
REMOVER(tab->windows, id, tab->num_windows, destroy_window, tab->capacity);
|
REMOVER(tab->windows, id, tab->num_windows, destroy_window);
|
||||||
if (active_window_id) {
|
if (active_window_id) {
|
||||||
for (unsigned int w = 0; w < tab->num_windows; w++) {
|
for (unsigned int w = 0; w < tab->num_windows; w++) {
|
||||||
if (tab->windows[w].id == active_window_id) {
|
if (tab->windows[w].id == active_window_id) {
|
||||||
@@ -376,6 +400,30 @@ remove_window_inner(Tab *tab, id_type id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
remove_floating_window_inner(Window *window, id_type id) {
|
||||||
|
Window *w;
|
||||||
|
for (size_t i = 0; i < window->floating.child_count; i++) {
|
||||||
|
if ((w = window->floating.children + i)->id == id) {
|
||||||
|
destroy_window(w);
|
||||||
|
zero_at_i(window->floating.children, i);
|
||||||
|
remove_i_from_array(window->floating.children, i, window->floating.child_count);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (remove_floating_window_inner(window->floating.children + i, id)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
remove_floating_window(id_type os_window_id, id_type tab_id, id_type parent_non_floating_window, id_type id) {
|
||||||
|
WITH_WINDOW(os_window_id, tab_id, parent_non_floating_window);
|
||||||
|
make_os_window_context_current(osw);
|
||||||
|
remove_floating_window_inner(window, id);
|
||||||
|
END_WITH_WINDOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
remove_window(id_type os_window_id, id_type tab_id, id_type id) {
|
remove_window(id_type os_window_id, id_type tab_id, id_type id) {
|
||||||
WITH_TAB(os_window_id, tab_id);
|
WITH_TAB(os_window_id, tab_id);
|
||||||
@@ -462,7 +510,7 @@ remove_tab_inner(OSWindow *os_window, id_type id) {
|
|||||||
id_type active_tab_id = 0;
|
id_type active_tab_id = 0;
|
||||||
if (os_window->active_tab < os_window->num_tabs) active_tab_id = os_window->tabs[os_window->active_tab].id;
|
if (os_window->active_tab < os_window->num_tabs) active_tab_id = os_window->tabs[os_window->active_tab].id;
|
||||||
make_os_window_context_current(os_window);
|
make_os_window_context_current(os_window);
|
||||||
REMOVER(os_window->tabs, id, os_window->num_tabs, destroy_tab, os_window->capacity);
|
REMOVER(os_window->tabs, id, os_window->num_tabs, destroy_tab);
|
||||||
if (active_tab_id) {
|
if (active_tab_id) {
|
||||||
for (unsigned int i = 0; i < os_window->num_tabs; i++) {
|
for (unsigned int i = 0; i < os_window->num_tabs; i++) {
|
||||||
if (os_window->tabs[i].id == active_tab_id) {
|
if (os_window->tabs[i].id == active_tab_id) {
|
||||||
@@ -501,7 +549,7 @@ remove_os_window(id_type os_window_id) {
|
|||||||
END_WITH_OS_WINDOW
|
END_WITH_OS_WINDOW
|
||||||
if (found) {
|
if (found) {
|
||||||
WITH_OS_WINDOW_REFS
|
WITH_OS_WINDOW_REFS
|
||||||
REMOVER(global_state.os_windows, os_window_id, global_state.num_os_windows, destroy_os_window_item, global_state.capacity);
|
REMOVER(global_state.os_windows, os_window_id, global_state.num_os_windows, destroy_os_window_item);
|
||||||
END_WITH_OS_WINDOW_REFS
|
END_WITH_OS_WINDOW_REFS
|
||||||
update_os_window_references();
|
update_os_window_references();
|
||||||
}
|
}
|
||||||
@@ -643,6 +691,26 @@ make_window_context_current(id_type window_id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
iter_child_windows(Window *self, bool(*callback)(Window *, void *data), void *data) {
|
||||||
|
for (size_t i = 0; i < self->floating.child_count; i++) {
|
||||||
|
Window *w = self->floating.children + i;
|
||||||
|
if (!callback(w, data)) return;
|
||||||
|
iter_child_windows(w, callback, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Window*
|
||||||
|
find_child_window(Window *self, id_type child_id) {
|
||||||
|
for (size_t i = 0; i < self->floating.child_count; i++) {
|
||||||
|
Window *w = self->floating.children + i;
|
||||||
|
if (w->id == child_id) return w;
|
||||||
|
if ((w = find_child_window(w, child_id))) return w;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
dispatch_pending_clicks(id_type timer_id UNUSED, void *data UNUSED) {
|
dispatch_pending_clicks(id_type timer_id UNUSED, void *data UNUSED) {
|
||||||
bool dispatched = false;
|
bool dispatched = false;
|
||||||
@@ -675,7 +743,8 @@ update_ime_position_for_window(id_type window_id, bool force, int update_focus)
|
|||||||
Tab *qtab = osw->tabs + t;
|
Tab *qtab = osw->tabs + t;
|
||||||
for (size_t w = 0; w < qtab->num_windows; w++) {
|
for (size_t w = 0; w < qtab->num_windows; w++) {
|
||||||
Window *window = qtab->windows + w;
|
Window *window = qtab->windows + w;
|
||||||
if (window->id == window_id) {
|
if (window_id != window->id) window = find_child_window(window, window_id);
|
||||||
|
if (window != NULL) {
|
||||||
// The screen may not be ready after the new window is created and focused, and still needs to enable IME.
|
// The screen may not be ready after the new window is created and focused, and still needs to enable IME.
|
||||||
if ((window->render_data.screen && (force || osw->is_focused)) || update_focus > 0) {
|
if ((window->render_data.screen && (force || osw->is_focused)) || update_focus > 0) {
|
||||||
OSWindow *orig = global_state.callback_os_window;
|
OSWindow *orig = global_state.callback_os_window;
|
||||||
@@ -695,7 +764,6 @@ update_ime_position_for_window(id_type window_id, bool force, int update_focus)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Python API {{{
|
// Python API {{{
|
||||||
#define PYWRAP0(name) static PyObject* py##name(PYNOARG)
|
#define PYWRAP0(name) static PyObject* py##name(PYNOARG)
|
||||||
#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)
|
#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)
|
||||||
@@ -705,6 +773,7 @@ update_ime_position_for_window(id_type window_id, bool force, int update_focus)
|
|||||||
#define THREE_UINT(name) PYWRAP1(name) { unsigned int a, b, c; PA("III", &a, &b, &c); name(a, b, c); Py_RETURN_NONE; }
|
#define THREE_UINT(name) PYWRAP1(name) { unsigned int a, b, c; PA("III", &a, &b, &c); name(a, b, c); Py_RETURN_NONE; }
|
||||||
#define TWO_ID(name) PYWRAP1(name) { id_type a, b; PA("KK", &a, &b); name(a, b); Py_RETURN_NONE; }
|
#define TWO_ID(name) PYWRAP1(name) { id_type a, b; PA("KK", &a, &b); name(a, b); Py_RETURN_NONE; }
|
||||||
#define THREE_ID(name) PYWRAP1(name) { id_type a, b, c; PA("KKK", &a, &b, &c); name(a, b, c); Py_RETURN_NONE; }
|
#define THREE_ID(name) PYWRAP1(name) { id_type a, b, c; PA("KKK", &a, &b, &c); name(a, b, c); Py_RETURN_NONE; }
|
||||||
|
#define FOUR_ID(name) PYWRAP1(name) { id_type a, b, c, d; PA("KKKK", &a, &b, &c, &d); name(a, b, c, d); Py_RETURN_NONE; }
|
||||||
#define THREE_ID_OBJ(name) PYWRAP1(name) { id_type a, b, c; PyObject *o; PA("KKKO", &a, &b, &c, &o); name(a, b, c, o); Py_RETURN_NONE; }
|
#define THREE_ID_OBJ(name) PYWRAP1(name) { id_type a, b, c; PyObject *o; PA("KKKO", &a, &b, &c, &o); name(a, b, c, o); Py_RETURN_NONE; }
|
||||||
#define K(name) PYWRAP1(name) { id_type a; PA("K", &a); name(a); Py_RETURN_NONE; }
|
#define K(name) PYWRAP1(name) { id_type a; PA("K", &a); name(a); Py_RETURN_NONE; }
|
||||||
#define KI(name) PYWRAP1(name) { id_type a; unsigned int b; PA("KI", &a, &b); name(a, b); Py_RETURN_NONE; }
|
#define KI(name) PYWRAP1(name) { id_type a; unsigned int b; PA("KI", &a, &b); name(a, b); Py_RETURN_NONE; }
|
||||||
@@ -1399,6 +1468,7 @@ PYWRAP1(buffer_keys_in_window) {
|
|||||||
|
|
||||||
THREE_ID_OBJ(update_window_title)
|
THREE_ID_OBJ(update_window_title)
|
||||||
THREE_ID(remove_window)
|
THREE_ID(remove_window)
|
||||||
|
FOUR_ID(remove_floating_window)
|
||||||
THREE_ID(detach_window)
|
THREE_ID(detach_window)
|
||||||
THREE_ID(attach_window)
|
THREE_ID(attach_window)
|
||||||
PYWRAP1(add_tab) { return PyLong_FromUnsignedLongLong(add_tab(PyLong_AsUnsignedLongLong(args))); }
|
PYWRAP1(add_tab) { return PyLong_FromUnsignedLongLong(add_tab(PyLong_AsUnsignedLongLong(args))); }
|
||||||
@@ -1412,6 +1482,14 @@ KII(swap_tabs)
|
|||||||
KK5I(add_borders_rect)
|
KK5I(add_borders_rect)
|
||||||
KKKK(set_redirect_keys_to_overlay)
|
KKKK(set_redirect_keys_to_overlay)
|
||||||
|
|
||||||
|
PYWRAP1(add_floating_window) {
|
||||||
|
id_type os_window_id, tab_id, floating_in;
|
||||||
|
PA("KKK", &os_window_id, &tab_id, &floating_in);
|
||||||
|
Window *ans = add_floating_window(os_window_id, tab_id, floating_in);
|
||||||
|
if (ans) return Py_BuildValue("KK", ans->id, ans->floating.non_floating_ancestor);
|
||||||
|
return Py_BuildValue("II", 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
os_window_focus_counters(PyObject *self UNUSED, PyObject *args UNUSED) {
|
os_window_focus_counters(PyObject *self UNUSED, PyObject *args UNUSED) {
|
||||||
RAII_PyObject(ans, PyDict_New());
|
RAII_PyObject(ans, PyDict_New());
|
||||||
@@ -1462,9 +1540,11 @@ static PyMethodDef module_methods[] = {
|
|||||||
MW(pt_to_px, METH_VARARGS),
|
MW(pt_to_px, METH_VARARGS),
|
||||||
MW(add_tab, METH_O),
|
MW(add_tab, METH_O),
|
||||||
MW(add_window, METH_VARARGS),
|
MW(add_window, METH_VARARGS),
|
||||||
|
MW(add_floating_window, METH_VARARGS),
|
||||||
MW(update_window_title, METH_VARARGS),
|
MW(update_window_title, METH_VARARGS),
|
||||||
MW(remove_tab, METH_VARARGS),
|
MW(remove_tab, METH_VARARGS),
|
||||||
MW(remove_window, METH_VARARGS),
|
MW(remove_window, METH_VARARGS),
|
||||||
|
MW(remove_floating_window, METH_VARARGS),
|
||||||
MW(detach_window, METH_VARARGS),
|
MW(detach_window, METH_VARARGS),
|
||||||
MW(attach_window, METH_VARARGS),
|
MW(attach_window, METH_VARARGS),
|
||||||
MW(set_active_tab, METH_VARARGS),
|
MW(set_active_tab, METH_VARARGS),
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ typedef struct WindowBarData {
|
|||||||
bool needs_render;
|
bool needs_render;
|
||||||
} WindowBarData;
|
} WindowBarData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct Window {
|
||||||
id_type id;
|
id_type id;
|
||||||
bool visible;
|
bool visible;
|
||||||
float cursor_opacity_at_last_render;
|
float cursor_opacity_at_last_render;
|
||||||
@@ -216,6 +216,12 @@ typedef struct {
|
|||||||
PendingClick *clicks;
|
PendingClick *clicks;
|
||||||
size_t num, capacity;
|
size_t num, capacity;
|
||||||
} pending_clicks;
|
} pending_clicks;
|
||||||
|
struct {
|
||||||
|
bool is_floating, can_become_key, is_key;
|
||||||
|
id_type parent, non_floating_ancestor;
|
||||||
|
struct Window *children; // must be maintained in render order
|
||||||
|
size_t child_count, child_capacity;
|
||||||
|
} floating;
|
||||||
} Window;
|
} Window;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -440,3 +446,5 @@ bool render_os_window(OSWindow *w, monotonic_t now, bool ignore_render_frames, b
|
|||||||
void update_mouse_pointer_shape(void);
|
void update_mouse_pointer_shape(void);
|
||||||
void adjust_window_size_for_csd(OSWindow *w, int width, int height, int *adjusted_width, int *adjusted_height);
|
void adjust_window_size_for_csd(OSWindow *w, int width, int height, int *adjusted_width, int *adjusted_height);
|
||||||
void dispatch_buffered_keys(Window *w);
|
void dispatch_buffered_keys(Window *w);
|
||||||
|
Window* find_child_window(Window *self, id_type child_id);
|
||||||
|
void iter_child_windows(Window *self, bool(*callback)(Window *, void *data), void *data);
|
||||||
|
|||||||
@@ -568,20 +568,27 @@ class Tab: # {{{
|
|||||||
pass_fds: tuple[int, ...] = (),
|
pass_fds: tuple[int, ...] = (),
|
||||||
remote_control_fd: int = -1,
|
remote_control_fd: int = -1,
|
||||||
next_to: Window | None = None,
|
next_to: Window | None = None,
|
||||||
|
is_float: bool = False,
|
||||||
) -> Window:
|
) -> Window:
|
||||||
|
floating_in = 0
|
||||||
|
if is_float:
|
||||||
|
fp = next_to or self.active_window
|
||||||
|
if fp is not None:
|
||||||
|
floating_in = fp.id
|
||||||
child = self.launch_child(
|
child = self.launch_child(
|
||||||
use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env,
|
use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env,
|
||||||
is_clone_launch=is_clone_launch, add_listen_on_env_var=False if allow_remote_control and remote_control_passwords else True,
|
is_clone_launch=is_clone_launch, add_listen_on_env_var=False if allow_remote_control and remote_control_passwords else True,
|
||||||
hold=hold, pass_fds=pass_fds, remote_control_fd=remote_control_fd,
|
hold=hold, pass_fds=pass_fds, remote_control_fd=remote_control_fd,
|
||||||
)
|
)
|
||||||
window = Window(
|
window = Window(
|
||||||
self, child, self.args, override_title=override_title,
|
self, child, self.args, override_title=override_title, floating_in=floating_in,
|
||||||
copy_colors_from=copy_colors_from, watchers=watchers,
|
copy_colors_from=copy_colors_from, watchers=watchers,
|
||||||
allow_remote_control=allow_remote_control, remote_control_passwords=remote_control_passwords
|
allow_remote_control=allow_remote_control, remote_control_passwords=remote_control_passwords
|
||||||
)
|
)
|
||||||
# Must add child before laying out so that resize_pty succeeds
|
# Must add child before laying out so that resize_pty succeeds
|
||||||
get_boss().add_child(window)
|
get_boss().add_child(window)
|
||||||
self._add_window(window, location=location, overlay_for=overlay_for, overlay_behind=overlay_behind, bias=bias, next_to=next_to)
|
if not floating_in:
|
||||||
|
self._add_window(window, location=location, overlay_for=overlay_for, overlay_behind=overlay_behind, bias=bias, next_to=next_to)
|
||||||
if marker:
|
if marker:
|
||||||
try:
|
try:
|
||||||
window.set_marker(marker)
|
window.set_marker(marker)
|
||||||
@@ -843,7 +850,7 @@ class Tab: # {{{
|
|||||||
yield w.as_dict(
|
yield w.as_dict(
|
||||||
is_active=w is active_window,
|
is_active=w is active_window,
|
||||||
is_focused=w.os_window_id == current_focused_os_window_id() and w is active_window,
|
is_focused=w.os_window_id == current_focused_os_window_id() and w is active_window,
|
||||||
is_self=w is self_window)
|
self_window=self_window)
|
||||||
|
|
||||||
def list_groups(self) -> list[dict[str, Any]]:
|
def list_groups(self) -> list[dict[str, Any]]:
|
||||||
return [g.as_simple_dict() for g in self.windows.groups]
|
return [g.as_simple_dict() for g in self.windows.groups]
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from typing import (
|
|||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
Any,
|
Any,
|
||||||
Deque,
|
Deque,
|
||||||
|
Iterator,
|
||||||
Literal,
|
Literal,
|
||||||
NamedTuple,
|
NamedTuple,
|
||||||
Optional,
|
Optional,
|
||||||
@@ -54,6 +55,7 @@ from .fast_data_types import (
|
|||||||
ColorProfile,
|
ColorProfile,
|
||||||
KeyEvent,
|
KeyEvent,
|
||||||
Screen,
|
Screen,
|
||||||
|
add_floating_window,
|
||||||
add_timer,
|
add_timer,
|
||||||
add_window,
|
add_window,
|
||||||
base64_decode,
|
base64_decode,
|
||||||
@@ -87,12 +89,13 @@ from .fast_data_types import (
|
|||||||
update_window_visibility,
|
update_window_visibility,
|
||||||
wakeup_main_loop,
|
wakeup_main_loop,
|
||||||
)
|
)
|
||||||
|
from .floats import calculate_geometry_for_float
|
||||||
from .keys import keyboard_mode_name, mod_mask
|
from .keys import keyboard_mode_name, mod_mask
|
||||||
from .progress import Progress
|
from .progress import Progress
|
||||||
from .rgb import to_color
|
from .rgb import to_color
|
||||||
from .terminfo import get_capabilities
|
from .terminfo import get_capabilities
|
||||||
from .types import MouseEvent, OverlayType, WindowGeometry, ac, run_once
|
from .types import MouseEvent, OverlayType, WindowGeometry, ac, run_once
|
||||||
from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict
|
from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict, WindowType
|
||||||
from .utils import (
|
from .utils import (
|
||||||
color_as_int,
|
color_as_int,
|
||||||
docs_url,
|
docs_url,
|
||||||
@@ -243,6 +246,7 @@ class WindowDict(TypedDict):
|
|||||||
at_prompt: bool
|
at_prompt: bool
|
||||||
created_at: int
|
created_at: int
|
||||||
in_alternate_screen: bool
|
in_alternate_screen: bool
|
||||||
|
floats: tuple['WindowDict', ...]
|
||||||
|
|
||||||
|
|
||||||
class PipeData(TypedDict):
|
class PipeData(TypedDict):
|
||||||
@@ -623,6 +627,7 @@ class Window:
|
|||||||
watchers: Watchers | None = None,
|
watchers: Watchers | None = None,
|
||||||
allow_remote_control: bool = False,
|
allow_remote_control: bool = False,
|
||||||
remote_control_passwords: dict[str, Sequence[str]] | None = None,
|
remote_control_passwords: dict[str, Sequence[str]] | None = None,
|
||||||
|
floating_in: int = 0, # window id of non-floating ancestor window
|
||||||
):
|
):
|
||||||
if watchers:
|
if watchers:
|
||||||
self.watchers = watchers
|
self.watchers = watchers
|
||||||
@@ -637,6 +642,7 @@ class Window:
|
|||||||
self.started_at = monotonic()
|
self.started_at = monotonic()
|
||||||
self.created_at = time_ns()
|
self.created_at = time_ns()
|
||||||
self.clear_progress_timer: int = 0
|
self.clear_progress_timer: int = 0
|
||||||
|
self.floating_in = floating_in
|
||||||
self.current_remote_data: list[str] = []
|
self.current_remote_data: list[str] = []
|
||||||
self.current_mouse_event_button = 0
|
self.current_mouse_event_button = 0
|
||||||
self.current_clipboard_read_ask: bool | None = None
|
self.current_clipboard_read_ask: bool | None = None
|
||||||
@@ -659,13 +665,21 @@ class Window:
|
|||||||
self.child_title = self.default_title
|
self.child_title = self.default_title
|
||||||
self.title_stack: Deque[str] = deque(maxlen=10)
|
self.title_stack: Deque[str] = deque(maxlen=10)
|
||||||
self.user_vars: dict[str, str] = {}
|
self.user_vars: dict[str, str] = {}
|
||||||
self.id: int = add_window(tab.os_window_id, tab.id, self.title)
|
self.non_floating_ancestor: int = 0
|
||||||
|
self.id: int = 0
|
||||||
|
if floating_in:
|
||||||
|
self.id, self.non_floating_ancestor = add_floating_window(tab.os_window_id, tab.id, floating_in)
|
||||||
|
if not self.id:
|
||||||
|
raise Exception(f'No window with id: {floating_in} in Tab: {tab.id} OS Window: {tab.os_window_id} was found, or the window counter wrapped')
|
||||||
|
get_boss().window_floats_map.setdefault(floating_in, []).append(self.id)
|
||||||
|
else:
|
||||||
|
self.id = add_window(tab.os_window_id, tab.id, self.title)
|
||||||
|
if not self.id:
|
||||||
|
raise Exception(f'No tab with id: {tab.id} in OS Window: {tab.os_window_id} was found, or the window counter wrapped')
|
||||||
self.clipboard_request_manager = ClipboardRequestManager(self.id)
|
self.clipboard_request_manager = ClipboardRequestManager(self.id)
|
||||||
self.margin = EdgeWidths()
|
self.margin = EdgeWidths()
|
||||||
self.padding = EdgeWidths()
|
self.padding = EdgeWidths()
|
||||||
self.kitten_result: dict[str, Any] | None = None
|
self.kitten_result: dict[str, Any] | None = None
|
||||||
if not self.id:
|
|
||||||
raise Exception(f'No tab with id: {tab.id} in OS Window: {tab.os_window_id} was found, or the window counter wrapped')
|
|
||||||
self.tab_id = tab.id
|
self.tab_id = tab.id
|
||||||
self.os_window_id = tab.os_window_id
|
self.os_window_id = tab.os_window_id
|
||||||
self.tabref: Callable[[], TabType | None] = weakref.ref(tab)
|
self.tabref: Callable[[], TabType | None] = weakref.ref(tab)
|
||||||
@@ -703,6 +717,8 @@ class Window:
|
|||||||
self.tab_id = tab.id
|
self.tab_id = tab.id
|
||||||
self.os_window_id = tab.os_window_id
|
self.os_window_id = tab.os_window_id
|
||||||
self.tabref = weakref.ref(tab)
|
self.tabref = weakref.ref(tab)
|
||||||
|
for cw in self.floats:
|
||||||
|
cw.change_tab(tab)
|
||||||
|
|
||||||
def effective_margin(self, edge: EdgeLiteral) -> int:
|
def effective_margin(self, edge: EdgeLiteral) -> int:
|
||||||
q = getattr(self.margin, edge)
|
q = getattr(self.margin, edge)
|
||||||
@@ -763,7 +779,16 @@ class Window:
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'Window(title={self.title}, id={self.id})'
|
return f'Window(title={self.title}, id={self.id})'
|
||||||
|
|
||||||
def as_dict(self, is_focused: bool = False, is_self: bool = False, is_active: bool = False) -> WindowDict:
|
@property
|
||||||
|
def floats(self) -> Iterator['Window']:
|
||||||
|
b = get_boss()
|
||||||
|
if b is not None:
|
||||||
|
for fid in b.window_floats_map.get(self.id, ()):
|
||||||
|
c = b.window_id_map.get(fid)
|
||||||
|
if c is not None:
|
||||||
|
yield c
|
||||||
|
|
||||||
|
def as_dict(self, is_focused: bool = False, self_window: WindowType | None = None, is_active: bool = False) -> WindowDict:
|
||||||
return {
|
return {
|
||||||
'id': self.id,
|
'id': self.id,
|
||||||
'is_focused': is_focused,
|
'is_focused': is_focused,
|
||||||
@@ -776,13 +801,15 @@ class Window:
|
|||||||
'last_cmd_exit_status': self.last_cmd_exit_status,
|
'last_cmd_exit_status': self.last_cmd_exit_status,
|
||||||
'env': self.child.environ or self.child.final_env,
|
'env': self.child.environ or self.child.final_env,
|
||||||
'foreground_processes': self.child.foreground_processes,
|
'foreground_processes': self.child.foreground_processes,
|
||||||
'is_self': is_self,
|
'is_self': self_window is self,
|
||||||
'at_prompt': self.at_prompt,
|
'at_prompt': self.at_prompt,
|
||||||
'lines': self.screen.lines,
|
'lines': self.screen.lines,
|
||||||
'columns': self.screen.columns,
|
'columns': self.screen.columns,
|
||||||
'user_vars': self.user_vars,
|
'user_vars': self.user_vars,
|
||||||
'created_at': self.created_at,
|
'created_at': self.created_at,
|
||||||
'in_alternate_screen': self.screen.is_using_alternate_linebuf(),
|
'in_alternate_screen': self.screen.is_using_alternate_linebuf(),
|
||||||
|
'floats': tuple(w.as_dict(
|
||||||
|
is_focused=is_focused and w.is_focused, is_active=is_active and w.is_focused, self_window=self_window) for w in self.floats),
|
||||||
}
|
}
|
||||||
|
|
||||||
def serialize_state(self) -> dict[str, Any]:
|
def serialize_state(self) -> dict[str, Any]:
|
||||||
@@ -803,6 +830,7 @@ class Window:
|
|||||||
'margin': self.margin.serialize(),
|
'margin': self.margin.serialize(),
|
||||||
'user_vars': self.user_vars,
|
'user_vars': self.user_vars,
|
||||||
'padding': self.padding.serialize(),
|
'padding': self.padding.serialize(),
|
||||||
|
'in_alternate_screen': self.screen.is_using_alternate_linebuf(),
|
||||||
}
|
}
|
||||||
if self.window_custom_type:
|
if self.window_custom_type:
|
||||||
ans['window_custom_type'] = self.window_custom_type
|
ans['window_custom_type'] = self.window_custom_type
|
||||||
@@ -810,6 +838,9 @@ class Window:
|
|||||||
ans['overlay_type'] = self.overlay_type.value
|
ans['overlay_type'] = self.overlay_type.value
|
||||||
if self.user_vars:
|
if self.user_vars:
|
||||||
ans['user_vars'] = self.user_vars
|
ans['user_vars'] = self.user_vars
|
||||||
|
floats = tuple(self.floats)
|
||||||
|
if floats:
|
||||||
|
ans['floats'] = floats
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -931,8 +962,8 @@ class Window:
|
|||||||
self.screen.lines, self.screen.columns,
|
self.screen.lines, self.screen.columns,
|
||||||
max(0, new_geometry.right - new_geometry.left), max(0, new_geometry.bottom - new_geometry.top))
|
max(0, new_geometry.right - new_geometry.left), max(0, new_geometry.bottom - new_geometry.top))
|
||||||
update_ime_position = False
|
update_ime_position = False
|
||||||
|
boss = get_boss()
|
||||||
if current_pty_size != self.last_reported_pty_size:
|
if current_pty_size != self.last_reported_pty_size:
|
||||||
boss = get_boss()
|
|
||||||
boss.child_monitor.resize_pty(self.id, *current_pty_size)
|
boss.child_monitor.resize_pty(self.id, *current_pty_size)
|
||||||
self.last_resized_at = monotonic()
|
self.last_resized_at = monotonic()
|
||||||
self.last_reported_pty_size = current_pty_size
|
self.last_reported_pty_size = current_pty_size
|
||||||
@@ -948,12 +979,14 @@ class Window:
|
|||||||
print(f'[{monotonic():.3f}] SIGWINCH sent to child in window: {self.id} with size: {current_pty_size}', file=sys.stderr)
|
print(f'[{monotonic():.3f}] SIGWINCH sent to child in window: {self.id} with size: {current_pty_size}', file=sys.stderr)
|
||||||
else:
|
else:
|
||||||
mark_os_window_dirty(self.os_window_id)
|
mark_os_window_dirty(self.os_window_id)
|
||||||
|
self.geometry = new_geometry
|
||||||
self.geometry = g = new_geometry
|
set_window_render_data(self.os_window_id, self.tab_id, self.id, self.screen, *self.geometry[:4])
|
||||||
set_window_render_data(self.os_window_id, self.tab_id, self.id, self.screen, *g[:4])
|
|
||||||
self.update_effective_padding()
|
self.update_effective_padding()
|
||||||
if update_ime_position:
|
if update_ime_position:
|
||||||
update_ime_position_for_window(self.id, True)
|
update_ime_position_for_window(self.id, True)
|
||||||
|
for cw in self.floats:
|
||||||
|
child_geometry = calculate_geometry_for_float(cw, self.geometry)
|
||||||
|
cw.set_geometry(child_geometry)
|
||||||
|
|
||||||
def contains(self, x: int, y: int) -> bool:
|
def contains(self, x: int, y: int) -> bool:
|
||||||
g = self.geometry
|
g = self.geometry
|
||||||
@@ -1710,8 +1743,12 @@ class Window:
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
def destroy(self) -> None:
|
def destroy(self) -> None:
|
||||||
self.call_watchers(self.watchers.on_close, {})
|
if self.destroyed:
|
||||||
|
return
|
||||||
self.destroyed = True
|
self.destroyed = True
|
||||||
|
for cw in self.floats:
|
||||||
|
cw.destroy()
|
||||||
|
self.call_watchers(self.watchers.on_close, {})
|
||||||
self.clipboard_request_manager.close()
|
self.clipboard_request_manager.close()
|
||||||
del self.kitten_result_processors
|
del self.kitten_result_processors
|
||||||
if hasattr(self, 'screen'):
|
if hasattr(self, 'screen'):
|
||||||
|
|||||||
Reference in New Issue
Block a user