diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 37f4666fd..7988a5b61 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -24,11 +24,9 @@ extern int pthread_setname_np(const char *name); #include #include #include -#include extern PyTypeObject Screen_Type; #define EXTRA_FDS 2 -#define wakeup_main_loop glfwPostEmptyEvent static void (*parse_func)(Screen*, PyObject*); @@ -58,7 +56,6 @@ static bool signal_received = false; static ChildMonitor *the_monitor = NULL; static uint8_t drain_buf[1024]; static int signal_fds[2], wakeup_fds[2]; -static void *glfw_window_id = NULL; static inline void @@ -120,12 +117,11 @@ self_pipe(int fds[2]) { static PyObject * new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { ChildMonitor *self; - PyObject *dump_callback, *death_notify, *wid; + PyObject *dump_callback, *death_notify; int ret; if (the_monitor) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; } - if (!PyArg_ParseTuple(args, "OOO", &wid, &death_notify, &dump_callback)) return NULL; - glfw_window_id = PyLong_AsVoidPtr(wid); + if (!PyArg_ParseTuple(args, "OO", &death_notify, &dump_callback)) return NULL; if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) { PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret)); return NULL; @@ -313,7 +309,7 @@ parse_input(ChildMonitor *self) { } if (UNLIKELY(signal_received)) { - glfwSetWindowShouldClose(glfw_window_id, true); + global_state.close_all_windows = true; } else { count = self->count; for (size_t i = 0; i < count; i++) { @@ -436,24 +432,26 @@ pyset_iutf8(ChildMonitor *self, PyObject *args) { static double last_render_at = -DBL_MAX; static inline double -cursor_width(double w, bool vert) { +cursor_width(double w, bool vert, OSWindow *os_window) { double dpi = vert ? global_state.logical_dpi_x : global_state.logical_dpi_y; double ans = w * dpi / 72.0; // as pixels - double factor = 2.0 / (vert ? global_state.viewport_width : global_state.viewport_height); + double factor = 2.0 / (vert ? os_window->viewport_width : os_window->viewport_height); return ans * factor; } extern void cocoa_update_title(PyObject*); static inline void -collect_cursor_info(CursorRenderInfo *ans, Window *w, double now) { +collect_cursor_info(CursorRenderInfo *ans, Window *w, double now, OSWindow *os_window) { ScreenRenderData *rd = &w->render_data; + Cursor *cursor = rd->screen->cursor; + ans->x = cursor->x; ans->y = cursor->y; + ans->is_visible = false; 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; + double time_since_start_blink = now - os_window->cursor_blink_zero_time; + bool cursor_blinking = OPT(cursor_blink_interval) > 0 && os_window->is_focused && time_since_start_blink <= OPT(cursor_stop_blinking_after) ? true : false; bool do_draw_cursor = true; if (cursor_blinking) { int t = (int)(time_since_start_blink * 1000); @@ -467,28 +465,29 @@ collect_cursor_info(CursorRenderInfo *ans, Window *w, double now) { 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 right = left + (ans->shape == CURSOR_BEAM ? cursor_width(1.5, true, os_window) : rd->dx * mult); double bottom = top - rd->dy; - if (ans->shape == CURSOR_UNDERLINE) top = bottom + cursor_width(2.0, false); + if (ans->shape == CURSOR_UNDERLINE) top = bottom + cursor_width(2.0, false, os_window); 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)); +static inline bool +update_window_title(Window *w, OSWindow *os_window) { + if (w->title && w->title != os_window->window_title) { + os_window->window_title = w->title; + set_os_window_title(os_window, PyUnicode_AsUTF8(w->title)); #ifdef __APPLE__ - cocoa_update_title(w->title); + if (os_window == global_state.focussed_os_window) cocoa_update_title(w->title); #endif + return true; } + return false; } static PyObject* @@ -498,34 +497,23 @@ simple_render_screen(PyObject UNUSED *self, PyObject *args) { ssize_t vao_idx, gvao_idx; float xstart, ystart, dx, dy; if (!PyArg_ParseTuple(args, "O!nnffff", &Screen_Type, &screen, &vao_idx, &gvao_idx, &xstart, &ystart, &dx, &dy)) return NULL; - static CursorRenderInfo cursor_info = {0}; - draw_cells(vao_idx, gvao_idx, xstart, ystart, dx, dy, screen, &cursor_info); - Py_RETURN_NONE; + PyObject *ret = draw_cells(vao_idx, gvao_idx, xstart, ystart, dx, dy, screen, current_os_window()) ? Py_True : Py_False; + Py_INCREF(ret); return ret; } -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, 0, 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; +static inline bool +render_os_window(OSWindow *os_window, double now, unsigned int *active_window_id) { + bool dirtied = false; + if (OPT(mouse_hide_wait) > 0 && now - os_window->last_mouse_activity_at > OPT(mouse_hide_wait)) hide_mouse(os_window); + if (os_window->num_tabs) { + Tab *tab = os_window->tabs + os_window->active_tab; 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) { if (w->last_drag_scroll_at > 0) { if (now - w->last_drag_scroll_at >= 0.02) { - if (drag_scroll(w)) { + if (drag_scroll(w, os_window)) { w->last_drag_scroll_at = now; set_maximum_wait(0.02); } else w->last_drag_scroll_at = 0; @@ -533,24 +521,55 @@ render(double now) { } 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.gvao_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); + *active_window_id = w->id; + collect_cursor_info(&WD.screen->current_cursor_render_info, w, now, os_window); + if (update_window_title(w, os_window)) dirtied = true; + } else WD.screen->current_cursor_render_info.is_visible = false; + if (draw_cells(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, os_window)) { + if (is_active_window && WD.screen->current_cursor_render_info.is_visible && WD.screen->current_cursor_render_info.shape != CURSOR_BLOCK) { + draw_cursor(&WD.screen->current_cursor_render_info, os_window == global_state.focussed_os_window); + } + dirtied = true; + } 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); } } } - #undef WD } - glfwSwapBuffers(glfw_window_id); - last_render_at = now; + return dirtied; } +static inline void +render(double now) { + double time_since_last_render = now - last_render_at; + if (time_since_last_render < OPT(repaint_delay)) { + set_maximum_wait(OPT(repaint_delay) - time_since_last_render); + return; + } +#define TD w->tab_bar_render_data + + for (size_t i = 0; i < global_state.num_os_windows; i++) { + OSWindow *w = global_state.os_windows + i; + if (!should_os_window_be_rendered(w)) continue; + make_window_context_current(w); + unsigned int active_window_id = 0; + bool window_rendered = render_os_window(w, now, &active_window_id); + bool tab_bar_changed = w->num_tabs > 1 && (w->last_active_tab != w->active_tab || w->last_num_tabs != w->num_tabs); + if (window_rendered || tab_bar_changed || active_window_id != w->last_active_window_id) { + draw_borders(); + if (TD.screen && w->num_tabs > 1) draw_cells(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, w); + swap_window_buffers(w); + w->last_active_tab = w->active_tab; w->last_num_tabs = w->num_tabs; w->last_active_window_id = active_window_id; + } + } + last_render_at = now; +#undef TD +} + + typedef struct { int fd; uint8_t *buf; size_t sz; } ThreadWriteData; static inline ThreadWriteData* @@ -599,18 +618,9 @@ cm_thread_write(PyObject UNUSED *self, PyObject *args) { Py_RETURN_NONE; } -static inline void -hide_mouse(double now) { - if (glfwGetInputMode(glfw_window_id, GLFW_CURSOR) == GLFW_CURSOR_NORMAL && OPT(mouse_hide_wait) > 0 && now - global_state.last_mouse_activity_at > OPT(mouse_hide_wait)) { - glfwSetInputMode(glfw_window_id, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); - } -} - - static inline void wait_for_events() { - if (maximum_wait < 0) glfwWaitEvents(); - else if (maximum_wait > 0) glfwWaitEventsTimeout(maximum_wait); + event_loop_wait(maximum_wait); maximum_wait = -1; } @@ -618,12 +628,24 @@ wait_for_events() { static PyObject* main_loop(ChildMonitor *self) { #define main_loop_doc "The main thread loop" - while (!glfwWindowShouldClose(glfw_window_id)) { + bool has_open_windows = true; + while (has_open_windows) { double now = monotonic(); render(now); - hide_mouse(now); wait_for_events(); parse_input(self); + if (global_state.close_all_windows) { + for (size_t w = 0; w < global_state.num_os_windows; w++) mark_os_window_for_close(&global_state.os_windows[w], true); + global_state.close_all_windows = false; + } + has_open_windows = false; + for (size_t w = 0; w < global_state.num_os_windows; w++) { + if (!should_os_window_close(&global_state.os_windows[w])) { + has_open_windows = true; + break; + } + } + } if (PyErr_Occurred()) return NULL; Py_RETURN_NONE; diff --git a/kitty/data-types.h b/kitty/data-types.h index 75d7eec1c..9d2353922 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -172,6 +172,14 @@ typedef struct { } Cursor; +typedef struct { + bool is_visible; + CursorShape shape; + unsigned int x, y; + double left, right, top, bottom; + color_type color; +} CursorRenderInfo; + typedef struct { color_type default_fg, default_bg, cursor_color, highlight_fg, highlight_bg; } DynamicColor; diff --git a/kitty/glfw.c b/kitty/glfw.c index 55b1accf8..24e5caa77 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -182,7 +182,7 @@ create_new_os_window(PyObject UNUSED *self, PyObject *args) { glfwSetWindowUserPointer(glfw_window, w); w->handle = glfw_window; glfwSetCursor(glfw_window, standard_cursor); - global_state.callback_os_window->viewport_size_dirty = true; + w->viewport_size_dirty = true; update_viewport(w); glfwSetFramebufferSizeCallback(glfw_window, framebuffer_size_callback); glfwSetCharModsCallback(glfw_window, char_mods_callback); @@ -394,6 +394,55 @@ request_window_attention(unsigned int kitty_window_id) { } } +void +set_os_window_title(OSWindow *w, const char *title) { + glfwSetWindowTitle(w->handle, title); +} + +void +hide_mouse(OSWindow *w) { + glfwSetInputMode(w->handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); +} + +void +make_window_context_current(OSWindow *w) { + glfwMakeContextCurrent(w->handle); + if (w->viewport_size_dirty) update_viewport_size(w->viewport_width, w->viewport_height); +} + +void +swap_window_buffers(OSWindow *w) { + glfwSwapBuffers(w->handle); +} + +void +event_loop_wait(double timeout) { + if (timeout < 0) glfwWaitEvents(); + else if (timeout > 0) glfwWaitEventsTimeout(timeout); +} + +void +wakeup_main_loop() { + glfwPostEmptyEvent(); +} + +void +mark_os_window_for_close(OSWindow* w, bool yes) { + glfwSetWindowShouldClose(w->handle, yes); +} + +bool +should_os_window_be_rendered(OSWindow* w) { + if (glfwGetWindowAttrib(w->handle, GLFW_ICONIFIED)) return false; + if (!glfwGetWindowAttrib(w->handle, GLFW_VISIBLE)) return false; + return true; +} + +bool +should_os_window_close(OSWindow* w) { + return glfwWindowShouldClose(w->handle) ? true : false; +} + static PyObject* primary_monitor_size(PyObject UNUSED *self) { GLFWmonitor* monitor = glfwGetPrimaryMonitor(); diff --git a/kitty/screen.h b/kitty/screen.h index 96a893efa..d8437caa9 100644 --- a/kitty/screen.h +++ b/kitty/screen.h @@ -55,6 +55,10 @@ typedef struct { size_t read_buf_sz, write_buf_sz, write_buf_used; pthread_mutex_t read_buf_lock, write_buf_lock; + CursorRenderInfo last_cursor_render_info, current_cursor_render_info; + bool colors_inverted_at_last_render; + float last_render_x_start, last_render_y_start; + } Screen; diff --git a/kitty/shaders.c b/kitty/shaders.c index 3c7c1b591..c5bd4e259 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -203,7 +203,7 @@ create_graphics_vao() { } static inline void -cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, CursorRenderInfo *cursor) { +cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, CursorRenderInfo *cursor, bool inverted) { struct CellRenderData { GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy; @@ -215,7 +215,6 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G }; static struct CellRenderData *rd; - bool inverted = screen_invert_colors(screen); // Send the uniform data rd = (struct CellRenderData*)map_vao_buffer(vao_idx, uniform_buffer, GL_WRITE_ONLY); if (UNLIKELY(screen->color_profile->dirty)) { @@ -246,11 +245,12 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G unmap_vao_buffer(vao_idx, uniform_buffer); rd = NULL; } -static inline void -cell_prepare_to_render(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, CursorRenderInfo *cursor) { +static inline bool +cell_prepare_to_render(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy) { size_t sz; CELL_BUFFERS; void *address; + bool needs_render = false; ensure_sprite_map(); @@ -259,6 +259,7 @@ cell_prepare_to_render(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, GLfloa address = alloc_and_map_vao_buffer(vao_idx, sz, cell_data_buffer, GL_STREAM_DRAW, GL_WRITE_ONLY); screen_update_cell_data(screen, address, sz); unmap_vao_buffer(vao_idx, cell_data_buffer); address = NULL; + needs_render = true; } if (screen_is_selection_dirty(screen)) { @@ -266,6 +267,7 @@ cell_prepare_to_render(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, GLfloa address = alloc_and_map_vao_buffer(vao_idx, sz, selection_buffer, GL_STREAM_DRAW, GL_WRITE_ONLY); screen_apply_selection(screen, address, sz); unmap_vao_buffer(vao_idx, selection_buffer); address = NULL; + needs_render = true; } if (gvao_idx && grman_update_layers(screen->grman, screen->scrolled_by, xstart, ystart, dx, dy, screen->columns, screen->lines)) { @@ -273,12 +275,28 @@ cell_prepare_to_render(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, GLfloa GLfloat *a = alloc_and_map_vao_buffer(gvao_idx, sz, 0, GL_STREAM_DRAW, GL_WRITE_ONLY); for (size_t i = 0; i < screen->grman->count; i++, a += 16) memcpy(a, screen->grman->render_data[i].vertices, sizeof(screen->grman->render_data[0].vertices)); unmap_vao_buffer(gvao_idx, 0); a = NULL; + needs_render = true; } + bool inverted = screen_invert_colors(screen); + if (inverted != screen->colors_inverted_at_last_render) needs_render = true; - cell_update_uniform_block(vao_idx, screen, uniform_buffer, xstart, ystart, dx, dy, cursor); + if (!needs_render && (screen->last_render_x_start != xstart || screen->last_render_y_start != ystart)) needs_render = true; +#define CC(attr) (screen->last_cursor_render_info.attr != screen->current_cursor_render_info.attr) + if (!needs_render && (CC(is_visible) || CC(shape) || CC(x) || CC(y) || CC(color))) needs_render = true; +#undef CC - bind_vao_uniform_buffer(vao_idx, uniform_buffer, cell_program_layouts[CELL_PROGRAM].render_data.index); - bind_vertex_array(vao_idx); + if (needs_render) { + cell_update_uniform_block(vao_idx, screen, uniform_buffer, xstart, ystart, dx, dy, &screen->current_cursor_render_info, inverted); + + bind_vao_uniform_buffer(vao_idx, uniform_buffer, cell_program_layouts[CELL_PROGRAM].render_data.index); + bind_vertex_array(vao_idx); + screen->colors_inverted_at_last_render = inverted; + screen->last_render_x_start = xstart; screen->last_render_y_start = ystart; +#define CC(attr) screen->last_cursor_render_info.attr = screen->current_cursor_render_info.attr + CC(is_visible); CC(shape); CC(x); CC(y); CC(color); +#undef CC + } + return needs_render; } static void @@ -336,10 +354,12 @@ draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen) { if (screen->grman->num_of_positive_refs) draw_graphics(vao_idx, gvao_idx, screen->grman->render_data, screen->grman->num_of_negative_refs, screen->grman->num_of_positive_refs); } -void -draw_cells(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, Screen *screen, CursorRenderInfo *cursor) { +bool +draw_cells(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, Screen *screen, OSWindow *os_window) { + bool needs_render = cell_prepare_to_render(vao_idx, gvao_idx, screen, xstart, ystart, dx, dy); + if (!needs_render) return false; GLfloat h = (GLfloat)screen->lines * dy; -#define SCALE(w, x) ((GLfloat)(global_state.viewport_##w) * (GLfloat)(x)) +#define SCALE(w, x) ((GLfloat)(os_window->viewport_##w) * (GLfloat)(x)) glScissor( (GLint)(SCALE(width, (xstart + 1.0f) / 2.0f)), (GLint)(SCALE(height, ((ystart - h) + 1.0f) / 2.0f)), @@ -347,9 +367,9 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat ystart, GL (GLsizei)(ceilf(SCALE(height, h / 2.0f))) ); #undef SCALE - cell_prepare_to_render(vao_idx, gvao_idx, screen, xstart, ystart, dx, dy, cursor); if (screen->grman->num_of_negative_refs) draw_cells_interleaved(vao_idx, gvao_idx, screen); else draw_all_cells(vao_idx, gvao_idx, screen); + return true; } // }}} @@ -374,11 +394,11 @@ init_cursor_program() { } void -draw_cursor(CursorRenderInfo *cursor) { +draw_cursor(CursorRenderInfo *cursor, bool is_focused) { bind_program(CURSOR_PROGRAM); bind_vertex_array(cursor_vertex_array); glUniform3f(cursor_uniform_locations[CURSOR_color], ((cursor->color >> 16) & 0xff) / 255.0, ((cursor->color >> 8) & 0xff) / 255.0, (cursor->color & 0xff) / 255.0); glUniform4f(cursor_uniform_locations[CURSOR_pos], cursor->left, cursor->top, cursor->right, cursor->bottom); - glDrawArrays(global_state.application_focused ? GL_TRIANGLE_FAN : GL_LINE_LOOP, 0, 4); + glDrawArrays(is_focused ? GL_TRIANGLE_FAN : GL_LINE_LOOP, 0, 4); unbind_vertex_array(); unbind_program(); } // }}} diff --git a/kitty/state.h b/kitty/state.h index 435a00845..1a1707301 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -76,7 +76,7 @@ typedef struct { int viewport_width, viewport_height; double viewport_x_ratio, viewport_y_ratio; Tab tabs[MAX_CHILDREN]; - unsigned int active_tab, num_tabs; + unsigned int active_tab, num_tabs, last_active_tab, last_num_tabs, last_active_window_id; ScreenRenderData tab_bar_render_data; bool is_focused; double cursor_blink_zero_time, last_mouse_activity_at; @@ -85,6 +85,7 @@ typedef struct { PyObject *window_title; bool is_key_pressed[MAX_KEY_COUNT]; bool viewport_size_dirty; + bool needs_render; } OSWindow; @@ -99,29 +100,32 @@ typedef struct { OSWindow os_windows[MAX_CHILDREN]; size_t num_os_windows; OSWindow *callback_os_window, *focussed_os_window; + bool close_all_windows; } GlobalState; extern GlobalState global_state; -typedef struct { - bool is_visible; - CursorShape shape; - double left, right, top, bottom; - color_type color; -} CursorRenderInfo; - #define call_boss(name, ...) { \ PyObject *cret_ = PyObject_CallMethod(global_state.boss, #name, __VA_ARGS__); \ if (cret_ == NULL) { PyErr_Print(); } \ else Py_DECREF(cret_); \ } +void mark_os_window_for_close(OSWindow* w, bool yes); +bool should_os_window_close(OSWindow* w); +bool should_os_window_be_rendered(OSWindow* w); +void wakeup_main_loop(); +void event_loop_wait(double timeout); +void swap_window_buffers(OSWindow *w); +void make_window_context_current(OSWindow *w); +void hide_mouse(OSWindow *w); +void set_os_window_title(OSWindow *w, const char *title); OSWindow* os_window_for_kitty_window(unsigned int); OSWindow* current_os_window(); bool drag_scroll(Window *, OSWindow*); void draw_borders(); -void draw_cells(ssize_t, ssize_t, float, float, float, float, Screen *, CursorRenderInfo *); -void draw_cursor(CursorRenderInfo *); +bool draw_cells(ssize_t, ssize_t, float, float, float, float, Screen *, OSWindow *); +void draw_cursor(CursorRenderInfo *, bool); void update_viewport_size(int, int); void free_texture(uint32_t*); void send_image_to_gpu(uint32_t*, const void*, int32_t, int32_t, bool, bool);