Clean up the timer handling code

This commit is contained in:
Kovid Goyal 2016-11-29 21:46:45 +05:30
parent 2d3b7d15c7
commit 7dc3b986be
2 changed files with 70 additions and 41 deletions

View File

@ -9,9 +9,7 @@ import signal
import struct
from collections import deque
from functools import partial
from itertools import count
from threading import Thread
from time import monotonic
from queue import Queue, Empty
from .child import Child
@ -26,13 +24,11 @@ from .char_grid import cursor_shader, cell_shader
from .keys import interpret_text_event, interpret_key_event, get_shortcut
from .layout import Stack
from .shaders import Sprites, ShaderProgram
from .timers import Timers
from .utils import handle_unix_signals
from .window import Window
timer_id = count()
class Tab:
def __init__(self, opts, args):
@ -108,7 +104,7 @@ class TabManager(Thread):
self.glfw_window_title = None
self.current_tab_bar_height = 0
self.action_queue = Queue()
self.pending_resize = None
self.pending_resize = True
self.resize_gl_viewport = False
self.shutting_down = False
self.screen_update_delay = opts.repaint_delay / 1000.0
@ -116,7 +112,7 @@ class TabManager(Thread):
self.read_wakeup_fd, self.write_wakeup_fd = os.pipe2(os.O_NONBLOCK | os.O_CLOEXEC)
self.read_dispatch_map = {self.signal_fd: self.signal_received, self.read_wakeup_fd: self.on_wakeup}
self.all_writers = []
self.timers = []
self.timers = Timers()
self.write_dispatch_map = {}
set_tab_manager(self)
cell_size.width, cell_size.height = set_font_family(opts.font_family, opts.font_size)
@ -211,13 +207,6 @@ class TabManager(Thread):
if len(self.tabs) == 0:
self.shutdown()
def call_after(self, delay, callback):
tid = next(timer_id)
self.timers.append((monotonic() + delay, tid, callback))
if len(self.timers) > 1:
self.timers.sort()
return tid
def run(self):
if self.args.profile:
import cProfile
@ -235,45 +224,27 @@ class TabManager(Thread):
while not self.shutting_down:
all_readers = list(self.read_dispatch_map)
all_writers = [w.child_fd for w in self.iterwindows() if w.write_buf]
timeout = max(0, self.timers[0][0] - monotonic()) if self.timers else None
readers, writers, _ = select.select(all_readers, all_writers, [], timeout)
readers, writers, _ = select.select(all_readers, all_writers, [], self.timers.timeout())
for r in readers:
self.read_dispatch_map[r]()
for w in writers:
self.write_dispatch_map[w]()
timers = []
callbacks = set()
for epoch, tid, callback in self.timers:
if epoch <= monotonic():
callback()
else:
timers.append((epoch, tid, callback))
callbacks.add(callback)
update_at = monotonic() + self.screen_update_delay
before = len(timers)
self.timers()
for w in self.iterwindows():
if w.screen.is_dirty() and w.update_screen not in callbacks:
timers.append((update_at, next(timer_id), w.update_screen))
if len(timers) > before:
timers.sort()
self.timers = timers
if w.screen.is_dirty():
self.timers.add_if_missing(self.screen_update_delay, w.update_screen)
def on_window_resize(self, window, w, h):
# debounce resize events
self.pending_resize = [monotonic(), w, h]
self.call_after(0.02, self.apply_pending_resize)
self.timers.add(0.02, self.apply_pending_resize, w, h)
self.pending_resize = True
def apply_pending_resize(self):
if self.pending_resize is None:
return
if monotonic() - self.pending_resize[0] < 0.02:
self.call_after(0.02, self.apply_pending_resize)
return
viewport_size.width, viewport_size.height = self.pending_resize[1:]
def apply_pending_resize(self, w, h):
viewport_size.width, viewport_size.height = w, h
for tab in self.tabs:
tab.relayout()
self.pending_resize = None
self.resize_gl_viewport = True
self.pending_resize = False
glfw_post_empty_event()
@property

58
kitty/timers.py Normal file
View File

@ -0,0 +1,58 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from collections import namedtuple
from operator import itemgetter
from time import monotonic
Event = namedtuple('Event', 'at callback args')
get_at = itemgetter(0)
class Timers:
def __init__(self):
self.timers = []
def add(self, delay, callback, *args):
self.remove(callback)
self.timers.append(Event(monotonic() + delay, callback, args))
self.timers.sort(key=get_at)
def add_if_missing(self, delay, callback, *args):
for ev in self.timers:
if ev.callback is callback:
break
else:
self.add(delay, callback, *args)
def remove(self, callback):
for i, ev in enumerate(self.timers):
if ev.callback is callback:
break
else:
return
del self.timers[i]
def timeout(self):
if self.timers:
return max(0, self.timers[0][0] - monotonic())
def __call__(self):
if self.timers:
now = monotonic()
for i, ev in enumerate(self.timers):
if ev[0] <= now:
try:
ev.callback(*ev.args)
except Exception:
import traceback
traceback.print_exc()
else:
break
else:
del self.timers[:]
return
del self.timers[:i]