diff --git a/kitty/glfw.c b/kitty/glfw.c index 3a12411e4..6ec6f78c1 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -416,8 +416,7 @@ apple_file_open_callback(const char* filepath) { static FreeTypeRenderCtx csd_title_render_ctx = NULL; static bool -draw_text_callback(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin) { - if (!set_callback_window(window)) return false; +ensure_csd_title_render_ctx(void) { if (!csd_title_render_ctx) { csd_title_render_ctx = create_freetype_render_context(NULL, true, false); if (!csd_title_render_ctx) { @@ -425,6 +424,13 @@ draw_text_callback(GLFWwindow *window, const char *text, uint32_t fg, uint32_t b return false; } } + return true; +} + +static bool +draw_text_callback(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin) { + if (!set_callback_window(window)) return false; + if (!ensure_csd_title_render_ctx()) return false; double xdpi, ydpi; get_window_dpi(window, &xdpi, &ydpi); unsigned px_sz = (unsigned)(global_state.callback_os_window->font_sz_in_pts * ydpi / 72.); @@ -435,6 +441,18 @@ draw_text_callback(GLFWwindow *window, const char *text, uint32_t fg, uint32_t b if (!ok && PyErr_Occurred()) PyErr_Print(); return ok; } + +bool +draw_window_title(OSWindow *window, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height) { + if (!ensure_csd_title_render_ctx()) return false; + unsigned px_sz = (unsigned)(window->fonts_data->font_sz_in_pts * window->fonts_data->logical_dpi_y / 72.); + px_sz = MIN(px_sz, 3 * height / 4); +#define RGB2BGR(x) (x & 0xFF000000) | ((x & 0xFF0000) >> 16) | (x & 0x00FF00) | ((x & 0x0000FF) << 16) + bool ok = render_single_line(csd_title_render_ctx, text, px_sz, RGB2BGR(fg), RGB2BGR(bg), output_buf, width, height, 0, 0, 0); +#undef RGB2BGR + if (!ok && PyErr_Occurred()) PyErr_Print(); + return ok; +} #endif // }}} diff --git a/kitty/graphics.c b/kitty/graphics.c index 3df1d7b13..b8208d11e 100644 --- a/kitty/graphics.c +++ b/kitty/graphics.c @@ -716,14 +716,21 @@ set_vertex_data(ImageRenderData *rd, const ImageRef *ref, const ImageRect *dest_ } void -gpu_data_for_centered_image(ImageRenderData *ans, unsigned int screen_width_px, unsigned int screen_height_px, unsigned int width, unsigned int height) { +gpu_data_for_image(ImageRenderData *ans, float left, float top, float right, float bottom) { + // x-axis is from -1 to 1, y axis is from 1 to -1 static const ImageRef source_rect = { .src_rect = { .left=0, .top=0, .bottom=1, .right=1 }}; const ImageRef *ref = &source_rect; + const ImageRect r = { .left = left, .right = right, .top = top, .bottom = bottom }; + set_vertex_data(ans, ref, &r); + ans->group_count = 1; +} + +void +gpu_data_for_centered_image(ImageRenderData *ans, unsigned int screen_width_px, unsigned int screen_height_px, unsigned int width, unsigned int height) { float width_frac = 2 * MIN(1, width / (float)screen_width_px), height_frac = 2 * MIN(1, height / (float)screen_height_px); float hmargin = (2 - width_frac) / 2; float vmargin = (2 - height_frac) / 2; - const ImageRect r = { .left = -1 + hmargin, .right = -1 + hmargin + width_frac, .top = 1 - vmargin, .bottom = 1 - vmargin - height_frac }; - set_vertex_data(ans, ref, &r); + gpu_data_for_image(ans, -1 + hmargin, 1 - vmargin, -1 + hmargin + width_frac, 1 - vmargin - height_frac); } bool diff --git a/kitty/graphics.h b/kitty/graphics.h index ca28c87ee..55015e88a 100644 --- a/kitty/graphics.h +++ b/kitty/graphics.h @@ -128,6 +128,7 @@ bool grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float void grman_scroll_images(GraphicsManager *self, const ScrollData*, CellPixelSize fg); void grman_resize(GraphicsManager*, index_type, index_type, index_type, index_type); void grman_rescale(GraphicsManager *self, CellPixelSize fg); +void gpu_data_for_image(ImageRenderData *ans, float left, float top, float right, float bottom); void gpu_data_for_centered_image(ImageRenderData *ans, unsigned int screen_width_px, unsigned int screen_height_px, unsigned int width, unsigned int height); bool png_path_to_bitmap(const char *path, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz); bool scan_active_animations(GraphicsManager *self, const monotonic_t now, monotonic_t *minimum_gap, bool os_window_context_set); diff --git a/kitty/shaders.c b/kitty/shaders.c index 6fddb48ae..fab383956 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -467,14 +467,87 @@ draw_tint(bool premult, Screen *screen, GLfloat xstart, GLfloat ystart, GLfloat } static void -draw_window_number(OSWindow *os_window, Screen *screen, GLfloat xstart, GLfloat ystart, GLfloat width, GLfloat height, Window *window UNUSED) { +set_cell_uniforms(float current_inactive_text_alpha, bool force) { + if (!cell_uniform_data.constants_set || force) { + cell_uniform_data.gploc = glGetUniformLocation(program_id(GRAPHICS_PROGRAM), "inactive_text_alpha"); + cell_uniform_data.gpploc = glGetUniformLocation(program_id(GRAPHICS_PREMULT_PROGRAM), "inactive_text_alpha"); + cell_uniform_data.cploc = glGetUniformLocation(program_id(CELL_PROGRAM), "inactive_text_alpha"); + cell_uniform_data.cfploc = glGetUniformLocation(program_id(CELL_FG_PROGRAM), "inactive_text_alpha"); + cell_uniform_data.amask_premult_loc = glGetUniformLocation(program_id(GRAPHICS_ALPHA_MASK_PROGRAM), "alpha_mask_premult"); +#define S(prog, name, val, type) { bind_program(prog); glUniform##type(glGetUniformLocation(program_id(prog), #name), val); } + S(GRAPHICS_PROGRAM, image, GRAPHICS_UNIT, 1i); + S(GRAPHICS_PREMULT_PROGRAM, image, GRAPHICS_UNIT, 1i); + S(CELL_PROGRAM, sprites, SPRITE_MAP_UNIT, 1i); S(CELL_FG_PROGRAM, sprites, SPRITE_MAP_UNIT, 1i); + S(CELL_PROGRAM, dim_opacity, OPT(dim_opacity), 1f); S(CELL_FG_PROGRAM, dim_opacity, OPT(dim_opacity), 1f); + S(CELL_BG_PROGRAM, defaultbg, OPT(background), 1f); +#undef S + cell_uniform_data.constants_set = true; + } + if (current_inactive_text_alpha != cell_uniform_data.prev_inactive_text_alpha || force) { + cell_uniform_data.prev_inactive_text_alpha = current_inactive_text_alpha; +#define S(prog, loc) { bind_program(prog); glUniform1f(cell_uniform_data.loc, current_inactive_text_alpha); } + S(CELL_PROGRAM, cploc); S(CELL_FG_PROGRAM, cfploc); S(GRAPHICS_PROGRAM, gploc); S(GRAPHICS_PREMULT_PROGRAM, gpploc); + } +} + +static GLfloat +render_window_title(OSWindow *os_window, Screen *screen UNUSED, GLfloat xstart, GLfloat ystart, GLfloat width, Window *window, GLfloat left, GLfloat right) { + unsigned bar_height = os_window->fonts_data->cell_height + 2; + if (!bar_height || right <= left) return 0; + unsigned bar_width = (unsigned)ceilf(right - left); + if (!window->title_bar_data.buf || window->title_bar_data.width != bar_width || window->title_bar_data.height != bar_height) { + free(window->title_bar_data.buf); + window->title_bar_data.buf = malloc(4 * bar_width * bar_height); + window->title_bar_data.last_drawn_title_object_id = NULL; + if (!window->title_bar_data.buf) return 0; + window->title_bar_data.height = bar_height; + window->title_bar_data.width = bar_width; + } + static char title[2048] = {0}; + if (window->title_bar_data.last_drawn_title_object_id != window->title) { + snprintf(title, arraysz(title), " %s", PyUnicode_AsUTF8(window->title)); +#define RGBCOL(which) ( 0xff000000 | colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.which, screen->color_profile->configured.which) ) + if (!draw_window_title(os_window, title, RGBCOL(highlight_fg), RGBCOL(highlight_bg), window->title_bar_data.buf, bar_width, bar_height)) return 0; +#undef RGBCOL + window->title_bar_data.last_drawn_title_object_id = window->title; + } + static ImageRenderData data = {.group_count=1}; + gpu_data_for_image(&data, xstart, ystart, xstart + width, ystart - 2.f * (bar_height / (float)os_window->viewport_height)); + if (!data.texture_id) { glGenTextures(1, &data.texture_id); } + glBindTexture(GL_TEXTURE_2D, data.texture_id); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bar_width, bar_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, window->title_bar_data.buf); + set_cell_uniforms(1.f, false); + bind_program(GRAPHICS_PROGRAM); + send_graphics_data_to_gpu(1, os_window->gvao_idx, &data); glEnable(GL_BLEND); - glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); // BLEND_PREMULT - bind_program(SEVEN_SEGMENT_PROGRAM); + if (os_window->is_semi_transparent) { BLEND_PREMULT; } else { BLEND_ONTO_OPAQUE; } + draw_graphics(GRAPHICS_PROGRAM, 0, os_window->gvao_idx, &data, 0, 1); + glDisable(GL_BLEND); + return (GLfloat)bar_height / (GLfloat)os_window->viewport_height; +} + +static void +draw_window_number(OSWindow *os_window, Screen *screen, GLfloat xstart, GLfloat ystart, GLfloat width, GLfloat height, Window *window) { GLfloat left = os_window->viewport_width * (xstart + 1.f) / 2.f; GLfloat right = left + os_window->viewport_width * width / 2.f; GLfloat top = os_window->viewport_height * (1.f - ystart) / 2.f; GLfloat bottom = os_window->viewport_height * (1.f + height - ystart) / 2.f; + GLfloat title_bar_height = 0; + if (window->title && PyUnicode_Check(window->title)) title_bar_height = render_window_title( + os_window, screen, xstart, ystart, width, window, left, right); + if (title_bar_height > 0) { + ystart -= title_bar_height; + top = os_window->viewport_height * (1.f - ystart) / 2.f; + bottom = os_window->viewport_height * (1.f + height - ystart) / 2.f; + } + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); // BLEND_PREMULT + bind_program(SEVEN_SEGMENT_PROGRAM); glUniform4f(seven_segment_program_layout.edges_location, xstart, ystart - height, xstart + width, ystart); glUniform4f(seven_segment_program_layout.area_bounds_location, left, top, right - left, bottom - top); color_type digit_color = colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.highlight_bg, screen->color_profile->configured.highlight_bg); @@ -621,31 +694,6 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen glDisable(GL_BLEND); } -static void -set_cell_uniforms(float current_inactive_text_alpha, bool force) { - if (!cell_uniform_data.constants_set || force) { - cell_uniform_data.gploc = glGetUniformLocation(program_id(GRAPHICS_PROGRAM), "inactive_text_alpha"); - cell_uniform_data.gpploc = glGetUniformLocation(program_id(GRAPHICS_PREMULT_PROGRAM), "inactive_text_alpha"); - cell_uniform_data.cploc = glGetUniformLocation(program_id(CELL_PROGRAM), "inactive_text_alpha"); - cell_uniform_data.cfploc = glGetUniformLocation(program_id(CELL_FG_PROGRAM), "inactive_text_alpha"); - cell_uniform_data.amask_premult_loc = glGetUniformLocation(program_id(GRAPHICS_ALPHA_MASK_PROGRAM), "alpha_mask_premult"); -#define S(prog, name, val, type) { bind_program(prog); glUniform##type(glGetUniformLocation(program_id(prog), #name), val); } - S(GRAPHICS_PROGRAM, image, GRAPHICS_UNIT, 1i); - S(GRAPHICS_PREMULT_PROGRAM, image, GRAPHICS_UNIT, 1i); - S(CELL_PROGRAM, sprites, SPRITE_MAP_UNIT, 1i); S(CELL_FG_PROGRAM, sprites, SPRITE_MAP_UNIT, 1i); - S(CELL_PROGRAM, dim_opacity, OPT(dim_opacity), 1f); S(CELL_FG_PROGRAM, dim_opacity, OPT(dim_opacity), 1f); - S(CELL_BG_PROGRAM, defaultbg, OPT(background), 1f); -#undef S - cell_uniform_data.constants_set = true; - } - if (current_inactive_text_alpha != cell_uniform_data.prev_inactive_text_alpha || force) { - cell_uniform_data.prev_inactive_text_alpha = current_inactive_text_alpha; -#define S(prog, loc) { bind_program(prog); glUniform1f(cell_uniform_data.loc, current_inactive_text_alpha); } - S(CELL_PROGRAM, cploc); S(CELL_FG_PROGRAM, cfploc); S(GRAPHICS_PROGRAM, gploc); S(GRAPHICS_PREMULT_PROGRAM, gpploc); -#undef S - } -} - void blank_canvas(float background_opacity, color_type color) { // See https://github.com/glfw/glfw/issues/1538 for why we use pre-multiplied alpha diff --git a/kitty/state.c b/kitty/state.c index 44f039d7b..ac0372ccb 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -287,6 +287,7 @@ update_os_window_title(OSWindow *os_window) { static void destroy_window(Window *w) { Py_CLEAR(w->render_data.screen); Py_CLEAR(w->title); + free(w->title_bar_data.buf); w->title_bar_data.buf = NULL; release_gpu_resources_for_window(w); } diff --git a/kitty/state.h b/kitty/state.h index 3edc5b3d5..caf151742 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -123,6 +123,11 @@ typedef struct { ClickQueue click_queues[8]; monotonic_t last_drag_scroll_at; uint32_t last_special_key_pressed; + struct { + unsigned width, height; + uint8_t *buf; + PyObject *last_drawn_title_object_id; + } title_bar_data; } Window; typedef struct { @@ -313,3 +318,4 @@ const char* format_mods(unsigned mods); void send_pending_click_to_window_id(id_type, void*); void send_pending_click_to_window(Window*, void*); void get_platform_dependent_config_values(void *glfw_window); +bool draw_window_title(OSWindow *window, const char *text, color_type fg, color_type bg, uint8_t *output_buf, size_t width, size_t height);