Handle window resizing correctly

This commit is contained in:
Kovid Goyal 2016-10-26 10:46:20 +05:30
parent cc1a277eeb
commit 7161406ff3
4 changed files with 73 additions and 63 deletions

View File

@ -35,14 +35,14 @@ class Boss(Thread):
pending_title_change = pending_icon_change = None
pending_color_changes = {}
def __init__(self, window, opts, args):
def __init__(self, window, window_width, window_height, opts, args):
Thread.__init__(self, name='ChildMonitor')
self.window, self.opts = window, opts
self.action_queue = Queue()
self.read_wakeup_fd, self.write_wakeup_fd = os.pipe2(os.O_NONBLOCK | os.O_CLOEXEC)
self.tracker = ChangeTracker(self.mark_dirtied)
self.char_grid = CharGrid(opts)
self.screen = Screen(self.opts, self.tracker, self)
self.char_grid = CharGrid(self.screen, opts, window_width, window_height)
sclass = DebugStream if args.dump_commands else Stream
self.stream = sclass(self.screen)
self.write_buf = memoryview(b'')
@ -53,7 +53,23 @@ class Boss(Thread):
resize_pty(80, 24)
def on_window_resize(self, window, w, h):
self.char_grid.on_resize(window, w, h)
self.queue_action(self.resize_screen, w, h)
def resize_screen(self, w, h):
self.char_grid.resize_screen(w, h)
def apply_opts(self, opts):
self.opts = opts
self.queue_action(self.apply_opts_to_screen)
def apply_opts_to_screen(self):
self.screen.apply_opts(self.opts)
self.char_grid.apply_opts(self.opts)
self.char_grid.dirty_everything()
def queue_action(self, func, *args):
self.action_queue.put((func, args))
self.wakeup()
def render(self):
if self.pending_title_change is not None:
@ -76,7 +92,7 @@ class Boss(Thread):
func, args = self.action_queue.get_nowait()
except Empty:
break
getattr(self, func)(*args)
func(*args)
def run(self):
while not self.shutting_down:
@ -130,15 +146,13 @@ class Boss(Thread):
def write_to_child(self, data):
if data:
self.action_queue.put(('queue_write', data))
self.wakeup()
self.queue_action(self.queue_write, data)
def queue_write(self, data):
self.write_buf = memoryview(self.write_buf.tobytes() + data)
def mark_dirtied(self):
self.action_queue.put(('update_screen', ()))
self.wakeup()
self.queue_action(self.update_screen)
def update_screen(self):
changes = self.tracker.consolidate_changes()
@ -155,7 +169,7 @@ class Boss(Thread):
def change_default_color(self, which, value):
self.pending_color_changes[which] = value
self.action_queue.put(('change_colors', ()))
self.queue_action(self.change_colors)
def change_colors(self):
self.char_grid.change_colors(self.pending_color_changes)

View File

@ -7,66 +7,62 @@ from threading import Lock
from .config import build_ansi_color_tables, to_color
from .fonts import set_font_family
from OpenGL.arrays import ArrayDatatype
from OpenGL.GL import (
GL_ARRAY_BUFFER,
GL_COLOR_BUFFER_BIT, GL_COMPILE_STATUS,
GL_FALSE, GL_FLOAT, GL_FRAGMENT_SHADER,
GL_LINK_STATUS, GL_RENDERER,
GL_SHADING_LANGUAGE_VERSION,
GL_STATIC_DRAW, GL_TEXTURE_2D, GL_TRIANGLES,
GL_TRUE, GL_UNPACK_ALIGNMENT, GL_VENDOR, GL_VERSION,
GL_VERTEX_SHADER, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T,
GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER,
GL_LINEAR, GL_RGB, GL_RGBA, GL_UNSIGNED_BYTE, GL_TEXTURE0,
GL_REPEAT,
glActiveTexture, glAttachShader,
glBindBuffer, glBindTexture, glBindVertexArray,
glBufferData, glClear, glClearColor,
glCompileShader, glCreateProgram,
glCreateShader, glDeleteProgram,
glDeleteShader, glDrawArrays,
glEnableVertexAttribArray, glGenBuffers, glGenTextures,
glGenVertexArrays, glGetAttribLocation,
glGetProgramInfoLog, glGetProgramiv,
glGetShaderInfoLog, glGetShaderiv, glGetString,
glGetUniformLocation, glLinkProgram, glPixelStorei,
glShaderSource, glTexImage2D, glTexParameteri, glUniform1i, glUseProgram,
glVertexAttribPointer, glViewport)
import OpenGL.GL as gl
class CharGrid:
def __init__(self, opts):
def __init__(self, screen, opts, window_width, window_height):
self.width, self.height = window_width, window_height
self.screen = screen
self.apply_opts(opts)
self.lock = Lock()
self.dirty_everything()
self.default_bg, self.default_fg = self.original_bg, self.original_fg
self.resize_lock = Lock()
self.apply_resize_to_screen(self.width, self.height)
def apply_clear_color(self):
bg = self.default_bg
glClearColor(bg[0]/255, bg[1]/255, bg[2]/255, 1)
def dirty_everything(self):
self.cell_resize_pending = True
self.clear_color_changed = True
self.resize_pending = self.width, self.height
def apply_opts(self, opts):
self.opts = opts
build_ansi_color_tables(opts)
self.opts = opts
self.default_bg = self.original_bg = opts.background
self.default_fg = self.original_fg = opts.foreground
self.original_bg = opts.background
self.original_fg = opts.foreground
self.cell_width, self.cell_height = set_font_family(opts.font_family, opts.font_size)
self.apply_clear_color()
def on_resize(self, window, w, h):
glViewport(0, 0, w, h)
self.do_layout(w, h)
def apply_resize_to_screen(self, w, h):
cells_per_line = w // self.cell_width
lines_per_screen = h // self.cell_height
self.screen.resize(lines_per_screen, cells_per_line)
def resize_screen(self, w, h):
' Screen was resized by the user (called in non-UI thread) '
with self.resize_lock:
self.apply_resize_to_screen(w, h)
self.resize_pending = w, h
def do_layout(self, w, h):
pass
def redraw(self):
pass
self.width, self.height = w, h
self.cells_per_line = w // self.cell_width
self.lines_per_screen = h // self.cell_height
if self.cell_resize_pending:
self.cell_resize_pending = False
def render(self):
with self.lock:
glClear(GL_COLOR_BUFFER_BIT)
with self.resize_lock:
if self.resize_pending:
self.do_layout(*self.resize_pending)
gl.glViewport(0, 0, self.width, self.height)
self.resize_pending = None
if self.clear_color_changed:
bg = self.default_bg
self.clear_color_changed = False
gl.glClearColor(bg[0]/255, bg[1]/255, bg[2]/255, 1)
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
def change_colors(self, changes):
dirtied = False
@ -81,8 +77,9 @@ class CharGrid:
setattr(self, 'default_' + which, val)
dirtied = True
if dirtied:
self.apply_clear_color()
self.redraw()
self.clear_color_changed = True
self.update_screen()
def update_screen(self, changes):
self.redraw()
def update_screen(self, changes=None):
if changes is None:
changes = {'screen': True}

View File

@ -3,7 +3,7 @@
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import os
from PyQt5.QtCore import QStandardPaths
import threading
appname = 'kitty'
version = (0, 1, 0)
@ -15,10 +15,7 @@ def _get_config_dir():
if 'KITTY_CONFIG_DIRECTORY' in os.environ:
return os.path.abspath(os.path.expanduser(os.environ['VISE_CONFIG_DIRECTORY']))
candidate = QStandardPaths.writableLocation(QStandardPaths.ConfigLocation)
if not candidate:
raise RuntimeError(
'Failed to find path for application config directory')
candidate = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME') or '~/.config'))
ans = os.path.join(candidate, appname)
try:
os.makedirs(ans)
@ -29,3 +26,4 @@ config_dir = _get_config_dir()
del _get_config_dir
terminfo_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'terminfo')
main_thread = threading.current_thread()

View File

@ -43,15 +43,16 @@ def setup_opengl():
def run_app(opts, args):
setup_opengl()
window_width = window_height = 1024
window = glfw.glfwCreateWindow(
1024, 1024, args.cls.encode('utf-8'), None, None)
window_width, window_height, args.cls.encode('utf-8'), None, None)
if not window:
raise SystemExit("glfwCreateWindow failed")
glfw.glfwSetWindowTitle(window, appname.encode('utf-8'))
try:
glfw.glfwMakeContextCurrent(window)
glfw.glfwSwapInterval(1)
boss = Boss(window, opts, args)
boss = Boss(window, window_width, window_height, opts, args)
glfw.glfwSetFramebufferSizeCallback(window, boss.on_window_resize)
boss.start()