Move the char grid render call into C

This commit is contained in:
Kovid Goyal 2017-09-13 07:58:11 +05:30
parent 943a1575ad
commit e6df82b255
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
11 changed files with 187 additions and 88 deletions

View File

@ -365,9 +365,6 @@ class Boss:
self.glfw_window.set_title(self.glfw_window_title)
if isosx:
cocoa_update_title(self.glfw_window_title)
for window in tab.visible_windows():
if not window.needs_layout:
window.char_grid.render_cells()
active = self.active_window
if active is not None:
draw_cursor = True
@ -383,7 +380,7 @@ class Boss:
active.char_grid.render_cursor(self.window_is_focused)
def gui_close_window(self, window):
window.char_grid.destroy()
window.destroy()
for tab in self.tab_manager:
if window in tab:
break

View File

@ -10,8 +10,7 @@ from .config import build_ansi_color_table
from .constants import ScreenGeometry, cell_size, viewport_size
from .fast_data_types import (
CELL_PROGRAM, CURSOR_BEAM, CURSOR_BLOCK, CURSOR_PROGRAM, CURSOR_UNDERLINE,
compile_program, create_cell_vao, draw_cells, draw_cursor,
init_cell_program, init_cursor_program, remove_vao
compile_program, draw_cursor, init_cell_program, init_cursor_program
)
from .rgb import to_color
from .utils import (
@ -42,16 +41,11 @@ def calculate_gl_geometry(window_geometry, viewport_width, viewport_height, cell
return ScreenGeometry(xstart, ystart, window_geometry.xnum, window_geometry.ynum, dx, dy)
def render_cells(vao_id, sg, screen):
draw_cells(vao_id, sg.xstart, sg.ystart, sg.dx, sg.dy, screen)
class CharGrid:
url_pat = re.compile('(?:http|https|file|ftp)://\S+', re.IGNORECASE)
def __init__(self, screen, opts):
self.vao_id = create_cell_vao()
self.screen_reversed = False
self.screen = screen
self.opts = opts
@ -63,16 +57,9 @@ class CharGrid:
self.default_cursor = Cursor(0, 0, opts.cursor_shape, opts.cursor_blink_interval > 0)
self.opts = opts
def destroy(self):
if self.vao_id is not None:
remove_vao(self.vao_id)
self.vao_id = None
def update_position(self, window_geometry):
self.screen_geometry = calculate_gl_geometry(window_geometry, viewport_size.width, viewport_size.height, cell_size.width, cell_size.height)
def resize(self, window_geometry):
self.update_position(window_geometry)
self.screen_geometry = sg = calculate_gl_geometry(window_geometry, viewport_size.width, viewport_size.height, cell_size.width, cell_size.height)
return sg
def change_colors(self, changes):
dirtied = False
@ -172,9 +159,6 @@ class CharGrid:
def text_for_selection(self):
return ''.join(self.screen.text_for_selection())
def render_cells(self):
render_cells(self.vao_id, self.screen_geometry, self.screen)
def render_cursor(self, is_focused):
if not self.screen.cursor_visible or self.screen.scrolled_by:
return

View File

@ -15,7 +15,7 @@ extern int pthread_setname_np(const char *name);
#include <pthread.h>
#undef _GNU_SOURCE
#endif
#include "data-types.h"
#include "state.h"
#include <termios.h>
#include <unistd.h>
#include <float.h>
@ -30,6 +30,7 @@ extern int pthread_setname_np(const char *name);
#define EXTRA_FDS 2
extern GlobalState global_state;
static void (*parse_func)(Screen*, PyObject*);
typedef struct {
@ -436,6 +437,16 @@ render(ChildMonitor *self, double *timeout) {
#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
if (global_state.num_tabs) {
Tab *tab = global_state.tabs + global_state.active_tab;
for (size_t i = 0; i < tab->num_windows; i++) {
Window *w = tab->windows + i;
#define WD w->render_data
if (w->visible && WD.screen) draw_cells(WD.vao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen);
#undef WD
}
}
ret = PyObject_CallFunctionObjArgs(self->render_func, NULL);
if (ret == NULL) { PyErr_Print(); return false; }
else Py_DECREF(ret);

View File

@ -288,33 +288,6 @@ typedef struct {
} ChildMonitor;
PyTypeObject ChildMonitor_Type;
typedef struct {
double visual_bell_duration;
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
extern GlobalState global_state;
#endif
#define clear_sprite_position(cell) (cell).sprite_x = 0; (cell).sprite_y = 0; (cell).sprite_z = 0;
#define left_shift_line(line, at, num) \
@ -326,11 +299,6 @@ extern GlobalState global_state;
clear_sprite_position((line)->cells[(at)]); \
}
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();
Cursor* alloc_cursor();

View File

@ -122,14 +122,14 @@ class Stack(Layout):
def set_active_window(self, windows, active_window_idx):
for i, w in enumerate(windows):
w.is_visible_in_layout = i == active_window_idx
w.set_visible_in_layout(i, i == active_window_idx)
def __call__(self, windows, active_window_idx):
self.blank_rects = []
wg = layout_single_window(self.margin_width, self.padding_width)
for i, w in enumerate(windows):
w.is_visible_in_layout = i == active_window_idx
w.set_geometry(wg)
w.set_visible_in_layout(i, i == active_window_idx)
w.set_geometry(i, wg)
if w.is_visible_in_layout:
self.blank_rects = blank_rects_for_window(w)
@ -142,7 +142,7 @@ class Tall(Layout):
self.blank_rects = br = []
if len(windows) == 1:
wg = layout_single_window(self.margin_width, self.padding_width)
windows[0].set_geometry(wg)
windows[0].set_geometry(0, wg)
self.blank_rects = blank_rects_for_window(windows[0])
return
xlayout = layout_dimension(
@ -152,14 +152,14 @@ class Tall(Layout):
ystart, ynum = next(layout_dimension(
available_height(), cell_size.height, 1, self.border_width, left_align=True,
margin_length=self.margin_width, padding_length=self.padding_width))
windows[0].set_geometry(window_geometry(xstart, xnum, ystart, ynum))
windows[0].set_geometry(0, window_geometry(xstart, xnum, ystart, ynum))
vh = available_height()
xstart, xnum = next(xlayout)
ylayout = layout_dimension(
available_height(), cell_size.height, len(windows) - 1, self.border_width, left_align=True,
margin_length=self.margin_width, padding_length=self.padding_width)
for w, (ystart, ynum) in zip(islice(windows, 1, None), ylayout):
w.set_geometry(window_geometry(xstart, xnum, ystart, ynum))
for i, (w, (ystart, ynum)) in enumerate(zip(islice(windows, 1, None), ylayout)):
w.set_geometry(i + 1, window_geometry(xstart, xnum, ystart, ynum))
left_blank_rect(windows[0], br, vh), top_blank_rect(windows[0], br, vh), right_blank_rect(windows[-1], br, vh)
br.append(Rect(windows[0].geometry.right, 0, windows[1].geometry.left, vh))
br.append(Rect(0, windows[0].geometry.bottom, windows[0].geometry.right, vh))

View File

@ -11,7 +11,7 @@
#define EXTRA_INIT PyModule_AddIntMacro(module, SCROLL_LINE); PyModule_AddIntMacro(module, SCROLL_PAGE); PyModule_AddIntMacro(module, SCROLL_FULL);
#include "data-types.h"
#include "state.h"
#include "lineops.h"
#include "screen.h"
#include <structmember.h>
@ -23,6 +23,7 @@
#include "modes.h"
#include "wcwidth9.h"
extern GlobalState global_state;
static const ScreenModes empty_modes = {0, .mDECAWM=true, .mDECTCEM=true, .mDECARM=true};
static Selection EMPTY_SELECTION = {0};

View File

@ -5,7 +5,7 @@
* Distributed under terms of the GPL3 license.
*/
#include "data-types.h"
#include "state.h"
#include "screen.h"
#include "sprites.h"
#ifdef __APPLE__

View File

@ -5,24 +5,31 @@
* Distributed under terms of the GPL3 license.
*/
#define IS_STATE
#include "data-types.h"
#include "state.h"
GlobalState global_state = {{0}};
static const Tab EMPTY_TAB = {0};
static const Window EMPTY_WINDOW = {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) { \
#define noop(...)
#define REMOVER(array, qid, count, empty, structure, destroy) { \
size_t capacity = sizeof(array)/sizeof(array[0]); \
for (size_t i = 0; i < count; i++) { \
if (array[i].id == qid) { \
destroy(array[i]); \
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)--; \
} \
}}
#define WITH_TAB(tab_id) \
for (size_t t = 0; t < global_state.num_tabs; t++) { \
if (global_state.tabs[t].id == tab_id) { \
Tab *tab = global_state.tabs + t;
#define END_WITH_TAB break; }}
static inline void
add_tab(unsigned int id) {
@ -33,15 +40,43 @@ add_tab(unsigned int id) {
}
static inline void
remove_tab(unsigned int id) {
REMOVER(global_state.tabs, id, global_state.num_tabs, EMPTY_TAB, Tab);
add_window(unsigned int tab_id, unsigned int id) {
WITH_TAB(tab_id);
ensure_can_add(tab->windows, tab->num_windows, "Too many children (add_window)");
tab->windows[tab->num_windows] = EMPTY_WINDOW;
tab->windows[tab->num_windows].id = id;
tab->windows[tab->num_windows].visible = true;
tab->num_windows++;
END_WITH_TAB;
}
static inline void
remove_tab(unsigned int id) {
REMOVER(global_state.tabs, id, global_state.num_tabs, EMPTY_TAB, Tab, noop);
}
static inline void
remove_window(unsigned int tab_id, unsigned int id) {
WITH_TAB(tab_id);
#define destroy_window(w) Py_CLEAR(w.render_data.screen)
REMOVER(tab->windows, id, tab->num_windows, EMPTY_WINDOW, Window, destroy_window);
#undef destroy_window
END_WITH_TAB;
}
static inline void
set_active_tab(unsigned int idx) {
global_state.active_tab = idx;
}
static inline void
set_active_window(unsigned int tab_id, unsigned int idx) {
WITH_TAB(tab_id);
tab->active_window = idx;
END_WITH_TAB;
}
static inline void
swap_tabs(unsigned int a, unsigned int b) {
Tab t = global_state.tabs[b];
@ -49,6 +84,15 @@ swap_tabs(unsigned int a, unsigned int b) {
global_state.tabs[a] = t;
}
static inline void
swap_windows(unsigned int tab_id, unsigned int a, unsigned int b) {
WITH_TAB(tab_id);
Window w = tab->windows[b];
tab->windows[b] = tab->windows[a];
tab->windows[a] = w;
END_WITH_TAB;
}
// Python API {{{
#define PYWRAP0(name) static PyObject* py##name(PyObject UNUSED *self)
#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)
@ -56,6 +100,7 @@ swap_tabs(unsigned int a, unsigned int b) {
#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; }
#define THREE_UINT(name) PYWRAP1(name) { unsigned int a, b, c; PA("III", &a, &b, &c); name(a, b, c); Py_RETURN_NONE; }
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); }
@ -74,15 +119,44 @@ PYWRAP1(set_tab_bar_render_data) {
#undef A
}
PYWRAP1(set_window_render_data) {
#define A(name) &(d.name)
unsigned int window_idx, tab_id;
ScreenRenderData d = {0};
PA("IIiffffO", &tab_id, &window_idx, A(vao_idx), A(xstart), A(ystart), A(dx), A(dy), A(screen));
WITH_TAB(tab_id);
Py_CLEAR(tab->windows[window_idx].render_data.screen);
tab->windows[window_idx].render_data = d;
Py_INCREF(tab->windows[window_idx].render_data.screen);
END_WITH_TAB;
Py_RETURN_NONE;
#undef A
}
PYWRAP1(update_window_visibility) {
unsigned int window_idx, tab_id;
int visible;
PA("IIp", &tab_id, &window_idx, &visible);
WITH_TAB(tab_id);
tab->windows[window_idx].visible = visible & 1;
END_WITH_TAB;
Py_RETURN_NONE;
}
PYWRAP0(destroy_global_data) {
Py_CLEAR(global_state.tab_bar_render_data.screen);
Py_RETURN_NONE;
}
ONE_UINT(add_tab)
TWO_UINT(add_window)
ONE_UINT(remove_tab)
TWO_UINT(remove_window)
ONE_UINT(set_active_tab)
TWO_UINT(set_active_window)
TWO_UINT(swap_tabs)
THREE_UINT(swap_windows)
#define M(name, arg_type) {#name, (PyCFunction)name, arg_type, NULL}
#define MW(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL}
@ -90,10 +164,16 @@ TWO_UINT(swap_tabs)
static PyMethodDef module_methods[] = {
MW(set_options, METH_O),
MW(add_tab, METH_O),
MW(add_window, METH_VARARGS),
MW(remove_tab, METH_O),
MW(remove_window, METH_VARARGS),
MW(set_active_tab, METH_O),
MW(set_active_window, METH_VARARGS),
MW(swap_tabs, METH_VARARGS),
MW(swap_windows, METH_VARARGS),
MW(set_tab_bar_render_data, METH_VARARGS),
MW(set_window_render_data, METH_VARARGS),
MW(update_window_visibility, METH_VARARGS),
MW(destroy_global_data, METH_NOARGS),
{NULL, NULL, 0, NULL} /* Sentinel */

43
kitty/state.h Normal file
View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#pragma once
#include "data-types.h"
typedef struct {
double visual_bell_duration;
bool enable_audio_bell;
} Options;
typedef struct {
ssize_t vao_idx;
float xstart, ystart, dx, dy;
Screen *screen;
} ScreenRenderData;
typedef struct {
unsigned int id;
bool visible;
ScreenRenderData render_data;
} Window;
typedef struct {
unsigned int id, active_window, num_windows;
Window windows[MAX_CHILDREN];
} Tab;
typedef struct {
Options opts;
Tab tabs[MAX_CHILDREN];
unsigned int active_tab, num_tabs;
ScreenRenderData tab_bar_render_data;
} GlobalState;
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;

View File

@ -14,8 +14,9 @@ from .constants import (
WindowGeometry, appname, cell_size, get_boss, shell_path, viewport_size
)
from .fast_data_types import (
DECAWM, Screen, add_tab, create_cell_vao, glfw_post_empty_event,
remove_tab, set_active_tab, swap_tabs, set_tab_bar_render_data
DECAWM, Screen, add_tab, add_window, create_cell_vao,
glfw_post_empty_event, remove_tab, remove_window, set_active_tab,
set_active_window, set_tab_bar_render_data, swap_tabs, swap_windows
)
from .layout import Rect, all_layouts
from .utils import color_as_int
@ -36,6 +37,7 @@ class Tab: # {{{
def __init__(self, opts, args, on_title_change, session_tab=None, special_window=None):
global borders
self.id = next(tab_counter)
add_tab(self.id)
self.opts, self.args = opts, args
self.name = getattr(session_tab, 'name', '')
self.on_title_change = on_title_change
@ -104,8 +106,8 @@ class Tab: # {{{
idx = -1
nl = self.opts.enabled_layouts[(idx + 1) % len(self.opts.enabled_layouts)]
self.current_layout = all_layouts[nl](self.opts, borders.border_width, self.windows)
for w in self.windows:
w.is_visible_in_layout = True
for i, w in enumerate(self.windows):
w.set_visible_in_layout(i, True)
self.relayout()
def launch_child(self, use_shell=False, cmd=None, stdin=None):
@ -125,7 +127,9 @@ class Tab: # {{{
window.title = window.override_title = override_title
# Must add child before laying out so that resize_pty succeeds
get_boss().add_child(window)
add_window(self.id, window.id)
self.active_window_idx = self.current_layout.add_window(self.windows, window, self.active_window_idx)
set_active_window(self.id, self.active_window_idx)
self.relayout_borders()
glfw_post_empty_event()
return window
@ -138,7 +142,9 @@ class Tab: # {{{
self.remove_window(self.windows[self.active_window_idx])
def remove_window(self, window):
remove_window(self.id, window.id)
self.active_window_idx = self.current_layout.remove_window(self.windows, window, self.active_window_idx)
set_active_window(self.id, self.active_window_idx)
self.relayout_borders()
glfw_post_empty_event()
@ -146,6 +152,7 @@ class Tab: # {{{
if idx != self.active_window_idx:
self.current_layout.set_active_window(self.windows, idx)
self.active_window_idx = idx
set_active_window(self.id, self.active_window_idx)
self.relayout_borders()
glfw_post_empty_event()
@ -163,6 +170,7 @@ class Tab: # {{{
def _next_window(self, delta=1):
if len(self.windows) > 1:
self.active_window_idx = self.current_layout.next_window(self.windows, self.active_window_idx, delta)
set_active_window(self.id, self.active_window_idx)
self.relayout_borders()
glfw_post_empty_event()
@ -177,7 +185,9 @@ class Tab: # {{{
idx = self.active_window_idx
nidx = (idx + len(self.windows) + delta) % len(self.windows)
self.windows[nidx], self.windows[idx] = self.windows[idx], self.windows[nidx]
swap_windows(self.id, nidx, idx)
self.active_window_idx = nidx
set_active_window(self.id, self.active_window_idx)
self.relayout()
def move_window_to_top(self):
@ -295,7 +305,6 @@ class TabManager: # {{{
self.active_tab_idx = 0
def _add_tab(self, tab):
add_tab(tab.id)
self.tabs.append(tab)
def _remove_tab(self, tab):

View File

@ -18,7 +18,9 @@ from .fast_data_types import (
GLFW_KEY_LEFT_SHIFT, GLFW_KEY_RIGHT_SHIFT, GLFW_KEY_UP, GLFW_MOD_SHIFT,
GLFW_MOUSE_BUTTON_1, GLFW_MOUSE_BUTTON_4, GLFW_MOUSE_BUTTON_5,
GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, GLFW_RELEASE, MOTION_MODE,
SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE, Screen, glfw_post_empty_event
SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE, Screen, create_cell_vao,
glfw_post_empty_event, remove_vao, set_window_render_data,
update_window_visibility
)
from .keys import get_key_map
from .mouse import DRAG, MOVE, PRESS, RELEASE, encode_mouse_event
@ -45,6 +47,8 @@ class Window:
def __init__(self, tab, child, opts, args):
self.id = next(window_counter)
self.vao_id = create_cell_vao()
self.tab_id = tab.id
self.tabref = weakref.ref(tab)
self.override_title = None
self.last_mouse_cursor_pos = 0, 0
@ -53,7 +57,7 @@ class Window:
self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0)
self.needs_layout = True
self.title = appname
self._is_visible_in_layout = True
self.is_visible_in_layout = True
self.child, self.opts = child, opts
self.start_visual_bell_at = None
self.screen = Screen(self, 24, 80, opts.scrollback_lines)
@ -63,15 +67,11 @@ class Window:
def __repr__(self):
return 'Window(title={}, id={})'.format(self.title, self.id)
@property
def is_visible_in_layout(self):
return self._is_visible_in_layout
@is_visible_in_layout.setter
def is_visible_in_layout(self, val):
def set_visible_in_layout(self, window_idx, val):
val = bool(val)
if val != self._is_visible_in_layout:
self._is_visible_in_layout = val
if val is not self.is_visible_in_layout:
self.is_visible_in_layout = val
update_window_visibility(self.tab_id, window_idx, val)
if val:
self.refresh()
@ -79,7 +79,7 @@ class Window:
self.screen.mark_as_dirty()
wakeup()
def set_geometry(self, new_geometry):
def set_geometry(self, window_idx, new_geometry):
if self.destroyed:
return
if self.needs_layout or new_geometry.xnum != self.screen.columns or new_geometry.ynum != self.screen.lines:
@ -88,11 +88,12 @@ class Window:
self.current_pty_size = (
self.screen.lines, self.screen.columns,
max(0, new_geometry.right - new_geometry.left), max(0, new_geometry.bottom - new_geometry.top))
self.char_grid.resize(new_geometry)
sg = self.char_grid.update_position(new_geometry)
self.needs_layout = False
boss.resize_pty(self.id)
else:
self.char_grid.update_position(new_geometry)
sg = self.char_grid.update_position(new_geometry)
set_window_render_data(self.tab_id, window_idx, self.vao_id, sg.xstart, sg.ystart, sg.dx, sg.dy, self.screen)
self.geometry = new_geometry
def contains(self, x, y):
@ -281,6 +282,11 @@ class Window:
def buf_toggled(self, is_main_linebuf):
self.screen.scroll(SCROLL_FULL, False)
def destroy(self):
if self.vao_id is not None:
remove_vao(self.vao_id)
self.vao_id = None
# actions {{{
def show_scrollback(self):