Make the sprite map globally accessible from C code as well as python code

The lock was removed as the Python GIL is sufficient to serialize access
to the SpriteMap structure.
This commit is contained in:
Kovid Goyal 2017-08-30 20:49:53 +05:30
parent 4feff2e2da
commit 6127d2d122
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
12 changed files with 191 additions and 203 deletions

View File

@ -148,10 +148,10 @@ def __init__(self, glfw_window, opts, args):
glfw_window.window_focus_callback = self.on_focus
self.tab_manager = TabManager(opts, args, startup_session)
self.sprites = Sprites()
self.sprites.do_layout(cell_size.width, cell_size.height)
self.cell_program, self.cursor_program = load_shader_programs()
self.borders_program = BordersProgram()
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
self.sprites.do_layout(cell_size.width, cell_size.height)
self.glfw_window.set_click_cursor(False)
self.show_mouse_cursor()
self.start_cursor_blink()

View File

@ -11,7 +11,7 @@
from .config import build_ansi_color_table, defaults
from .constants import (
GLfloat, GLuint, ScreenGeometry, cell_size, get_boss, viewport_size
GLfloat, GLuint, ScreenGeometry, cell_size, viewport_size
)
from .fast_data_types import (
CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, DATA_CELL_SIZE, GL_BLEND,
@ -234,16 +234,13 @@ def scroll(self, amt, upwards=True):
self.update_cell_data()
def update_cell_data(self, force_full_refresh=False):
sprites = get_boss().sprites
is_dirty = self.screen.is_dirty()
with sprites.lock:
cursor_changed, history_line_added_count = self.screen.update_cell_data(
sprites.backend, addressof(self.main_sprite_map), force_full_refresh)
if self.scrolled_by:
self.scrolled_by = min(self.scrolled_by + history_line_added_count, self.screen.historybuf.count)
self.screen.set_scroll_cell_data(
sprites.backend, addressof(self.main_sprite_map),
self.scrolled_by, addressof(self.scroll_sprite_map))
cursor_changed, history_line_added_count = self.screen.update_cell_data(
addressof(self.main_sprite_map), force_full_refresh)
if self.scrolled_by:
self.scrolled_by = min(self.scrolled_by + history_line_added_count, self.screen.historybuf.count)
self.screen.set_scroll_cell_data(
addressof(self.main_sprite_map), self.scrolled_by, addressof(self.scroll_sprite_map))
data = self.scroll_sprite_map if self.scrolled_by else self.main_sprite_map
with self.buffer_lock:

View File

@ -9,6 +9,7 @@
#include "glfw.h"
#include "gl.h"
#include "modes.h"
#include "sprites.h"
#include <stddef.h>
#ifdef WITH_PROFILER
#include <gperftools/profiler.h>
@ -79,6 +80,7 @@ static PyMethodDef module_methods[] = {
{"get_fontconfig_font", (PyCFunction)get_fontconfig_font, METH_VARARGS, ""},
#endif
GLFW_FUNC_WRAPPERS
SPRITE_FUNC_WRAPPERS
#ifdef WITH_PROFILER
{"start_profiler", (PyCFunction)start_profiler, METH_VARARGS, ""},
{"stop_profiler", (PyCFunction)stop_profiler, METH_NOARGS, ""},
@ -112,7 +114,6 @@ PyInit_fast_data_types(void) {
if (!init_Timers(m)) return NULL;
if (!init_ChildMonitor(m)) return NULL;
if (!init_ColorProfile(m)) return NULL;
if (!init_SpriteMap(m)) return NULL;
if (!init_ChangeTracker(m)) return NULL;
if (!init_Screen(m)) return NULL;
if (!add_module_gl_constants(m)) return NULL;

View File

@ -26,6 +26,7 @@ typedef uint32_t char_type;
typedef uint32_t color_type;
typedef uint32_t combining_type;
typedef unsigned int index_type;
typedef uint16_t sprite_index;
#define ERROR_PREFIX "[PARSE ERROR]"
#define ANY_MODE 3
@ -191,29 +192,6 @@ typedef struct {
} ColorProfile;
PyTypeObject ColorProfile_Type;
typedef struct SpritePosition SpritePosition;
struct SpritePosition {
SpritePosition *next;
unsigned int x, y, z;
char_type ch;
combining_type cc;
bool is_second;
bool filled;
bool rendered;
};
PyTypeObject SpritePosition_Type;
typedef struct {
PyObject_HEAD
size_t max_array_len, max_texture_size, max_y;
unsigned int x, y, z, xnum, ynum;
SpritePosition cache[1024];
bool dirty;
} SpriteMap;
PyTypeObject SpriteMap_Type;
typedef struct {
PyObject_HEAD
@ -336,7 +314,6 @@ int init_Timers(PyObject *);
int init_ChildMonitor(PyObject *);
int init_Line(PyObject *);
int init_ColorProfile(PyObject *);
int init_SpriteMap(PyObject *);
int init_ChangeTracker(PyObject *);
int init_Screen(PyObject *);
int init_Face(PyObject *);
@ -351,7 +328,7 @@ void cursor_reset(Cursor*);
Cursor* cursor_copy(Cursor*);
void cursor_copy_to(Cursor *src, Cursor *dest);
void cursor_reset_display_attrs(Cursor*);
bool update_cell_range_data(ScreenModes *modes, SpriteMap *, Line *, unsigned int, unsigned int, unsigned int *);
bool update_cell_range_data(ScreenModes *modes, Line *, unsigned int, unsigned int, unsigned int *);
PyObject* line_text_at(char_type, combining_type);
void line_clear_text(Line *self, unsigned int at, unsigned int num, int ch);

View File

@ -1167,25 +1167,23 @@ change_scrollback_size(Screen *self, PyObject *args) {
static PyObject*
screen_update_cell_data(Screen *self, PyObject *args) {
SpriteMap *spm;
PyObject *dp;
unsigned int *data;
int force_screen_refresh;
if (!PyArg_ParseTuple(args, "O!O!p", &SpriteMap_Type, &spm, &PyLong_Type, &dp, &force_screen_refresh)) return NULL;
if (!PyArg_ParseTuple(args, "O!p", &PyLong_Type, &dp, &force_screen_refresh)) return NULL;
data = PyLong_AsVoidPtr(dp);
PyObject *cursor_changed = self->change_tracker->cursor_changed ? Py_True : Py_False;
unsigned int history_line_added_count = self->change_tracker->history_line_added_count;
if (!tracker_update_cell_data(&(self->modes), self->change_tracker, self->linebuf, spm, data, (bool)force_screen_refresh)) return NULL;
if (!tracker_update_cell_data(&(self->modes), self->change_tracker, self->linebuf, data, (bool)force_screen_refresh)) return NULL;
return Py_BuildValue("OI", cursor_changed, history_line_added_count);
}
static PyObject*
set_scroll_cell_data(Screen *self, PyObject *args) {
SpriteMap *spm;
PyObject *dp, *sp;
unsigned int *data, *src, scrolled_by;
if (!PyArg_ParseTuple(args, "O!O!IO", &SpriteMap_Type, &spm, &PyLong_Type, &sp, &scrolled_by, &dp)) return NULL;
if (!PyArg_ParseTuple(args, "O!IO", &PyLong_Type, &sp, &scrolled_by, &dp)) return NULL;
data = PyLong_AsVoidPtr(dp);
src = PyLong_AsVoidPtr(sp);
@ -1194,7 +1192,7 @@ set_scroll_cell_data(Screen *self, PyObject *args) {
for (index_type y = 0; y < MIN(self->lines, scrolled_by); y++) {
historybuf_init_line(self->historybuf, scrolled_by - 1 - y, self->historybuf->line);
self->historybuf->line->ynum = y;
if (!update_cell_range_data(&(self->modes), spm, self->historybuf->line, 0, self->columns - 1, data)) return NULL;
if (!update_cell_range_data(&(self->modes), self->historybuf->line, 0, self->columns - 1, data)) return NULL;
}
if (scrolled_by < self->lines) {
// Less than a full screen has been scrolled, copy some lines from the screen buffer to the scroll buffer

View File

@ -8,7 +8,6 @@
from contextlib import contextmanager
from ctypes import addressof, sizeof
from functools import lru_cache
from threading import Lock
from .fast_data_types import (
BOLD, GL_ARRAY_BUFFER, GL_CLAMP_TO_EDGE, GL_COMPILE_STATUS, GL_FLOAT,
@ -17,7 +16,7 @@
GL_TEXTURE0, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER,
GL_TEXTURE_MIN_FILTER, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_TRUE,
GL_UNIFORM_BUFFER, GL_UNPACK_ALIGNMENT, GL_UNSIGNED_BYTE, GL_VERTEX_SHADER,
ITALIC, SpriteMap, copy_image_sub_data, get_uniform_block_offsets,
ITALIC, copy_image_sub_data, get_uniform_block_offsets,
get_uniform_block_size, glActiveTexture, glAttachShader, glBindBuffer,
glBindBufferBase, glBindTexture, glBindVertexArray, glCompileShader,
glCopyImageSubData, glCreateProgram, glCreateShader, glDeleteBuffer,
@ -28,7 +27,8 @@
glGetUniformBlockIndex, glGetUniformLocation, glLinkProgram, glPixelStorei,
glShaderSource, glTexParameteri, glTexStorage3D, glTexSubImage3D,
glUseProgram, glVertexAttribDivisor, glVertexAttribPointer,
replace_or_create_buffer
render_dirty_sprites, replace_or_create_buffer, sprite_map_current_layout,
sprite_map_increment, sprite_map_set_layout, sprite_map_set_limits, sprite_map_free
)
from .fonts.render import render_cell
from .utils import safe_print
@ -120,24 +120,20 @@ def __init__(self):
self.last_ynum = -1
self.sampler_num = 0
self.texture_unit = GL_TEXTURE0 + self.sampler_num
self.backend = SpriteMap(glGetIntegerv(GL_MAX_TEXTURE_SIZE), glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS))
self.lock = Lock()
sprite_map_set_limits(glGetIntegerv(GL_MAX_TEXTURE_SIZE), glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS))
def do_layout(self, cell_width=1, cell_height=1):
self.cell_width, self.cell_height = cell_width, cell_height
self.backend.layout(cell_width or 1, cell_height or 1)
sprite_map_set_layout(cell_width or 1, cell_height or 1)
if self.texture_id is not None:
glDeleteTexture(self.texture_id)
self.texture_id = None
self.ensure_state()
self.pre_render()
def pre_render(self):
# Pre-render the basic cells to ensure they have known sprite numbers
def send(*a, **kw):
buf = render_cell(*a, **kw)[0]
x, y, z = self.backend.increment()
x, y, z = sprite_map_increment()
self.send_to_gpu(x, y, z, buf)
return x
@ -149,7 +145,8 @@ def send(*a, **kw):
@property
def layout(self):
return 1 / self.backend.xnum, 1 / self.backend.ynum
xnum, ynum, znum = sprite_map_current_layout()
return 1 / xnum, 1 / ynum
def render_cell(self, text, bold, italic, is_second):
first, second = render_cell(text, bold, italic)
@ -157,14 +154,14 @@ def render_cell(self, text, bold, italic, is_second):
return ans or render_cell()[0]
def render_dirty_cells(self):
with self.lock:
self.backend.render_dirty_cells(self.render_cell, self.send_to_gpu)
render_dirty_sprites(self.render_cell, self.send_to_gpu)
def send_to_gpu(self, x, y, z, buf):
if self.backend.z >= self.last_num_of_layers:
xnum, ynum, znum = sprite_map_current_layout()
if znum >= self.last_num_of_layers:
self.realloc_texture()
else:
if self.backend.z == 0 and self.backend.ynum > self.last_ynum:
if znum == 0 and ynum > self.last_ynum:
self.realloc_texture()
tgt = GL_TEXTURE_2D_ARRAY
glBindTexture(tgt, self.texture_id)
@ -182,12 +179,13 @@ def realloc_texture(self):
glTexParameteri(tgt, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(tgt, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameteri(tgt, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
znum = self.backend.z + 1
width, height = self.backend.xnum * self.cell_width, self.backend.ynum * self.cell_height
xnum, bynum, z = sprite_map_current_layout()
znum = z + 1
width, height = xnum * self.cell_width, bynum * self.cell_height
glTexStorage3D(tgt, 1, GL_R8, width, height, znum)
if self.texture_id is not None:
ynum = self.backend.ynum
if self.backend.z == 0:
ynum = bynum
if z == 0:
ynum -= 1 # Only copy the previous rows
try:
glCopyImageSubData(self.texture_id, tgt, 0, 0, 0, 0, tex, tgt, 0, 0, 0, 0,
@ -203,10 +201,11 @@ def realloc_texture(self):
glBindTexture(tgt, tex)
glDeleteTexture(self.texture_id)
self.last_num_of_layers = znum
self.last_ynum = self.backend.ynum
self.last_ynum = bynum
self.texture_id = tex
def destroy(self):
sprite_map_free()
if self.texture_id is not None:
glDeleteTexture(self.texture_id)
self.texture_id = None

View File

@ -6,28 +6,46 @@
*/
#include "data-types.h"
#include "sprites.h"
#include <structmember.h>
static PyObject*
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
SpriteMap *self;
unsigned long mlen, msz;
if (!PyArg_ParseTuple(args, "kk", &msz, &mlen)) return NULL;
typedef struct {
size_t max_array_len, max_texture_size, max_y;
unsigned int x, y, z, xnum, ynum;
SpritePosition cache[1024];
bool dirty;
} SpriteMap;
self = (SpriteMap *)type->tp_alloc(type, 0);
if (self != NULL) {
self->max_array_len = mlen;
self->max_texture_size = msz;
self->dirty = true;
static SpriteMap sprite_map = {
.max_array_len = 1000,
.max_texture_size = 1000,
.max_y = 100,
.dirty = true
};
static inline void
sprite_map_set_error(int error) {
switch(error) {
case 1:
PyErr_NoMemory(); break;
case 2:
PyErr_SetString(PyExc_RuntimeError, "Out of texture space for sprites"); break;
default:
PyErr_SetString(PyExc_RuntimeError, "Unknown error occurred while allocating sprites"); break;
}
return (PyObject*) self;
}
static void
dealloc(SpriteMap* self) {
PyObject*
sprite_map_set_limits(PyObject UNUSED *self, PyObject *args) {
if (!PyArg_ParseTuple(args, "kk", &(sprite_map.max_texture_size), &(sprite_map.max_array_len))) return NULL;
Py_RETURN_NONE;
}
PyObject*
sprite_map_free() {
SpritePosition *s, *t;
for (size_t i = 0; i < sizeof(self->cache)/sizeof(self->cache[0]); i++) {
s = &(self->cache[i]);
for (size_t i = 0; i < sizeof(sprite_map.cache)/sizeof(sprite_map.cache[0]); i++) {
s = &(sprite_map.cache[i]);
s = s->next;
while (s) {
t = s;
@ -35,51 +53,27 @@ dealloc(SpriteMap* self) {
PyMem_Free(t);
}
}
Py_TYPE(self)->tp_free((PyObject*)self);
}
static PyObject*
layout(SpriteMap *self, PyObject *args) {
#define layout_doc "layout(cell_width, cell_height) -> Invalidate the cache and prepare it for new cell size"
unsigned long cell_width, cell_height;
if (!PyArg_ParseTuple(args, "kk", &cell_width, &cell_height)) return NULL;
self->xnum = MIN(MAX(1, self->max_texture_size / cell_width), UINT16_MAX);
self->max_y = MIN(MAX(1, self->max_texture_size / cell_height), UINT16_MAX);
self->ynum = 1;
self->x = 0; self->y = 0; self->z = 0;
for (size_t i = 0; i < sizeof(self->cache)/sizeof(self->cache[0]); i++) {
SpritePosition *s = &(self->cache[i]);
do {
s->filled = false;
s->is_second = false;
s->rendered = false;
s->ch = 0; s->cc = 0;
s->x = 0; s->y = 0; s->z = 0;
s = s->next;
} while (s != NULL);
}
Py_RETURN_NONE;
}
static void
do_increment(SpriteMap *self, int *error) {
self->x++;
if (self->x >= self->xnum) {
self->x = 0; self->y++;
self->ynum = MIN(MAX(self->ynum, self->y + 1), self->max_y);
if (self->y >= self->max_y) {
self->y = 0; self->z++;
if (self->z >= MIN(UINT16_MAX, self->max_array_len)) *error = 2;
static inline void
do_increment(int *error) {
sprite_map.x++;
if (sprite_map.x >= sprite_map.xnum) {
sprite_map.x = 0; sprite_map.y++;
sprite_map.ynum = MIN(MAX(sprite_map.ynum, sprite_map.y + 1), sprite_map.max_y);
if (sprite_map.y >= sprite_map.max_y) {
sprite_map.y = 0; sprite_map.z++;
if (sprite_map.z >= MIN(UINT16_MAX, sprite_map.max_array_len)) *error = 2;
}
}
}
static SpritePosition*
sprite_position_for(SpriteMap *self, char_type ch, combining_type cc, bool is_second, int *error) {
SpritePosition*
sprite_map_position_for(char_type ch, combining_type cc, bool is_second, int *error) {
char_type pos_char = ch & POSCHAR_MASK; // Includes only the char and bold and italic bits
unsigned int idx = ((ch >> (ATTRS_SHIFT - 6)) & 0x300) | (ch & 0xFF); // Includes only italic, bold and lowest byte of ch
SpritePosition *s = &(self->cache[idx]);
unsigned int idx = ((ch >> (ATTRS_SHIFT - 4)) & 0x300) | (ch & 0xFF); // Includes only italic, bold and lowest byte of ch
SpritePosition *s = &(sprite_map.cache[idx]);
// Optimize for the common case of an ASCII char already in the cache
if (LIKELY(s->ch == pos_char && s->filled && s->cc == cc && s->is_second == is_second)) return s; // Cache hit
while(true) {
@ -99,47 +93,67 @@ sprite_position_for(SpriteMap *self, char_type ch, combining_type cc, bool is_se
s->is_second = is_second;
s->filled = true;
s->rendered = false;
s->x = self->x; s->y = self->y; s->z = self->z;
do_increment(self, error);
self->dirty = true;
s->x = sprite_map.x; s->y = sprite_map.y; s->z = sprite_map.z;
do_increment(error);
sprite_map.dirty = true;
return s;
}
static void set_sprite_error(int error) {
switch(error) {
case 1:
PyErr_NoMemory(); break;
case 2:
PyErr_SetString(PyExc_RuntimeError, "Out of texture space for sprites"); break;
default:
PyErr_SetString(PyExc_RuntimeError, "Unknown error occurred while allocating sprites"); break;
}
}
static PyObject*
increment(SpriteMap *self) {
PyObject*
sprite_map_increment() {
#define increment_doc "Increment the current position and return the old (x, y, z) values"
unsigned int x = self->x, y = self->y, z = self->z;
unsigned int x = sprite_map.x, y = sprite_map.y, z = sprite_map.z;
int error = 0;
do_increment(self, &error);
if (error) { set_sprite_error(error); return NULL; }
do_increment(&error);
if (error) { sprite_map_set_error(error); return NULL; }
return Py_BuildValue("III", x, y, z);
}
static PyObject*
position_for(SpriteMap *self, PyObject *args) {
PyObject*
sprite_map_set_layout(PyObject UNUSED *s_, PyObject *args) {
// Invalidate cache since cell size has changed.
unsigned long cell_width, cell_height;
if (!PyArg_ParseTuple(args, "kk", &cell_width, &cell_height)) return NULL;
SpritePosition *s;
sprite_map.xnum = MIN(MAX(1, sprite_map.max_texture_size / cell_width), UINT16_MAX);
sprite_map.max_y = MIN(MAX(1, sprite_map.max_texture_size / cell_height), UINT16_MAX);
sprite_map.ynum = 1;
sprite_map.x = 0; sprite_map.y = 0; sprite_map.z = 0;
for (size_t i = 0; i < sizeof(sprite_map.cache)/sizeof(sprite_map.cache[0]); i++) {
s = &(sprite_map.cache[i]);
do {
s->filled = false;
s->is_second = false;
s->rendered = false;
s->ch = 0; s->cc = 0;
s->x = 0; s->y = 0; s->z = 0;
s = s->next;
} while (s != NULL);
}
sprite_map.dirty = true;
Py_RETURN_NONE;
}
PyObject*
sprite_map_current_layout(PyObject UNUSED *s) {
return Py_BuildValue("III", sprite_map.xnum, sprite_map.ynum, sprite_map.z);
}
PyObject*
sprite_position_for(PyObject UNUSED *self, PyObject *args) {
#define position_for_doc "position_for(ch, cc, is_second) -> x, y, z the sprite position for the specified text"
unsigned long ch = 0;
unsigned long long cc = 0;
int is_second = 0, error = 0;
if (!PyArg_ParseTuple(args, "|kKp", &ch, &cc, &is_second)) return NULL;
SpritePosition *pos = sprite_position_for(self, ch, cc, is_second, &error);
if (pos == NULL) {set_sprite_error(error); return NULL; }
SpritePosition *pos = sprite_map_position_for(ch, cc, is_second, &error);
if (pos == NULL) { sprite_map_set_error(error); return NULL; }
return Py_BuildValue("III", pos->x, pos->y, pos->z);
}
bool
update_cell_range_data(ScreenModes *modes, SpriteMap *self, Line *line, unsigned int xstart, unsigned int xmax, unsigned int *data) {
update_cell_range_data(ScreenModes *modes, Line *line, unsigned int xstart, unsigned int xmax, unsigned int *data) {
SpritePosition *sp;
char_type previous_ch=0, ch;
uint8_t previous_width = 0;
@ -149,9 +163,9 @@ update_cell_range_data(ScreenModes *modes, SpriteMap *self, Line *line, unsigned
size_t base = line->ynum * line->xnum * DATA_CELL_SIZE;
for (size_t i = xstart, offset = base + xstart * DATA_CELL_SIZE; i <= xmax; i++, offset += DATA_CELL_SIZE) {
ch = line->cells[i].ch;
if (previous_width == 2) sp = sprite_position_for(self, previous_ch, 0, true, &err);
else sp = sprite_position_for(self, ch, line->cells[i].cc, false, &err);
if (sp == NULL) { set_sprite_error(err); return false; }
if (previous_width == 2) sp = sprite_map_position_for(previous_ch, 0, true, &err);
else sp = sprite_map_position_for(ch, line->cells[i].cc, false, &err);
if (sp == NULL) { sprite_map_set_error(err); return false; }
char_type attrs = ch >> ATTRS_SHIFT;
unsigned int decoration = (attrs >> DECORATION_SHIFT) & DECORATION_MASK;
unsigned int strikethrough = ((attrs >> STRIKE_SHIFT) & 1) ? 3 : 0;
@ -167,17 +181,17 @@ update_cell_range_data(ScreenModes *modes, SpriteMap *self, Line *line, unsigned
return true;
}
static PyObject*
render_dirty_cells(SpriteMap *self, PyObject *args) {
PyObject*
render_dirty_sprites(PyObject UNUSED *s_, PyObject *args) {
#define render_dirty_cells_doc "Render all cells that are marked as dirty"
PyObject *render_cell, *send_to_gpu;
if (!PyArg_ParseTuple(args, "OO", &render_cell, &send_to_gpu)) return NULL;
if (!self->dirty) { Py_RETURN_NONE; }
if (!sprite_map.dirty) { Py_RETURN_NONE; }
for (size_t i = 0; i < sizeof(self->cache)/sizeof(self->cache[0]); i++) {
SpritePosition *sp = &(self->cache[i]);
for (size_t i = 0; i < sizeof(sprite_map.cache)/sizeof(sprite_map.cache[0]); i++) {
SpritePosition *sp = &(sprite_map.cache[i]);
while (sp) {
if (sp->filled && !sp->rendered) {
PyObject *text = line_text_at(sp->ch & CHAR_MASK, sp->cc);
@ -196,44 +210,6 @@ render_dirty_cells(SpriteMap *self, PyObject *args) {
sp = sp->next;
}
}
self->dirty = false;
sprite_map.dirty = false;
Py_RETURN_NONE;
}
// Boilerplate {{{
static PyMemberDef members[] = {
{"xnum", T_UINT, offsetof(SpriteMap, xnum), 0, "xnum"},
{"ynum", T_UINT, offsetof(SpriteMap, ynum), 0, "ynum"},
{"x", T_UINT, offsetof(SpriteMap, x), 0, "x"},
{"y", T_UINT, offsetof(SpriteMap, y), 0, "y"},
{"z", T_UINT, offsetof(SpriteMap, z), 0, "z"},
{NULL} /* Sentinel */
};
static PyMethodDef methods[] = {
METHOD(layout, METH_VARARGS)
METHOD(position_for, METH_VARARGS)
METHOD(render_dirty_cells, METH_VARARGS)
METHOD(increment, METH_NOARGS)
{NULL} /* Sentinel */
};
PyTypeObject SpriteMap_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.SpriteMap",
.tp_basicsize = sizeof(SpriteMap),
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "SpriteMap",
.tp_methods = methods,
.tp_members = members,
.tp_new = new,
};
INIT_TYPE(SpriteMap)
// }}}

39
kitty/sprites.h Normal file
View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#pragma once
typedef struct SpritePosition SpritePosition;
struct SpritePosition {
SpritePosition *next;
sprite_index x, y, z;
char_type ch;
combining_type cc;
bool is_second;
bool filled;
bool rendered;
};
PyObject* sprite_map_set_limits(PyObject UNUSED *self, PyObject *args);
PyObject* sprite_map_set_layout(PyObject UNUSED *s, PyObject *args);
PyObject* sprite_map_current_layout(PyObject UNUSED *s);
PyObject* sprite_map_free();
PyObject* sprite_map_increment();
SpritePosition* sprite_map_position_for(char_type ch, combining_type cc, bool is_second, int *error);
PyObject* sprite_position_for(PyObject UNUSED *self, PyObject *args);
bool update_cell_range_data(ScreenModes *modes, Line *line, unsigned int xstart, unsigned int xmax, unsigned int *data);
PyObject* render_dirty_sprites(PyObject UNUSED *self, PyObject *args);
#define SPRITE_FUNC_WRAPPERS \
{"sprite_map_set_limits", (PyCFunction)sprite_map_set_limits, METH_VARARGS, ""}, \
{"sprite_map_set_layout", (PyCFunction)sprite_map_set_layout, METH_VARARGS, ""}, \
{"sprite_map_current_layout", (PyCFunction)sprite_map_current_layout, METH_NOARGS, ""}, \
{"sprite_map_free", (PyCFunction)sprite_map_free, METH_NOARGS, ""}, \
{"sprite_map_increment", (PyCFunction)sprite_map_increment, METH_NOARGS, ""}, \
{"sprite_position_for", (PyCFunction)sprite_position_for, METH_VARARGS, ""}, \
{"render_dirty_sprites", (PyCFunction)render_dirty_sprites, METH_VARARGS, ""}, \

View File

@ -285,8 +285,7 @@ def update(self):
s.draw('')
break
s.erase_in_line(0, False) # Ensure no long titles bleed after the last tab
sprites = get_boss().sprites
s.update_cell_data(sprites.backend, addressof(self.render_buf), True)
s.update_cell_data(addressof(self.render_buf), True)
self.cell_ranges = cr
self.dirty = True
glfw_post_empty_event()

View File

@ -97,13 +97,13 @@ update_cell_range(ChangeTracker *self, PyObject *args) {
Py_RETURN_NONE;
}
bool tracker_update_cell_data(ScreenModes *modes, ChangeTracker *self, LineBuf *lb, SpriteMap *spm, unsigned int *data, bool force_screen_refresh) {
bool tracker_update_cell_data(ScreenModes *modes, ChangeTracker *self, LineBuf *lb, unsigned int *data, bool force_screen_refresh) {
unsigned int y;
Py_ssize_t start;
#define UPDATE_RANGE(xstart, xmax) \
linebuf_init_line(lb, y); \
if (!update_cell_range_data(modes, spm, lb->line, (xstart), (xmax), data)) return false;
if (!update_cell_range_data(modes, lb->line, (xstart), (xmax), data)) return false;
if (self->screen_changed || force_screen_refresh) {
for (y = 0; y < self->ynum; y++) {

View File

@ -52,4 +52,4 @@ static inline void tracker_reset(ChangeTracker *self) {
PyObject* tracker_consolidate_changes(ChangeTracker *self);
bool tracker_resize(ChangeTracker *self, unsigned int ynum, unsigned int xnum);
bool tracker_update_cell_data(ScreenModes*, ChangeTracker *, LineBuf *, SpriteMap *, unsigned int *, bool);
bool tracker_update_cell_data(ScreenModes*, ChangeTracker *, LineBuf *, unsigned int *, bool);

View File

@ -7,7 +7,8 @@
from kitty.config import build_ansi_color_table, defaults
from kitty.fast_data_types import (
REVERSE, ColorProfile, Cursor as C, HistoryBuf, LineBuf, SpriteMap, Timers
REVERSE, ColorProfile, Cursor as C, HistoryBuf, LineBuf, Timers,
sprite_map_set_layout, sprite_map_set_limits, sprite_position_for
)
from kitty.utils import sanitize_title, wcwidth
@ -283,17 +284,18 @@ def test_color_profile(self):
self.ae(c.as_color(255 << 8 | 1), (0xee, 0xee, 0xee))
def test_sprite_map(self):
s = SpriteMap(10, 2)
s.layout(5, 5)
self.ae(s.position_for(0), (0, 0, 0))
self.ae(s.position_for(1), (1, 0, 0))
self.ae(s.position_for(2), (0, 1, 0))
self.ae(s.position_for(3), (1, 1, 0))
self.ae(s.position_for(4), (0, 0, 1))
self.ae(s.position_for(5), (1, 0, 1))
self.ae(s.position_for(0, 1), (0, 1, 1))
self.ae(s.position_for(0, 2), (1, 1, 1))
self.ae(s.position_for(0, 2), (1, 1, 1))
sprite_map_set_limits(10, 2)
sprite_map_set_layout(5, 5)
self.ae(sprite_position_for(0), (0, 0, 0))
self.ae(sprite_position_for(1), (1, 0, 0))
self.ae(sprite_position_for(2), (0, 1, 0))
self.ae(sprite_position_for(3), (1, 1, 0))
self.ae(sprite_position_for(4), (0, 0, 1))
self.ae(sprite_position_for(5), (1, 0, 1))
self.ae(sprite_position_for(0, 1), (0, 1, 1))
self.ae(sprite_position_for(0, 2), (1, 1, 1))
self.ae(sprite_position_for(0, 2), (1, 1, 1))
sprite_map_set_limits(1000, 1000)
def test_historybuf(self):
lb = filled_line_buf()