Remove all python threading related code
This commit is contained in:
@@ -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)
|
||||
|
||||
115
kitty/boss.py
115
kitty/boss.py
@@ -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(
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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():
|
||||
|
||||
Reference in New Issue
Block a user