diff --git a/kitty/borders.py b/kitty/borders.py index 91d0f3d0f..ba8f05093 100644 --- a/kitty/borders.py +++ b/kitty/borders.py @@ -5,7 +5,6 @@ from ctypes import addressof from functools import partial from itertools import chain -from threading import Lock from .constants import GLfloat, GLint, GLuint, viewport_size from .fast_data_types import ( @@ -74,7 +73,6 @@ class Borders: def __init__(self, opts): self.is_dirty = False - self.lock = Lock() self.can_render = False self.border_width = pt_to_px(opts.window_border_width) self.padding_width = pt_to_px(opts.window_padding_width) @@ -113,31 +111,29 @@ class Borders: color, pw, g.left - pw, g.top - pw, g.right + pw, g.bottom + pw) - with self.lock: - self.num_of_rects = len(rects) // 12 - self.rects = (GLfloat * len(rects))() - self.starts = (GLint * self.num_of_rects)() - self.counts = (GLuint * self.num_of_rects)() - for i, x in enumerate(rects): - self.rects[i] = x - if i % 12 == 0: - idx = i // 12 - self.starts[idx] = i // 3 - self.counts[idx] = 4 - self.is_dirty = True - self.can_render = True + self.num_of_rects = len(rects) // 12 + self.rects = (GLfloat * len(rects))() + self.starts = (GLint * self.num_of_rects)() + self.counts = (GLuint * self.num_of_rects)() + for i, x in enumerate(rects): + self.rects[i] = x + if i % 12 == 0: + idx = i // 12 + self.starts[idx] = i // 3 + self.counts[idx] = 4 + self.is_dirty = True + self.can_render = True def render(self, program): - with self.lock: - if not self.can_render: - return - with program: - if self.is_dirty: - program.send_data(self.rects) - program.set_colors(self.color_buf) - self.is_dirty = False - with program.bound_vertex_array(program.vao_id): - glMultiDrawArrays( - GL_TRIANGLE_FAN, - addressof(self.starts), - addressof(self.counts), self.num_of_rects) + if not self.can_render: + return + with program: + if self.is_dirty: + program.send_data(self.rects) + program.set_colors(self.color_buf) + self.is_dirty = False + with program.bound_vertex_array(program.vao_id): + glMultiDrawArrays( + GL_TRIANGLE_FAN, + addressof(self.starts), + addressof(self.counts), self.num_of_rects) diff --git a/kitty/boss.py b/kitty/boss.py index 2674cc201..d02603699 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -2,23 +2,20 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2016, Kovid Goyal -import inspect import io import os import signal import struct -from functools import wraps from gettext import gettext as _ -from queue import Empty, Queue -from threading import Thread, current_thread +from threading import Thread from time import monotonic from .borders import BordersProgram from .char_grid import load_shader_programs from .config import MINIMUM_FONT_SIZE from .constants import ( - MODIFIER_KEYS, cell_size, is_key_pressed, isosx, main_thread, - mouse_button_pressed, mouse_cursor_pos, set_boss, viewport_size, wakeup + MODIFIER_KEYS, cell_size, is_key_pressed, isosx, mouse_button_pressed, + mouse_cursor_pos, set_boss, viewport_size, wakeup ) from .fast_data_types import ( GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA, GLFW_CURSOR, GLFW_CURSOR_HIDDEN, @@ -61,28 +58,6 @@ def conditional_run(w, i): next(i, None) -def callback(func): - ''' Wrapper for function that executes first half (up to a yield statement) - in the UI thread and the rest in the child thread. If the function yields - something, the destroyed attribute of that something is checked before - running the second half. If the function returns before the yield, the - second half is not run. ''' - - assert inspect.isgeneratorfunction(func) - - @wraps(func) - def f(self, *a): - i = func(self, *a) - try: - w = next(i) - except StopIteration: - pass - else: - self.queue_action(conditional_run, w, i) - - return f - - class DumpCommands: # {{{ def __init__(self, args): @@ -121,8 +96,6 @@ class Boss(Thread): self.cursor_blinking = True self.window_is_focused = True self.glfw_window_title = None - self.action_queue = Queue() - self.pending_resize = False self.resize_gl_viewport = False self.shutting_down = False self.signal_fd = handle_unix_signals() @@ -133,7 +106,6 @@ class Boss(Thread): self.read_wakeup_fd, self.on_wakeup, self.signal_fd, self.signal_received, self.timers, opts.repaint_delay / 1000.0, DumpCommands(args) if args.dump_commands or args.dump_bytes else None) - self.pending_ui_thread_calls = Queue() set_boss(self) self.current_font_size = opts.font_size cell_size.width, cell_size.height = set_font_family(opts) @@ -179,23 +151,9 @@ class Boss(Thread): for t in self: yield from t - def queue_action(self, func, *args): - self.action_queue.put((func, args)) - wakeup() - def on_wakeup(self): if not self.shutting_down: drain_read(self.read_wakeup_fd) - while True: - try: - func, args = self.action_queue.get_nowait() - except Empty: - break - try: - func(*args) - except Exception: - import traceback - safe_print(traceback.format_exc()) def add_child_fd(self, child_fd, window): ' Must be called in child thread ' @@ -205,49 +163,27 @@ class Boss(Thread): ' Must be called in child thread ' self.child_monitor.remove_child(child_fd) - def queue_ui_action(self, func, *args): - self.pending_ui_thread_calls.put((func, args)) - glfw_post_empty_event() - def close_window(self, window=None): - ' Can be called in either thread, will first kill the child, then remove the window from the gui ' if window is None: window = self.active_window - if current_thread() is main_thread: - self.queue_action(self.close_window, window) - else: - self.remove_child_fd(window.child_fd) - window.destroy() - self.queue_ui_action(self.gui_close_window, window) + window.destroy() + self.gui_close_window(window) def close_tab(self, tab=None): - ' Can be called in either thread, will first kill all children, then remove the tab from the gui ' if tab is None: tab = self.active_tab - if current_thread() is main_thread: - self.queue_action(self.close_tab, tab) - else: - for window in tab: - self.remove_child_fd(window.child_fd) - window.destroy() - self.queue_ui_action(self.gui_close_window, window) + for window in tab: + self.close_window(window) def run(self): self.child_monitor.loop() - @callback def on_window_resize(self, window, w, h): # debounce resize events - self.pending_resize = True - yield - self.timers.add(0.02, self.apply_pending_resize, w, h) - - def apply_pending_resize(self, w, h): if w > 100 and h > 100: viewport_size.width, viewport_size.height = w, h self.tab_manager.resize() self.resize_gl_viewport = True - self.pending_resize = False glfw_post_empty_event() else: safe_print('Ignoring resize request for sizes under 100x100') @@ -274,7 +210,7 @@ class Boss(Thread): cell_size.width, cell_size.height = set_font_family( self.opts, override_font_size=self.current_font_size) self.sprites.do_layout(cell_size.width, cell_size.height) - self.queue_action(self.resize_windows_after_font_size_change) + self.resize_windows_after_font_size_change() def resize_windows_after_font_size_change(self): self.tab_manager.resize() @@ -297,17 +233,13 @@ class Boss(Thread): if t is not None: return t.active_window - @callback def on_text_input(self, window, codepoint, mods): w = self.active_window if w is not None: - yield w - if w is not None: - data = interpret_text_event(codepoint, mods, w) - if data: - w.write_to_child(data) + data = interpret_text_event(codepoint, mods, w) + if data: + w.write_to_child(data) - @callback def on_key(self, window, key, scancode, action, mods): is_key_pressed[key] = action == GLFW_PRESS self.start_cursor_blink() @@ -327,7 +259,6 @@ class Boss(Thread): window = self.active_window if window is None: return - yield window if func is not None: f = getattr(tab, func, getattr(window, func, None)) if f is not None: @@ -342,17 +273,15 @@ class Boss(Thread): if data: window.write_to_child(data) - @callback def on_focus(self, window, focused): self.window_is_focused = focused w = self.active_window if w is not None: - yield w w.focus_changed(focused) def display_scrollback(self, data): if self.opts.scrollback_in_new_tab: - self.queue_ui_action(self.display_scrollback_in_new_tab, data) + self.display_scrollback_in_new_tab(data) else: tab = self.active_tab if tab is not None: @@ -371,7 +300,6 @@ class Boss(Thread): th = self.current_tab_bar_height return th > 0 and y >= viewport_size.height - th - @callback def on_mouse_button(self, window, button, action, mods): mouse_button_pressed[button] = action == GLFW_PRESS self.show_mouse_cursor() @@ -385,7 +313,6 @@ class Boss(Thread): focus_moved = False old_focus = self.active_window tab = self.active_tab - yield if button == GLFW_MOUSE_BUTTON_1 and w is not old_focus: tab.set_active_window(w) focus_moved = True @@ -395,28 +322,22 @@ class Boss(Thread): w.focus_changed(True) w.on_mouse_button(button, action, mods) - @callback def on_mouse_move(self, window, xpos, ypos): mouse_cursor_pos[:2] = xpos, ypos = int( xpos * viewport_size.x_ratio), int(ypos * viewport_size.y_ratio) self.show_mouse_cursor() w = self.window_for_pos(xpos, ypos) if w is not None: - yield w w.on_mouse_move(xpos, ypos) else: self.change_mouse_cursor(self.in_tab_bar(ypos)) - @callback def on_mouse_scroll(self, window, x, y): self.show_mouse_cursor() w = self.window_for_pos(*mouse_cursor_pos) if w is not None: - yield w w.on_mouse_scroll(x, y) - # GUI thread API {{{ - def show_mouse_cursor(self): self.glfw_window.set_input_mode(GLFW_CURSOR, GLFW_CURSOR_NORMAL) if self.opts.mouse_hide_wait > 0: @@ -446,8 +367,6 @@ class Boss(Thread): self.cursor_blinking = False def render(self): - if self.pending_resize: - return if self.resize_gl_viewport: glViewport(0, 0, viewport_size.width, viewport_size.height) self.resize_gl_viewport = False @@ -526,22 +445,22 @@ class Boss(Thread): if text: w = self.active_window if w is not None: - self.queue_action(w.paste, text) + w.paste(text) def next_tab(self): - self.queue_action(self.tab_manager.next_tab) + self.tab_manager.next_tab() def previous_tab(self): - self.queue_action(self.tab_manager.next_tab, -1) + self.tab_manager.next_tab(-1) def new_tab(self): self.tab_manager.new_tab() def move_tab_forward(self): - self.queue_action(self.tab_manager.move_tab, 1) + self.tab_manager.move_tab(1) def move_tab_backward(self): - self.queue_action(self.tab_manager.move_tab, -1) + self.tab_manager.move_tab(-1) def display_scrollback_in_new_tab(self, data): self.tab_manager.new_tab( diff --git a/kitty/char_grid.py b/kitty/char_grid.py index 403fc00f8..6fa48f3be 100644 --- a/kitty/char_grid.py +++ b/kitty/char_grid.py @@ -7,7 +7,6 @@ import sys from collections import namedtuple from ctypes import addressof, memmove, sizeof from enum import Enum -from threading import Lock from .config import build_ansi_color_table, defaults from .constants import ( @@ -158,7 +157,6 @@ class CharGrid: url_pat = re.compile('(?:http|https|file|ftp)://\S+', re.IGNORECASE) def __init__(self, screen, opts): - self.buffer_lock = Lock() self.vao_id = None self.current_selection = Selection() self.last_rendered_selection = None @@ -199,11 +197,10 @@ class CharGrid: rt = (GLuint * (self.screen_geometry.ynum * self.screen_geometry.xnum * DATA_CELL_SIZE)) self.main_sprite_map = rt() self.scroll_sprite_map = rt() - with self.buffer_lock: - self.render_buf = rt() - self.selection_buf = (GLfloat * (self.screen_geometry.ynum * self.screen_geometry.xnum))() - self.render_buf_is_dirty = True - self.current_selection.clear() + self.render_buf = rt() + self.selection_buf = (GLfloat * (self.screen_geometry.ynum * self.screen_geometry.xnum))() + self.render_buf_is_dirty = True + self.current_selection.clear() def change_colors(self, changes): dirtied = False @@ -243,12 +240,11 @@ class CharGrid: addressof(self.main_sprite_map), self.scrolled_by, addressof(self.scroll_sprite_map)) data = self.scroll_sprite_map if self.scrolled_by else self.main_sprite_map - with self.buffer_lock: - if is_dirty: - self.current_selection.clear() - memmove(self.render_buf, data, sizeof(type(data))) - self.render_data = self.screen_geometry - self.render_buf_is_dirty = True + if is_dirty: + self.current_selection.clear() + memmove(self.render_buf, data, sizeof(type(data))) + self.render_data = self.screen_geometry + self.render_buf_is_dirty = True if cursor_changed: c = self.screen.cursor self.current_cursor = Cursor(c.x, c.y, c.shape, c.blink) @@ -265,19 +261,18 @@ class CharGrid: x = 0 if mx <= cell_size.width else self.screen.columns - 1 y = 0 if my <= cell_size.height else self.screen.lines - 1 ps = None - with self.buffer_lock: - if is_press: - self.current_selection.start_x = self.current_selection.end_x = x - self.current_selection.start_y = self.current_selection.end_y = y - self.current_selection.start_scrolled_by = self.current_selection.end_scrolled_by = self.scrolled_by - self.current_selection.in_progress = True - elif self.current_selection.in_progress: - self.current_selection.end_x = x - self.current_selection.end_y = y - self.current_selection.end_scrolled_by = self.scrolled_by - if is_press is False: - self.current_selection.in_progress = False - ps = self.text_for_selection() + if is_press: + self.current_selection.start_x = self.current_selection.end_x = x + self.current_selection.start_y = self.current_selection.end_y = y + self.current_selection.start_scrolled_by = self.current_selection.end_scrolled_by = self.scrolled_by + self.current_selection.in_progress = True + elif self.current_selection.in_progress: + self.current_selection.end_x = x + self.current_selection.end_y = y + self.current_selection.end_scrolled_by = self.scrolled_by + if is_press is False: + self.current_selection.in_progress = False + ps = self.text_for_selection() if ps and ps.strip(): set_primary_selection(ps) @@ -370,21 +365,20 @@ class CharGrid: return s.text(self.screen.linebuf, self.screen.historybuf) def prepare_for_render(self, cell_program): - with self.buffer_lock: - sg = self.render_data - if sg is None: - return - if self.vao_id is None: - self.vao_id = cell_program.create_sprite_map() - start, end = sel = self.current_selection.limits(self.scrolled_by, self.screen.lines, self.screen.columns) - selection_changed = sel != self.last_rendered_selection - if selection_changed: - self.screen.apply_selection(addressof(self.selection_buf), start[0], start[1], end[0], end[1], len(self.selection_buf)) - cell_program.send_vertex_data(self.vao_id, self.selection_buf, bufnum=1) - self.last_rendered_selection = sel - if self.render_buf_is_dirty: - cell_program.send_vertex_data(self.vao_id, self.render_buf) - self.render_buf_is_dirty = False + sg = self.render_data + if sg is None: + return + if self.vao_id is None: + self.vao_id = cell_program.create_sprite_map() + start, end = sel = self.current_selection.limits(self.scrolled_by, self.screen.lines, self.screen.columns) + selection_changed = sel != self.last_rendered_selection + if selection_changed: + self.screen.apply_selection(addressof(self.selection_buf), start[0], start[1], end[0], end[1], len(self.selection_buf)) + cell_program.send_vertex_data(self.vao_id, self.selection_buf, bufnum=1) + self.last_rendered_selection = sel + if self.render_buf_is_dirty: + cell_program.send_vertex_data(self.vao_id, self.render_buf) + self.render_buf_is_dirty = False return sg def render_cells(self, sg, cell_program, sprites, invert_colors=False): diff --git a/kitty/constants.py b/kitty/constants.py index 679b45600..381b336ac 100644 --- a/kitty/constants.py +++ b/kitty/constants.py @@ -3,7 +3,6 @@ # License: GPL v3 Copyright: 2016, Kovid Goyal import os -import threading import pwd import ctypes import sys @@ -64,10 +63,6 @@ def wakeup(): os.write(get_boss.boss.write_wakeup_fd, b'1') -def queue_action(func, *args): - get_boss.boss.queue_action(func, *args) - - is_key_pressed = defaultdict(lambda: False) mouse_button_pressed = defaultdict(lambda: False) mouse_cursor_pos = [0, 0] @@ -76,7 +71,6 @@ cell_size = ViewportSize() base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) terminfo_dir = os.path.join(base_dir, 'terminfo') logo_data_file = os.path.join(base_dir, 'logo', 'kitty.rgba') -main_thread = threading.current_thread() shell_path = pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh' GLint = ctypes.c_int if ctypes.sizeof(ctypes.c_int) == 4 else ctypes.c_long diff --git a/kitty/fonts/freetype.py b/kitty/fonts/freetype.py index ddf7d7ff9..6566cb2a6 100644 --- a/kitty/fonts/freetype.py +++ b/kitty/fonts/freetype.py @@ -8,7 +8,6 @@ import unicodedata from collections import namedtuple from functools import lru_cache from itertools import chain -from threading import Lock from kitty.fast_data_types import FT_PIXEL_MODE_GRAY, Face from kitty.fonts.box_drawing import render_missing_glyph @@ -114,7 +113,6 @@ def set_font_family(opts, override_font_size=None): CharBitmap = namedtuple( 'CharBitmap', 'data bearingX bearingY advance rows columns' ) -freetype_lock = Lock() def render_to_bitmap(font, face, text): @@ -162,26 +160,25 @@ def render_char(text, bold=False, italic=False, width=1): elif italic: key = 'italic' ch = text[0] - with freetype_lock: - font = symbol_map.get(ch) - if font is None or not font.face.get_char_index(ch): - font = current_font_family.get(key) or current_font_family['regular'] - face = font.face - if not face.get_char_index(ch): - try: - font = font_for_char(ch, bold, italic) - except FontNotFound: - font = font_for_char( - ch, bold, italic, allow_bitmaped_fonts=True - ) - face = alt_face_cache.get(font) - if face is None: - face = alt_face_cache[font] = Face(font.face, font.index) - if face.is_scalable: - set_char_size(face, **cff_size) - else: - face = font.face - return render_using_face(font, face, text, width, italic, bold) + font = symbol_map.get(ch) + if font is None or not font.face.get_char_index(ch): + font = current_font_family.get(key) or current_font_family['regular'] + face = font.face + if not face.get_char_index(ch): + try: + font = font_for_char(ch, bold, italic) + except FontNotFound: + font = font_for_char( + ch, bold, italic, allow_bitmaped_fonts=True + ) + face = alt_face_cache.get(font) + if face is None: + face = alt_face_cache[font] = Face(font.face, font.index) + if face.is_scalable: + set_char_size(face, **cff_size) + else: + face = font.face + return render_using_face(font, face, text, width, italic, bold) def place_char_in_cell(bitmap_char): diff --git a/kitty/main.py b/kitty/main.py index ff1fea48c..ccc45d22d 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -8,7 +8,6 @@ import os import sys from contextlib import contextmanager from gettext import gettext as _ -from queue import Empty from .boss import Boss from .config import ( @@ -168,16 +167,6 @@ def clear_buffers(window, opts): def dispatch_pending_calls(boss): - while True: - try: - func, args = boss.pending_ui_thread_calls.get_nowait() - except Empty: - break - try: - func(*args) - except Exception: - import traceback - safe_print(traceback.format_exc()) boss.ui_timers.call() diff --git a/kitty/tabs.py b/kitty/tabs.py index 296d27e0c..c71e7d3ee 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -5,14 +5,13 @@ from collections import deque, namedtuple from ctypes import addressof from functools import partial -from queue import Queue, Empty from .borders import Borders from .char_grid import calculate_gl_geometry, render_cells from .child import Child from .config import build_ansi_color_table from .constants import ( - GLuint, WindowGeometry, appname, cell_size, get_boss, queue_action, + GLuint, WindowGeometry, appname, cell_size, get_boss, shell_path, viewport_size ) from .fast_data_types import ( @@ -47,14 +46,14 @@ class Tab: l = self.enabled_layouts[0] self.current_layout = all_layouts[l](opts, self.borders.border_width, self.windows) if special_window is None: - queue_action(self.new_window) + self.new_window() else: - queue_action(self.new_special_window, special_window) + self.new_special_window(special_window) else: self.cwd = session_tab.cwd or args.directory l = session_tab.layout self.current_layout = all_layouts[l](opts, self.borders.border_width, self.windows) - queue_action(self.startup, session_tab) + self.startup(session_tab) def startup(self, session_tab): for cmd in session_tab.windows: @@ -211,7 +210,6 @@ class TabBar: def __init__(self, data, opts): self.num_tabs = 1 self.cell_width = 1 - self.queue = Queue() self.vao_id = None self.render_buf = self.selection_buf = None self.selection_buf_changed = True @@ -233,7 +231,6 @@ class TabBar: self.active_fg = as_rgb(color_as_int(opts.active_tab_foreground)) def layout(self, viewport_width, viewport_height, cell_width, cell_height): - ' Must be called in the child thread ' self.cell_width = cell_width s = self.screen ncells = viewport_width // cell_width @@ -252,18 +249,12 @@ class TabBar: self.screen_geometry = calculate_gl_geometry(g, viewport_width, viewport_height, cell_width, cell_height) self.update() - def update(self): - ' Must be called in the child thread ' + def update(self, data): if self.render_buf is None: return s = self.screen s.cursor.x = 0 s.erase_in_line(2, False) - while True: - try: - self.current_data = self.queue.get_nowait() - except Empty: - break max_title_length = (self.screen_geometry.xnum // len(self.current_data)) - 1 cr = [] @@ -290,17 +281,7 @@ class TabBar: self.dirty = True glfw_post_empty_event() - def schedule_layout(self, data): - ' Must be called in the GUI thread ' - queue_action(self.layout, *data) - - def schedule_update(self, data): - ' Must be called in the GUI thread ' - self.queue.put(data) - queue_action(self.update) - def render(self, cell_program, sprites): - ' Must be called in the GUI thread ' if self.render_buf is not None: sprites.render_dirty_sprites() if self.vao_id is None: @@ -314,7 +295,6 @@ class TabBar: render_cells(self.vao_id, self.screen_geometry, cell_program, sprites, self.screen.color_profile) def tab_at(self, x): - ' Must be called in the GUI thread ' x = (x - self.window_geometry.left) // self.cell_width for i, (a, b) in enumerate(self.cell_ranges): if a <= x <= b: @@ -328,15 +308,15 @@ class TabManager: self.tabs = [Tab(opts, args, self.title_changed, t) for t in startup_session.tabs] self.active_tab_idx = startup_session.active_tab_idx self.tab_bar = TabBar(self.tab_bar_data, opts) - self.tab_bar.schedule_layout(self.tab_bar_layout_data) + self.tab_bar.layout(*self.tab_bar_layout_data) def update_tab_bar(self): if len(self.tabs) > 1: - self.tab_bar.schedule_update(self.tab_bar_data) + self.tab_bar.update(self.tab_bar_data) def resize(self, only_tabs=False): if not only_tabs: - self.tab_bar.schedule_layout(self.tab_bar_layout_data) + self.tab_bar.layout(*self.tab_bar_layout_data) for tab in self.tabs: tab.relayout() @@ -375,27 +355,24 @@ class TabManager: self.update_tab_bar() def new_tab(self, special_window=None): - ' Must be called in the GUI thread ' needs_resize = len(self.tabs) == 1 self.active_tab_idx = len(self.tabs) self.tabs.append(Tab(self.opts, self.args, self.title_changed, special_window=special_window)) self.update_tab_bar() if needs_resize: - queue_action(get_boss().tabbar_visibility_changed) + get_boss().tabbar_visibility_changed() def remove(self, tab): - ' Must be called in the GUI thread ' needs_resize = len(self.tabs) == 2 self.tabs.remove(tab) self.active_tab_idx = max(0, min(self.active_tab_idx, len(self.tabs) - 1)) self.update_tab_bar() tab.destroy() if needs_resize: - queue_action(get_boss().tabbar_visibility_changed) + get_boss().tabbar_visibility_changed() @property def tab_bar_layout_data(self): - ' Must be called in the GUI thread ' return viewport_size.width, viewport_size.height, cell_size.width, cell_size.height @property @@ -417,7 +394,6 @@ class TabManager: return self.tab_bar.blank_rects if len(self.tabs) < 2 else () def render(self, cell_program, sprites): - ' Must be called in the GUI thread ' if len(self.tabs) < 2: return self.tab_bar.render(cell_program, sprites) diff --git a/kitty/window.py b/kitty/window.py index a0aee3624..8f9f0d85b 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -91,12 +91,10 @@ class Window: get_boss().close_window(self) def destroy(self): - self.destroyed = True - self.child.hangup() - self.child.get_child_status() # Ensure child does not become zombie - # At this point this window can still render to screen using its - # existing buffers in char_grid. The rest of the cleanup must be - # performed in the GUI thread. + if not self.destroyed: + self.destroyed = True + self.child.hangup() + self.child.get_child_status() # Ensure child does not become zombie def write_ready(self): while self.write_buf: @@ -125,8 +123,7 @@ class Window: pass # failure to beep is not critical if self.opts.visual_bell_duration > 0: self.start_visual_bell_at = monotonic() - tm = get_boss() - tm.queue_ui_action(tm.request_attention) + get_boss().request_attention() glfw_post_empty_event() def use_utf8(self, on): @@ -237,8 +234,7 @@ class Window: is_key_pressed[GLFW_KEY_LEFT_SHIFT] or is_key_pressed[GLFW_KEY_RIGHT_SHIFT]) x, y = max(0, x - self.geometry.left), max(0, y - self.geometry.top) self.last_mouse_cursor_pos = x, y - tm = get_boss() - tm.queue_ui_action(tm.change_mouse_cursor, self.char_grid.has_url_at(x, y)) + get_boss().change_mouse_cursor(self.char_grid.has_url_at(x, y)) if send_event: x, y = self.char_grid.cell_for_pos(x, y) if x is not None: @@ -320,8 +316,7 @@ class Window: def copy_to_clipboard(self): text = self.char_grid.text_for_selection() if text: - tm = get_boss() - tm.queue_ui_action(tm.glfw_window.set_clipboard_string, text) + get_boss().glfw_window.set_clipboard_string(text) def scroll_line_up(self): if self.screen.is_main_linebuf():