Remove all python threading related code

This commit is contained in:
Kovid Goyal
2017-08-31 13:11:46 +05:30
parent fa30a95b46
commit 0365828a5b
8 changed files with 112 additions and 252 deletions

View File

@@ -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)

View File

@@ -2,23 +2,20 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
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(

View File

@@ -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):

View File

@@ -3,7 +3,6 @@
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
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

View File

@@ -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):

View File

@@ -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()

View File

@@ -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)

View File

@@ -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():