From df4df76d24ea7c70cdaac5c8093cbe4bdf55851b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 17 Sep 2017 13:03:02 +0530 Subject: [PATCH] Switch to using an opaque block cursor The text is rendered in the background color on top of the opaque block cursor. We use the background color since applications can change both cursor and background colors, so it is up to the application to ensure the colors have good contrast. Fix #126 --- kitty/cell_vertex.glsl | 23 +++++++++-- kitty/child-monitor.c | 68 +++++++++++++++++++------------- kitty/config.py | 5 --- kitty/cursor_fragment.glsl | 4 +- kitty/cursor_vertex.glsl | 5 ++- kitty/kitty.conf | 5 +-- kitty/shaders.c | 81 ++++++++++++-------------------------- kitty/state.c | 1 - kitty/state.h | 14 +++++-- 9 files changed, 103 insertions(+), 103 deletions(-) diff --git a/kitty/cell_vertex.glsl b/kitty/cell_vertex.glsl index 2d8b72ffa..1483a0908 100644 --- a/kitty/cell_vertex.glsl +++ b/kitty/cell_vertex.glsl @@ -1,10 +1,9 @@ #version GLSL_VERSION -uniform uvec2 dimensions; // xnum, ynum +uniform uvec4 dimensions; // xnum, ynum, cursor.x, cursor.y uniform vec4 steps; // xstart, ystart, dx, dy uniform vec2 sprite_layout; // dx, dy uniform ivec2 color_indices; // which color to use as fg and which as bg -uniform uvec4 default_colors; // The default colors -uniform uint url_color; // The color to use for the URL highlight +uniform uint default_colors[6]; // The default colors uniform uvec4 url_range; // The range for the currently highlighted URL (start_x, end_x, start_y, end_y) uniform ColorTable { uint color_table[256]; // The color table @@ -83,10 +82,18 @@ float in_range(uvec4 range, uint x, uint y) { return 0.0; } +float is_cursor(uint x, uint y) { + if (x == dimensions[2] && y == dimensions[3]) return 1.0; + return 0.0; +} + void main() { uint instance_id = uint(gl_InstanceID); + // The current cell being rendered uint r = instance_id / dimensions.x; uint c = instance_id - r * dimensions.x; + + // The position of this vertex, at a corner of the cell float left = steps[0] + c * steps[2]; float top = steps[1] - r * steps[3]; vec2 xpos = vec2(left, left + steps[2]); @@ -94,15 +101,23 @@ void main() { uvec2 pos = pos_map[gl_VertexID]; gl_Position = vec4(xpos[pos.x], ypos[pos.y], 0, 1); + // The character sprite being rendered sprite_pos = to_sprite_pos(pos, sprite_coords.x, sprite_coords.y, sprite_coords.z & SHORT_MASK); + + // Foreground and background colors uint text_attrs = sprite_coords[3]; int fg_index = color_indices[(text_attrs >> 6) & REVERSE_MASK]; int bg_index = color_indices[1 - fg_index]; uint resolved_fg = as_color(colors[fg_index], default_colors[fg_index]); foreground = apply_selection(color_to_vec(resolved_fg), default_colors[2]); background = apply_selection(to_color(colors[bg_index], default_colors[bg_index]), default_colors[3]); + float cursor = is_cursor(c, r); + foreground = cursor * background + (1.0 - cursor) * foreground; + background = cursor * color_to_vec(default_colors[4]) + (1.0 - cursor) * background; + + // Underline and strike through (rendered via sprites) float in_url = in_range(url_range, c, r); - decoration_fg = mix_vecs(in_url, color_to_vec(url_color), to_color(colors[2], resolved_fg)); + decoration_fg = mix_vecs(in_url, color_to_vec(default_colors[5]), to_color(colors[2], resolved_fg)); underline_pos = mix_vecs(in_url, to_sprite_pos(pos, TWO, ZERO, ZERO), to_sprite_pos(pos, (text_attrs >> 2) & DECORATION_MASK, ZERO, ZERO)); strike_pos = to_sprite_pos(pos, ((text_attrs >> 7) & STRIKE_MASK) * THREE, ZERO, ZERO); } diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 30a8184f9..8cf2f51a7 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -451,9 +451,12 @@ cursor_width(double w, bool vert) { extern void cocoa_update_title(PyObject*); static inline void -render_cursor(Window *w, double now) { +collect_cursor_info(CursorRenderInfo *ans, Window *w, double now) { ScreenRenderData *rd = &w->render_data; - if (rd->screen->scrolled_by || !screen_is_cursor_visible(rd->screen)) return; + if (rd->screen->scrolled_by || !screen_is_cursor_visible(rd->screen)) { + ans->is_visible = false; + return; + } double time_since_start_blink = now - global_state.cursor_blink_zero_time; bool cursor_blinking = OPT(cursor_blink_interval) > 0 && global_state.application_focused && time_since_start_blink <= OPT(cursor_stop_blinking_after) ? true : false; bool do_draw_cursor = true; @@ -466,37 +469,50 @@ render_cursor(Window *w, double now) { double delay = (bucket / 1000.0) - time_since_start_blink; set_maximum_wait(delay); } - if (do_draw_cursor) { - Cursor *cursor = rd->screen->cursor; - double left = rd->xstart + cursor->x * rd->dx; - double top = rd->ystart - cursor->y * rd->dy; - int shape = cursor->shape ? cursor->shape : OPT(cursor_shape); - unsigned long mult = MAX(1, screen_current_char_width(rd->screen)); - double right = left + (shape == CURSOR_BEAM ? cursor_width(1.5, true) : rd->dx * mult); - double bottom = top - rd->dy; - if (shape == CURSOR_UNDERLINE) top = bottom + cursor_width(2.0, false); - bool semi_transparent = OPT(cursor_opacity) < 1.0 && shape == CURSOR_BLOCK ? true : false; - ColorProfile *cp = rd->screen->color_profile; - color_type col = colorprofile_to_color(cp, cp->overridden.cursor_color, cp->configured.cursor_color); - draw_cursor(semi_transparent, global_state.application_focused, col, OPT(cursor_opacity), left, right, top, bottom); + if (!do_draw_cursor) { ans->is_visible = false; return; } + ans->is_visible = true; + ColorProfile *cp = rd->screen->color_profile; + Cursor *cursor = rd->screen->cursor; + ans->shape = cursor->shape ? cursor->shape : OPT(cursor_shape); + ans->color = colorprofile_to_color(cp, cp->overridden.cursor_color, cp->configured.cursor_color); + if (ans->shape == CURSOR_BLOCK) return; + double left = rd->xstart + cursor->x * rd->dx; + double top = rd->ystart - cursor->y * rd->dy; + unsigned long mult = MAX(1, screen_current_char_width(rd->screen)); + double right = left + (ans->shape == CURSOR_BEAM ? cursor_width(1.5, true) : rd->dx * mult); + double bottom = top - rd->dy; + if (ans->shape == CURSOR_UNDERLINE) top = bottom + cursor_width(2.0, false); + ans->left = left; ans->right = right; ans->top = top; ans->bottom = bottom; +} + +static inline void +update_window_title(Window *w) { + if (w->title && w->title != global_state.application_title) { + global_state.application_title = w->title; + glfwSetWindowTitle(glfw_window_id, PyUnicode_AsUTF8(w->title)); +#ifdef __APPLE__ + cocoa_update_title(w->title); +#endif } } static inline void render(double now) { double time_since_last_render = now - last_render_at; + static CursorRenderInfo cursor_info; if (time_since_last_render < OPT(repaint_delay)) { set_maximum_wait(OPT(repaint_delay) - time_since_last_render); return; } draw_borders(); + cursor_info.is_visible = false; #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); + if (TD.screen && global_state.num_tabs > 1) draw_cells(TD.vao_idx, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, &cursor_info); #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++) { + for (unsigned int i = 0; i < tab->num_windows; i++) { Window *w = tab->windows + i; #define WD w->render_data if (w->visible && WD.screen) { @@ -508,22 +524,20 @@ render(double now) { } else w->last_drag_scroll_at = 0; } else set_maximum_wait(now - w->last_drag_scroll_at); } - draw_cells(WD.vao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen); + bool is_active_window = i == tab->active_window; + if (is_active_window) { + collect_cursor_info(&cursor_info, w, now); + update_window_title(w); + } else cursor_info.is_visible = false; + draw_cells(WD.vao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, &cursor_info); + if (is_active_window && cursor_info.is_visible && cursor_info.shape != CURSOR_BLOCK) draw_cursor(&cursor_info); if (WD.screen->start_visual_bell_at != 0) { double bell_left = global_state.opts.visual_bell_duration - (now - WD.screen->start_visual_bell_at); set_maximum_wait(bell_left); } } } - Window *w = tab->windows + tab->active_window; - if (w->visible && WD.screen) render_cursor(w, now); - if (w->title && w->title != global_state.application_title) { - global_state.application_title = w->title; - glfwSetWindowTitle(glfw_window_id, PyUnicode_AsUTF8(w->title)); -#ifdef __APPLE__ - cocoa_update_title(w->title); -#endif - } + #undef WD } glfwSwapBuffers(glfw_window_id); diff --git a/kitty/config.py b/kitty/config.py index d34c7a266..3feb6b093 100644 --- a/kitty/config.py +++ b/kitty/config.py @@ -47,10 +47,6 @@ def to_bool(x): return x.lower() in 'y yes true'.split() -def to_opacity(x): - return max(0.3, min(float(x), 1)) - - def parse_mods(parts): def map_mod(m): @@ -212,7 +208,6 @@ type_map = { 'font_size': to_font_size, 'font_size_delta': positive_float, 'cursor_shape': to_cursor_shape, - 'cursor_opacity': to_opacity, 'open_url_modifiers': to_open_url_modifiers, 'repaint_delay': positive_int, 'input_delay': positive_int, diff --git a/kitty/cursor_fragment.glsl b/kitty/cursor_fragment.glsl index 45e832a81..a07f9f498 100644 --- a/kitty/cursor_fragment.glsl +++ b/kitty/cursor_fragment.glsl @@ -1,7 +1,7 @@ #version GLSL_VERSION -uniform vec4 color; +uniform vec3 color; out vec4 final_color; void main() { - final_color = color; + final_color = vec4(color, 1); } diff --git a/kitty/cursor_vertex.glsl b/kitty/cursor_vertex.glsl index bc33d1b60..3329f2063 100644 --- a/kitty/cursor_vertex.glsl +++ b/kitty/cursor_vertex.glsl @@ -1,6 +1,5 @@ #version GLSL_VERSION -uniform vec2 xpos; -uniform vec2 ypos; +uniform vec4 pos; const uvec2 pos_map[] = uvec2[4]( uvec2(1, 0), // right, top @@ -10,6 +9,8 @@ const uvec2 pos_map[] = uvec2[4]( ); void main() { + vec2 xpos = vec2(pos[0], pos[2]); + vec2 ypos = vec2(pos[1], pos[3]); uvec2 pos = pos_map[gl_VertexID]; gl_Position = vec4(xpos[pos[0]], ypos[pos[1]], 0, 1); } diff --git a/kitty/kitty.conf b/kitty/kitty.conf index 8f44334c5..5059db8b8 100644 --- a/kitty/kitty.conf +++ b/kitty/kitty.conf @@ -49,10 +49,7 @@ selection_background #FFFACD url_color #0087BD # The cursor color -cursor #ffffff - -# The cursor opacity -cursor_opacity 0.7 +cursor #cccccc # The cursor shape can be one of (block, beam, underline) cursor_shape block diff --git a/kitty/shaders.c b/kitty/shaders.c index 9a35ce25d..7411d5b44 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -552,33 +552,32 @@ destroy_sprite_map() { // Cell {{{ -enum CellUniforms { CELL_dimensions, CELL_default_colors, CELL_color_indices, CELL_steps, CELL_sprites, CELL_sprite_layout, CELL_url_color, CELL_url_range, CELL_color_table, NUM_CELL_UNIFORMS }; +enum CellUniforms { CELL_dimensions, CELL_default_colors, CELL_color_indices, CELL_steps, CELL_sprites, CELL_sprite_layout, CELL_url_range, CELL_color_table, NUM_CELL_UNIFORMS }; static GLint cell_uniform_locations[NUM_CELL_UNIFORMS] = {0}; +static GLint cell_uniform_indices[NUM_CELL_UNIFORMS] = {0}; static GLint cell_color_table_stride = 0, cell_color_table_offset = 0, cell_color_table_size = 0, cell_color_table_block_index = 0; static void init_cell_program() { Program *p = programs + CELL_PROGRAM; int left = NUM_CELL_UNIFORMS; - GLint ctable_idx = 0; for (int i = 0; i < p->num_of_uniforms; i++, left--) { -#define SET_LOC(which) if (strcmp(p->uniforms[i].name, #which) == 0) cell_uniform_locations[CELL_##which] = p->uniforms[i].location - SET_LOC(dimensions); - else SET_LOC(default_colors); - else SET_LOC(color_indices); - else SET_LOC(steps); - else SET_LOC(sprites); - else SET_LOC(sprite_layout); - else SET_LOC(url_range); - else SET_LOC(url_color); - else if (strcmp(p->uniforms[i].name, "color_table[0]") == 0) { ctable_idx = i; cell_uniform_locations[CELL_color_table] = p->uniforms[i].location; } +#define SET_LOC(which) if (strcmp(p->uniforms[i].name, #which) == 0 || strcmp(p->uniforms[i].name, #which "[0]") == 0) { cell_uniform_locations[CELL_##which] = p->uniforms[i].location; cell_uniform_indices[CELL_##which] = i; } + SET_LOC(dimensions) + else SET_LOC(color_table) + else SET_LOC(default_colors) + else SET_LOC(color_indices) + else SET_LOC(steps) + else SET_LOC(sprites) + else SET_LOC(sprite_layout) + else SET_LOC(url_range) else { fatal("Unknown uniform in cell program: %s", p->uniforms[i].name); } } if (left) { fatal("Left over uniforms in cell program"); } cell_color_table_block_index = block_index(CELL_PROGRAM, "ColorTable"); cell_color_table_size = block_size(CELL_PROGRAM, cell_color_table_block_index); cell_color_table_stride = cell_color_table_size / (256 * sizeof(GLuint)); - cell_color_table_offset = block_offset(CELL_PROGRAM, ctable_idx); + cell_color_table_offset = block_offset(CELL_PROGRAM, cell_uniform_indices[CELL_color_table]); #undef SET_LOC } @@ -601,10 +600,9 @@ create_cell_vao() { #undef A1 } -static bool cell_constants_sent = false; static void -draw_cells_impl(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, CursorRenderInfo *cursor) { size_t sz; void *address; bool inverted = screen_invert_colors(screen); @@ -625,16 +623,20 @@ draw_cells_impl(ssize_t vao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLf copy_color_table_to_buffer(screen->color_profile, address, cell_color_table_offset, cell_color_table_stride); unmap_vao_buffer(vao_idx, 2); } + index_type cx = screen->columns, cy = screen->lines; + if (cursor->is_visible && cursor->shape == CURSOR_BLOCK) { cx = screen->cursor->x, cy = screen->cursor->y; } int sprite_map_unit = bind_sprite_map(); render_dirty_sprites(render_and_send_dirty_sprites); #define UL(name) cell_uniform_locations[CELL_##name] bind_program(CELL_PROGRAM); bind_vao_uniform_buffer(vao_idx, 2, cell_color_table_block_index); - glUniform2ui(UL(dimensions), screen->columns, screen->lines); check_gl(); + glUniform4ui(UL(dimensions), screen->columns, screen->lines, cx, cy); check_gl(); glUniform4f(UL(steps), xstart, ystart, dx, dy); check_gl(); glUniform2i(UL(color_indices), inverted & 1, 1 - (inverted & 1)); check_gl(); #define COLOR(name) colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.name, screen->color_profile->configured.name) - glUniform4ui(UL(default_colors), COLOR(default_fg), COLOR(default_bg), COLOR(highlight_fg), COLOR(highlight_bg)); check_gl(); + static GLuint colors[6]; + colors[0] = COLOR(default_fg); colors[1] = COLOR(default_bg); colors[2] = COLOR(highlight_fg); colors[3] = COLOR(highlight_bg); colors[4] = cursor->color; colors[5] = OPT(url_color); + glUniform1uiv(UL(default_colors), sizeof(colors)/sizeof(colors[0]), colors); check_gl(); #undef COLOR GLuint start_x, start_y, end_x, end_y; screen_url_range(screen, &start_x, &start_y, &end_x, &end_y); @@ -643,10 +645,6 @@ draw_cells_impl(ssize_t vao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLf unsigned int x, y, z; sprite_map_current_layout(&x, &y, &z); glUniform2f(UL(sprite_layout), 1.0 / (float)x, 1.0 / (float)y); check_gl(); - if (!cell_constants_sent) { - glUniform1ui(UL(url_color), OPT(url_color)); check_gl(); - cell_constants_sent = true; - } bind_vertex_array(vao_idx); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); check_gl(); unbind_vertex_array(); @@ -657,7 +655,7 @@ draw_cells_impl(ssize_t vao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLf // }}} // Cursor {{{ -enum CursorUniforms { CURSOR_color, CURSOR_xpos, CURSOR_ypos, NUM_CURSOR_UNIFORMS }; +enum CursorUniforms { CURSOR_color, CURSOR_pos, NUM_CURSOR_UNIFORMS }; static GLint cursor_uniform_locations[NUM_CURSOR_UNIFORMS] = {0}; static ssize_t cursor_vertex_array; @@ -669,8 +667,7 @@ init_cursor_program() { for (int i = 0; i < p->num_of_uniforms; i++, left--) { #define SET_LOC(which) if (strcmp(p->uniforms[i].name, #which) == 0) cursor_uniform_locations[CURSOR_##which] = p->uniforms[i].location SET_LOC(color); - else SET_LOC(xpos); - else SET_LOC(ypos); + else SET_LOC(pos); else { fatal("Unknown uniform in cursor program"); } } if (left) { fatal("Left over uniforms in cursor program"); } @@ -678,19 +675,12 @@ init_cursor_program() { } static void -draw_cursor_impl(bool semi_transparent, bool is_focused, color_type color, float alpha, float left, float right, float top, float bottom) { - if (semi_transparent) { glEnable(GL_BLEND); check_gl(); } - bind_program(CURSOR_PROGRAM); bind_vertex_array(cursor_vertex_array); - glUniform4f(cursor_uniform_locations[CURSOR_color], ((color >> 16) & 0xff) / 255.0, ((color >> 8) & 0xff) / 255.0, (color & 0xff) / 255.0, alpha); - check_gl(); - glUniform2f(cursor_uniform_locations[CURSOR_xpos], left, right); - check_gl(); - glUniform2f(cursor_uniform_locations[CURSOR_ypos], top, bottom); - check_gl(); - glDrawArrays(is_focused ? GL_TRIANGLE_FAN : GL_LINE_LOOP, 0, 4); - check_gl(); +draw_cursor_impl(CursorRenderInfo *cursor) { + bind_program(CURSOR_PROGRAM); bind_vertex_array(cursor_vertex_array); check_gl(); + glUniform3f(cursor_uniform_locations[CURSOR_color], ((cursor->color >> 16) & 0xff) / 255.0, ((cursor->color >> 8) & 0xff) / 255.0, (cursor->color & 0xff) / 255.0); check_gl(); + glUniform4f(cursor_uniform_locations[CURSOR_pos], cursor->left, cursor->top, cursor->right, cursor->bottom); check_gl(); + glDrawArrays(global_state.application_focused ? GL_TRIANGLE_FAN : GL_LINE_LOOP, 0, 4); check_gl(); unbind_vertex_array(); unbind_program(); - if (semi_transparent) { glDisable(GL_BLEND); check_gl(); } } // }}} @@ -826,14 +816,6 @@ PYWRAP1(map_vao_buffer) { } NO_ARG(init_cursor_program) -PYWRAP1(draw_cursor) { - int semi_transparent, is_focused; - unsigned int color; - float alpha, left, right, top, bottom; - PA("ppIfffff", &semi_transparent, &is_focused, &color, &alpha, &left, &right, &top, &bottom); - draw_cursor(semi_transparent, is_focused, color, alpha, left, right, top, bottom); - Py_RETURN_NONE; -} NO_ARG(init_borders_program) PYWRAP1(add_borders_rect) { unsigned int a, b, c, d, e; PA("IIIII", &a, &b, &c, &d, &e); add_borders_rect(a, b, c, d, e); Py_RETURN_NONE; } @@ -841,15 +823,6 @@ TWO_INT(send_borders_rects) NO_ARG(init_cell_program) NO_ARG_INT(create_cell_vao) -PYWRAP1(draw_cells) { - float xstart, ystart, dx, dy; - int vao_idx; - Screen *screen; - - PA("iffffO", &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) PYWRAP1(layout_sprite_map) { unsigned int cell_width, cell_height; @@ -899,7 +872,6 @@ 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), @@ -911,7 +883,6 @@ static PyMethodDef module_methods[] = { MW(bind_program, METH_O), MW(unbind_program, METH_NOARGS), MW(init_cursor_program, METH_NOARGS), - MW(draw_cursor, METH_VARARGS), MW(init_borders_program, METH_NOARGS), MW(add_borders_rect, METH_VARARGS), MW(send_borders_rects, METH_VARARGS), diff --git a/kitty/state.c b/kitty/state.c index ab4915070..9ed6e9335 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -154,7 +154,6 @@ PYWRAP1(set_options) { S(cursor_blink_interval, PyFloat_AsDouble); S(cursor_stop_blinking_after, PyFloat_AsDouble); S(cursor_shape, PyLong_AsLong); - S(cursor_opacity, PyFloat_AsDouble); S(mouse_hide_wait, PyFloat_AsDouble); S(wheel_scroll_multiplier, PyFloat_AsDouble); S(open_url_modifiers, PyLong_AsUnsignedLong); diff --git a/kitty/state.h b/kitty/state.h index 6c5d9961e..36dfa9ce3 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -10,7 +10,7 @@ #define OPT(name) global_state.opts.name typedef struct { - double visual_bell_duration, cursor_blink_interval, cursor_stop_blinking_after, mouse_hide_wait, click_interval, cursor_opacity, wheel_scroll_multiplier; + double visual_bell_duration, cursor_blink_interval, cursor_stop_blinking_after, mouse_hide_wait, click_interval, wheel_scroll_multiplier; bool enable_audio_bell; CursorShape cursor_shape; unsigned int open_url_modifiers; @@ -77,6 +77,14 @@ typedef struct { } GlobalState; extern GlobalState global_state; + +typedef struct { + bool is_visible; + CursorShape shape; + double left, right, top, bottom; + color_type color; +} CursorRenderInfo; + bool drag_scroll(Window *); #define call_boss(name, ...) { \ @@ -88,6 +96,6 @@ bool drag_scroll(Window *); #define EXTERNAL_FUNC(name, ret, ...) typedef ret (*name##_func)(__VA_ARGS__); extern name##_func name #define EXTERNAL_FUNC0(name, ret) typedef ret (*name##_func)(); extern name##_func name EXTERNAL_FUNC0(draw_borders, void); -EXTERNAL_FUNC(draw_cells, void, ssize_t, float, float, float, float, Screen *); -EXTERNAL_FUNC(draw_cursor, void, bool, bool, color_type, float, float, float, float, float); +EXTERNAL_FUNC(draw_cells, void, ssize_t, float, float, float, float, Screen *, CursorRenderInfo *); +EXTERNAL_FUNC(draw_cursor, void, CursorRenderInfo *); EXTERNAL_FUNC(update_viewport_size, void, int, int);