Move render call for tab bar to C code

This commit is contained in:
Kovid Goyal 2017-09-12 20:23:26 +05:30
parent 2e0cbb88bb
commit 943a1575ad
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 137 additions and 32 deletions

View File

@ -15,8 +15,8 @@ from .constants import (
from .fast_data_types import ( from .fast_data_types import (
GLFW_CURSOR, GLFW_CURSOR_HIDDEN, GLFW_CURSOR_NORMAL, GLFW_MOUSE_BUTTON_1, GLFW_CURSOR, GLFW_CURSOR_HIDDEN, GLFW_CURSOR_NORMAL, GLFW_MOUSE_BUTTON_1,
GLFW_PRESS, GLFW_REPEAT, ChildMonitor, Timers as _Timers, GLFW_PRESS, GLFW_REPEAT, ChildMonitor, Timers as _Timers,
destroy_sprite_map, glfw_post_empty_event, layout_sprite_map, destroy_global_data, destroy_sprite_map, glfw_post_empty_event,
resize_gl_viewport layout_sprite_map, resize_gl_viewport
) )
from .fonts.render import render_cell_wrapper, set_font_family from .fonts.render import render_cell_wrapper, set_font_family
from .keys import ( from .keys import (
@ -365,7 +365,6 @@ class Boss:
self.glfw_window.set_title(self.glfw_window_title) self.glfw_window.set_title(self.glfw_window_title)
if isosx: if isosx:
cocoa_update_title(self.glfw_window_title) cocoa_update_title(self.glfw_window_title)
self.tab_manager.render()
for window in tab.visible_windows(): for window in tab.visible_windows():
if not window.needs_layout: if not window.needs_layout:
window.char_grid.render_cells() window.char_grid.render_cells()
@ -408,6 +407,7 @@ class Boss:
t.destroy() t.destroy()
del self.tab_manager del self.tab_manager
destroy_sprite_map() destroy_sprite_map()
destroy_global_data()
del self.glfw_window del self.glfw_window
def paste_from_clipboard(self): def paste_from_clipboard(self):

View File

@ -424,6 +424,7 @@ pyset_iutf8(ChildMonitor *self, PyObject *args) {
static double last_render_at = -DBL_MAX; static double last_render_at = -DBL_MAX;
draw_borders_func draw_borders = NULL; draw_borders_func draw_borders = NULL;
draw_cells_func draw_cells = NULL;
static inline bool static inline bool
render(ChildMonitor *self, double *timeout) { render(ChildMonitor *self, double *timeout) {
@ -432,6 +433,9 @@ render(ChildMonitor *self, double *timeout) {
double time_since_last_render = now - last_render_at; double time_since_last_render = now - last_render_at;
if (time_since_last_render > self->repaint_delay) { if (time_since_last_render > self->repaint_delay) {
draw_borders(); draw_borders();
#define TD global_state.tab_bar_render_data
if (TD.screen && global_state.num_tabs > 1) draw_cells(TD.vao_idx, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen);
#undef TD
ret = PyObject_CallFunctionObjArgs(self->render_func, NULL); ret = PyObject_CallFunctionObjArgs(self->render_func, NULL);
if (ret == NULL) { PyErr_Print(); return false; } if (ret == NULL) { PyErr_Print(); return false; }
else Py_DECREF(ret); else Py_DECREF(ret);

View File

@ -22,6 +22,7 @@
#define MIN(x, y) (((x) > (y)) ? (y) : (x)) #define MIN(x, y) (((x) > (y)) ? (y) : (x))
#define xstr(s) str(s) #define xstr(s) str(s)
#define str(s) #s #define str(s) #s
#define fatal(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); exit(EXIT_FAILURE); }
typedef uint32_t char_type; typedef uint32_t char_type;
typedef uint32_t color_type; typedef uint32_t color_type;
@ -292,8 +293,22 @@ typedef struct {
bool enable_audio_bell; bool enable_audio_bell;
} Options; } Options;
typedef struct {
unsigned int id;
} Tab;
typedef struct {
ssize_t vao_idx;
float xstart, ystart, dx, dy;
Screen *screen;
} ScreenRenderData;
typedef struct { typedef struct {
Options opts; Options opts;
Tab tabs[MAX_CHILDREN];
unsigned int active_tab, num_tabs;
ScreenRenderData tab_bar_render_data;
} GlobalState; } GlobalState;
#ifndef IS_STATE #ifndef IS_STATE
@ -313,6 +328,8 @@ extern GlobalState global_state;
typedef void (*draw_borders_func)(); typedef void (*draw_borders_func)();
extern draw_borders_func draw_borders; extern draw_borders_func draw_borders;
typedef void (*draw_cells_func)(ssize_t, float, float, float, float, Screen *);
extern draw_cells_func draw_cells;
// Global functions // Global functions
Line* alloc_line(); Line* alloc_line();

View File

@ -33,8 +33,6 @@ static char glbuf[4096];
#define GL_STACK_OVERFLOW 0x0503 #define GL_STACK_OVERFLOW 0x0503
#endif #endif
#define fatal(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); exit(EXIT_FAILURE); }
#ifdef ENABLE_DEBUG_GL #ifdef ENABLE_DEBUG_GL
static void static void
check_for_gl_error(int line) { check_for_gl_error(int line) {
@ -596,7 +594,7 @@ create_cell_vao() {
} }
static void static void
draw_cells(ssize_t vao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, Screen *screen) { draw_cells_impl(ssize_t vao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, Screen *screen) {
size_t sz; size_t sz;
void *address; void *address;
bool inverted = screen_invert_colors(screen); bool inverted = screen_invert_colors(screen);
@ -842,7 +840,7 @@ PYWRAP1(draw_cells) {
Screen *screen; Screen *screen;
PA("iffffO", &vao_idx, &xstart, &ystart, &dx, &dy, &screen); PA("iffffO", &vao_idx, &xstart, &ystart, &dx, &dy, &screen);
draw_cells(vao_idx, xstart, ystart, dx, dy, screen); draw_cells_impl(vao_idx, xstart, ystart, dx, dy, screen);
Py_RETURN_NONE; Py_RETURN_NONE;
} }
NO_ARG(destroy_sprite_map) NO_ARG(destroy_sprite_map)
@ -900,6 +898,7 @@ PYWRAP0(check_for_extensions) {
#define MW(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL} #define MW(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL}
static PyMethodDef module_methods[] = { static PyMethodDef module_methods[] = {
{"glewInit", (PyCFunction)glew_init, METH_NOARGS, NULL}, {"glewInit", (PyCFunction)glew_init, METH_NOARGS, NULL},
{"draw_cells", (PyCFunction)pydraw_cells, METH_VARARGS, NULL},
M(compile_program, METH_VARARGS), M(compile_program, METH_VARARGS),
MW(check_for_extensions, METH_NOARGS), MW(check_for_extensions, METH_NOARGS),
MW(create_vao, METH_NOARGS), MW(create_vao, METH_NOARGS),
@ -917,7 +916,6 @@ static PyMethodDef module_methods[] = {
MW(send_borders_rects, METH_VARARGS), MW(send_borders_rects, METH_VARARGS),
MW(init_cell_program, METH_NOARGS), MW(init_cell_program, METH_NOARGS),
MW(create_cell_vao, METH_NOARGS), MW(create_cell_vao, METH_NOARGS),
MW(draw_cells, METH_VARARGS),
MW(layout_sprite_map, METH_VARARGS), MW(layout_sprite_map, METH_VARARGS),
MW(destroy_sprite_map, METH_NOARGS), MW(destroy_sprite_map, METH_NOARGS),
MW(resize_gl_viewport, METH_VARARGS), MW(resize_gl_viewport, METH_VARARGS),
@ -965,6 +963,7 @@ init_shaders(PyObject *module) {
PyModule_AddObject(module, "GL_VERSION_REQUIRED", Py_BuildValue("II", REQUIRED_VERSION_MAJOR, REQUIRED_VERSION_MINOR)); PyModule_AddObject(module, "GL_VERSION_REQUIRED", Py_BuildValue("II", REQUIRED_VERSION_MAJOR, REQUIRED_VERSION_MINOR));
if (PyModule_AddFunctions(module, module_methods) != 0) return false; if (PyModule_AddFunctions(module, module_methods) != 0) return false;
draw_borders = &draw_borders_impl; draw_borders = &draw_borders_impl;
draw_cells = &draw_cells_impl;
return true; return true;
} }
// }}} // }}}

View File

@ -8,15 +8,55 @@
#define IS_STATE #define IS_STATE
#include "data-types.h" #include "data-types.h"
GlobalState global_state = {{0}};
static const Tab EMPTY_TAB = {0};
#define ensure_can_add(array, count, msg) if (count >= sizeof(array)/sizeof(array[0]) - 1) fatal(msg);
#define REMOVER(array, qid, count, empty, structure) { \
size_t capacity = sizeof(array)/sizeof(array[0]); \
for (size_t i = 0; i < count; i++) { \
if (array[i].id == qid) { \
array[i] = empty; \
size_t num_to_right = capacity - count - 1; \
if (num_to_right) memmove(array + i, array + i + 1, num_to_right * sizeof(structure)); \
(count)--; \
} \
}}
static inline void
add_tab(unsigned int id) {
ensure_can_add(global_state.tabs, global_state.num_tabs, "Too many children (add_tab)");
global_state.tabs[global_state.num_tabs] = EMPTY_TAB;
global_state.tabs[global_state.num_tabs].id = id;
global_state.num_tabs++;
}
static inline void
remove_tab(unsigned int id) {
REMOVER(global_state.tabs, id, global_state.num_tabs, EMPTY_TAB, Tab);
}
static inline void
set_active_tab(unsigned int idx) {
global_state.active_tab = idx;
}
static inline void
swap_tabs(unsigned int a, unsigned int b) {
Tab t = global_state.tabs[b];
global_state.tabs[b] = global_state.tabs[a];
global_state.tabs[a] = t;
}
// Python API {{{
#define PYWRAP0(name) static PyObject* py##name(PyObject UNUSED *self) #define PYWRAP0(name) static PyObject* py##name(PyObject UNUSED *self)
#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args) #define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)
#define PYWRAP2(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args, PyObject *kw) #define PYWRAP2(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args, PyObject *kw)
#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL; #define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;
#define ONE_UINT(name) PYWRAP1(name) { name((unsigned int)PyLong_AsUnsignedLong(args)); Py_RETURN_NONE; }
#define TWO_UINT(name) PYWRAP1(name) { unsigned int a, b; PA("II", &a, &b); name(a, b); Py_RETURN_NONE; }
GlobalState global_state = {{0}};
// Python API {{{
PYWRAP1(set_options) { PYWRAP1(set_options) {
#define S(name, convert) { PyObject *ret = PyObject_GetAttrString(args, #name); if (ret == NULL) return NULL; global_state.opts.name = convert(ret); Py_DECREF(ret); } #define S(name, convert) { PyObject *ret = PyObject_GetAttrString(args, #name); if (ret == NULL) return NULL; global_state.opts.name = convert(ret); Py_DECREF(ret); }
S(visual_bell_duration, PyFloat_AsDouble); S(visual_bell_duration, PyFloat_AsDouble);
@ -25,10 +65,36 @@ PYWRAP1(set_options) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
PYWRAP1(set_tab_bar_render_data) {
#define A(name) &(global_state.tab_bar_render_data.name)
Py_CLEAR(global_state.tab_bar_render_data.screen);
PA("iffffO", A(vao_idx), A(xstart), A(ystart), A(dx), A(dy), A(screen));
Py_INCREF(global_state.tab_bar_render_data.screen);
Py_RETURN_NONE;
#undef A
}
PYWRAP0(destroy_global_data) {
Py_CLEAR(global_state.tab_bar_render_data.screen);
Py_RETURN_NONE;
}
ONE_UINT(add_tab)
ONE_UINT(remove_tab)
ONE_UINT(set_active_tab)
TWO_UINT(swap_tabs)
#define M(name, arg_type) {#name, (PyCFunction)name, arg_type, NULL} #define M(name, arg_type) {#name, (PyCFunction)name, arg_type, NULL}
#define MW(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL} #define MW(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL}
static PyMethodDef module_methods[] = { static PyMethodDef module_methods[] = {
MW(set_options, METH_O), MW(set_options, METH_O),
MW(add_tab, METH_O),
MW(remove_tab, METH_O),
MW(set_active_tab, METH_O),
MW(swap_tabs, METH_VARARGS),
MW(set_tab_bar_render_data, METH_VARARGS),
MW(destroy_global_data, METH_NOARGS),
{NULL, NULL, 0, NULL} /* Sentinel */ {NULL, NULL, 0, NULL} /* Sentinel */
}; };

View File

@ -4,17 +4,18 @@
from collections import deque, namedtuple from collections import deque, namedtuple
from functools import partial from functools import partial
from itertools import count
from .borders import Borders from .borders import Borders
from .char_grid import calculate_gl_geometry, render_cells from .char_grid import calculate_gl_geometry
from .child import Child from .child import Child
from .config import build_ansi_color_table from .config import build_ansi_color_table
from .constants import ( from .constants import (
WindowGeometry, appname, cell_size, get_boss, shell_path, WindowGeometry, appname, cell_size, get_boss, shell_path, viewport_size
viewport_size
) )
from .fast_data_types import ( from .fast_data_types import (
DECAWM, Screen, create_cell_vao, glfw_post_empty_event DECAWM, Screen, add_tab, create_cell_vao, glfw_post_empty_event,
remove_tab, set_active_tab, swap_tabs, set_tab_bar_render_data
) )
from .layout import Rect, all_layouts from .layout import Rect, all_layouts
from .utils import color_as_int from .utils import color_as_int
@ -22,16 +23,19 @@ from .window import Window
TabbarData = namedtuple('TabbarData', 'title is_active is_last') TabbarData = namedtuple('TabbarData', 'title is_active is_last')
borders = None borders = None
tab_counter = count()
next(tab_counter)
def SpecialWindow(cmd, stdin=None, override_title=None): def SpecialWindow(cmd, stdin=None, override_title=None):
return (cmd, stdin, override_title) return (cmd, stdin, override_title)
class Tab: class Tab: # {{{
def __init__(self, opts, args, on_title_change, session_tab=None, special_window=None): def __init__(self, opts, args, on_title_change, session_tab=None, special_window=None):
global borders global borders
self.id = next(tab_counter)
self.opts, self.args = opts, args self.opts, self.args = opts, args
self.name = getattr(session_tab, 'name', '') self.name = getattr(session_tab, 'name', '')
self.on_title_change = on_title_change self.on_title_change = on_title_change
@ -199,9 +203,10 @@ class Tab:
def __repr__(self): def __repr__(self):
return 'Tab(title={}, id={})'.format(self.name or self.title, hex(id(self))) return 'Tab(title={}, id={})'.format(self.name or self.title, hex(id(self)))
# }}}
class TabBar: class TabBar: # {{{
def __init__(self, opts): def __init__(self, opts):
self.num_tabs = 1 self.num_tabs = 1
@ -238,7 +243,8 @@ class TabBar:
self.tab_bar_blank_rects = (Rect(0, g.top, g.left, g.bottom), Rect(g.right - 1, g.top, viewport_width, g.bottom)) self.tab_bar_blank_rects = (Rect(0, g.top, g.left, g.bottom), Rect(g.right - 1, g.top, viewport_width, g.bottom))
else: else:
self.tab_bar_blank_rects = () self.tab_bar_blank_rects = ()
self.screen_geometry = calculate_gl_geometry(g, viewport_width, viewport_height, cell_width, cell_height) self.screen_geometry = sg = calculate_gl_geometry(g, viewport_width, viewport_height, cell_width, cell_height)
set_tab_bar_render_data(self.vao_id, sg.xstart, sg.ystart, sg.dx, sg.dy, self.screen)
def update(self, data): def update(self, data):
if self.layout_changed is None: if self.layout_changed is None:
@ -270,18 +276,15 @@ class TabBar:
self.cell_ranges = cr self.cell_ranges = cr
glfw_post_empty_event() glfw_post_empty_event()
def render(self):
if self.layout_changed is not None:
render_cells(self.vao_id, self.screen_geometry, self.screen)
def tab_at(self, x): def tab_at(self, x):
x = (x - self.window_geometry.left) // self.cell_width x = (x - self.window_geometry.left) // self.cell_width
for i, (a, b) in enumerate(self.cell_ranges): for i, (a, b) in enumerate(self.cell_ranges):
if a <= x <= b: if a <= x <= b:
return i return i
# }}}
class TabManager: class TabManager: # {{{
def __init__(self, opts, args): def __init__(self, opts, args):
self.opts, self.args = opts, args self.opts, self.args = opts, args
@ -291,9 +294,22 @@ class TabManager:
self.tab_bar.layout(*self.tab_bar_layout_data) self.tab_bar.layout(*self.tab_bar_layout_data)
self.active_tab_idx = 0 self.active_tab_idx = 0
def _add_tab(self, tab):
add_tab(tab.id)
self.tabs.append(tab)
def _remove_tab(self, tab):
remove_tab(tab.id)
self.tabs.remove(tab)
def _set_active_tab(self, idx):
self.active_tab_idx = idx
set_active_tab(idx)
def init(self, startup_session): def init(self, startup_session):
self.tabs = [Tab(self.opts, self.args, self.title_changed, t) for t in startup_session.tabs] for t in startup_session.tabs:
self.active_tab_idx = startup_session.active_tab_idx self._add_tab(Tab(self.opts, self.args, self.title_changed, t))
self._set_active_tab(max(0, min(startup_session.active_tab_idx, len(self.tabs) - 1)))
if len(self.tabs) > 1: if len(self.tabs) > 1:
get_boss().tabbar_visibility_changed() get_boss().tabbar_visibility_changed()
self.update_tab_bar() self.update_tab_bar()
@ -310,7 +326,7 @@ class TabManager:
tab.relayout() tab.relayout()
def set_active_tab(self, idx): def set_active_tab(self, idx):
self.active_tab_idx = idx self._set_active_tab(idx)
self.active_tab.relayout_borders() self.active_tab.relayout_borders()
self.update_tab_bar() self.update_tab_bar()
@ -337,7 +353,8 @@ class TabManager:
idx = self.active_tab_idx idx = self.active_tab_idx
nidx = (idx + len(self.tabs) + delta) % len(self.tabs) nidx = (idx + len(self.tabs) + delta) % len(self.tabs)
self.tabs[idx], self.tabs[nidx] = self.tabs[nidx], self.tabs[idx] self.tabs[idx], self.tabs[nidx] = self.tabs[nidx], self.tabs[idx]
self.active_tab_idx = nidx swap_tabs(idx, nidx)
self._set_active_tab(nidx)
self.update_tab_bar() self.update_tab_bar()
def title_changed(self, new_title): def title_changed(self, new_title):
@ -345,16 +362,17 @@ class TabManager:
def new_tab(self, special_window=None): def new_tab(self, special_window=None):
needs_resize = len(self.tabs) == 1 needs_resize = len(self.tabs) == 1
self.active_tab_idx = len(self.tabs) idx = len(self.tabs)
self.tabs.append(Tab(self.opts, self.args, self.title_changed, special_window=special_window)) self._add_tab(Tab(self.opts, self.args, self.title_changed, special_window=special_window))
self._set_active_tab(idx)
self.update_tab_bar() self.update_tab_bar()
if needs_resize: if needs_resize:
get_boss().tabbar_visibility_changed() get_boss().tabbar_visibility_changed()
def remove(self, tab): def remove(self, tab):
needs_resize = len(self.tabs) == 2 needs_resize = len(self.tabs) == 2
self.tabs.remove(tab) self._remove_tab(tab)
self.active_tab_idx = max(0, min(self.active_tab_idx, len(self.tabs) - 1)) self._set_active_tab(max(0, min(self.active_tab_idx, len(self.tabs) - 1)))
self.update_tab_bar() self.update_tab_bar()
tab.destroy() tab.destroy()
if needs_resize: if needs_resize:
@ -386,3 +404,4 @@ class TabManager:
if len(self.tabs) < 2: if len(self.tabs) < 2:
return return
self.tab_bar.render() self.tab_bar.render()
# }}}