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

View File

@ -424,6 +424,7 @@ pyset_iutf8(ChildMonitor *self, PyObject *args) {
static double last_render_at = -DBL_MAX;
draw_borders_func draw_borders = NULL;
draw_cells_func draw_cells = NULL;
static inline bool
render(ChildMonitor *self, double *timeout) {
@ -432,6 +433,9 @@ render(ChildMonitor *self, double *timeout) {
double time_since_last_render = now - last_render_at;
if (time_since_last_render > self->repaint_delay) {
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);
if (ret == NULL) { PyErr_Print(); return false; }
else Py_DECREF(ret);

View File

@ -22,6 +22,7 @@
#define MIN(x, y) (((x) > (y)) ? (y) : (x))
#define xstr(s) str(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 color_type;
@ -292,8 +293,22 @@ typedef struct {
bool enable_audio_bell;
} Options;
typedef struct {
unsigned int id;
} Tab;
typedef struct {
ssize_t vao_idx;
float xstart, ystart, dx, dy;
Screen *screen;
} ScreenRenderData;
typedef struct {
Options opts;
Tab tabs[MAX_CHILDREN];
unsigned int active_tab, num_tabs;
ScreenRenderData tab_bar_render_data;
} GlobalState;
#ifndef IS_STATE
@ -313,6 +328,8 @@ extern GlobalState global_state;
typedef void (*draw_borders_func)();
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
Line* alloc_line();

View File

@ -33,8 +33,6 @@ static char glbuf[4096];
#define GL_STACK_OVERFLOW 0x0503
#endif
#define fatal(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); exit(EXIT_FAILURE); }
#ifdef ENABLE_DEBUG_GL
static void
check_for_gl_error(int line) {
@ -596,7 +594,7 @@ create_cell_vao() {
}
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;
void *address;
bool inverted = screen_invert_colors(screen);
@ -842,7 +840,7 @@ PYWRAP1(draw_cells) {
Screen *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;
}
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}
static PyMethodDef module_methods[] = {
{"glewInit", (PyCFunction)glew_init, METH_NOARGS, NULL},
{"draw_cells", (PyCFunction)pydraw_cells, METH_VARARGS, NULL},
M(compile_program, METH_VARARGS),
MW(check_for_extensions, METH_NOARGS),
MW(create_vao, METH_NOARGS),
@ -917,7 +916,6 @@ static PyMethodDef module_methods[] = {
MW(send_borders_rects, METH_VARARGS),
MW(init_cell_program, METH_NOARGS),
MW(create_cell_vao, METH_NOARGS),
MW(draw_cells, METH_VARARGS),
MW(layout_sprite_map, METH_VARARGS),
MW(destroy_sprite_map, METH_NOARGS),
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));
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
draw_borders = &draw_borders_impl;
draw_cells = &draw_cells_impl;
return true;
}
// }}}

View File

@ -8,15 +8,55 @@
#define IS_STATE
#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 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 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) {
#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);
@ -25,10 +65,36 @@ PYWRAP1(set_options) {
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 MW(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL}
static PyMethodDef module_methods[] = {
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 */
};

View File

@ -4,17 +4,18 @@
from collections import deque, namedtuple
from functools import partial
from itertools import count
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 .config import build_ansi_color_table
from .constants import (
WindowGeometry, appname, cell_size, get_boss, shell_path,
viewport_size
WindowGeometry, appname, cell_size, get_boss, shell_path, viewport_size
)
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 .utils import color_as_int
@ -22,16 +23,19 @@ from .window import Window
TabbarData = namedtuple('TabbarData', 'title is_active is_last')
borders = None
tab_counter = count()
next(tab_counter)
def SpecialWindow(cmd, stdin=None, override_title=None):
return (cmd, stdin, override_title)
class Tab:
class Tab: # {{{
def __init__(self, opts, args, on_title_change, session_tab=None, special_window=None):
global borders
self.id = next(tab_counter)
self.opts, self.args = opts, args
self.name = getattr(session_tab, 'name', '')
self.on_title_change = on_title_change
@ -199,9 +203,10 @@ class Tab:
def __repr__(self):
return 'Tab(title={}, id={})'.format(self.name or self.title, hex(id(self)))
# }}}
class TabBar:
class TabBar: # {{{
def __init__(self, opts):
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))
else:
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):
if self.layout_changed is None:
@ -270,18 +276,15 @@ class TabBar:
self.cell_ranges = cr
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):
x = (x - self.window_geometry.left) // self.cell_width
for i, (a, b) in enumerate(self.cell_ranges):
if a <= x <= b:
return i
# }}}
class TabManager:
class TabManager: # {{{
def __init__(self, opts, args):
self.opts, self.args = opts, args
@ -291,9 +294,22 @@ class TabManager:
self.tab_bar.layout(*self.tab_bar_layout_data)
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):
self.tabs = [Tab(self.opts, self.args, self.title_changed, t) for t in startup_session.tabs]
self.active_tab_idx = startup_session.active_tab_idx
for t in startup_session.tabs:
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:
get_boss().tabbar_visibility_changed()
self.update_tab_bar()
@ -310,7 +326,7 @@ class TabManager:
tab.relayout()
def set_active_tab(self, idx):
self.active_tab_idx = idx
self._set_active_tab(idx)
self.active_tab.relayout_borders()
self.update_tab_bar()
@ -337,7 +353,8 @@ class TabManager:
idx = self.active_tab_idx
nidx = (idx + len(self.tabs) + delta) % len(self.tabs)
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()
def title_changed(self, new_title):
@ -345,16 +362,17 @@ class TabManager:
def new_tab(self, special_window=None):
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))
idx = len(self.tabs)
self._add_tab(Tab(self.opts, self.args, self.title_changed, special_window=special_window))
self._set_active_tab(idx)
self.update_tab_bar()
if needs_resize:
get_boss().tabbar_visibility_changed()
def remove(self, tab):
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._remove_tab(tab)
self._set_active_tab(max(0, min(self.active_tab_idx, len(self.tabs) - 1)))
self.update_tab_bar()
tab.destroy()
if needs_resize:
@ -386,3 +404,4 @@ class TabManager:
if len(self.tabs) < 2:
return
self.tab_bar.render()
# }}}