diff --git a/kitty/cell_vertex.glsl b/kitty/cell_vertex.glsl index d186a762e..bc3719332 100644 --- a/kitty/cell_vertex.glsl +++ b/kitty/cell_vertex.glsl @@ -4,6 +4,7 @@ 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 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 }; @@ -71,10 +72,16 @@ vec3 apply_selection(vec3 color, uint which) { return is_selected * color_to_vec(which) + (1.0 - is_selected) * color; } +uint in_range(uvec4 range, uint x, uint y) { + if (range[0] <= x && x <= range[1] && range[2] <= y && y <= range[3]) return ONE; + return ZERO; +} + void main() { uint instance_id = uint(gl_InstanceID); uint r = instance_id / dimensions.x; uint c = instance_id - r * dimensions.x; + uint in_url = in_range(url_range, c, r); float left = steps[0] + c * steps[2]; float top = steps[1] - r * steps[3]; vec2 xpos = vec2(left, left + steps[2]); @@ -87,10 +94,11 @@ void main() { uint fg = colors[color_indices[reverse]]; uint bg = colors[color_indices[ONE - reverse]]; uint resolved_fg = as_color(fg, default_colors[color_indices[0]]); - uint decoration = colors[2]; + uint underline; foreground = apply_selection(color_to_vec(resolved_fg), default_colors[2]); background = apply_selection(to_color(bg, default_colors[color_indices[1]]), default_colors[3]); - decoration_fg = to_color(decoration, resolved_fg); - underline_pos = to_sprite_pos(pos, (text_attrs >> 26) & DECORATION_MASK, ZERO, ZERO); + if (in_url == ONE) { underline = ONE; decoration_fg = color_to_vec(uint(255)); } + else { underline = (text_attrs >> 26) & DECORATION_MASK; decoration_fg = to_color(colors[2], resolved_fg); } + underline_pos = to_sprite_pos(pos, underline, ZERO, ZERO); strike_pos = to_sprite_pos(pos, ((text_attrs >> 31) & STRIKE_MASK) * THREE, ZERO, ZERO); } diff --git a/kitty/data-types.h b/kitty/data-types.h index da822ff2c..ffbdfa74e 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -234,6 +234,7 @@ typedef struct { uint32_t utf8_state, utf8_codepoint, *g0_charset, *g1_charset, *g_charset; Selection selection; SelectionBoundary last_rendered_selection_start, last_rendered_selection_end; + Selection url_range; bool use_latin1, selection_updated_once, is_dirty, scroll_changed; Cursor *cursor; SavepointBuffer main_savepoints, alt_savepoints; diff --git a/kitty/mouse.c b/kitty/mouse.c index 3ded5c6d6..9d45e1ab1 100644 --- a/kitty/mouse.c +++ b/kitty/mouse.c @@ -149,14 +149,33 @@ drag_scroll(Window *w) { } +static inline void +detect_url(Window *w, Screen *screen, unsigned int x, unsigned int y) { + bool has_url = false; + index_type url_start, url_end = 0; + Line *line = screen_visual_line(screen, y); + if (line) { + url_start = line_url_start_at(line, x); + if (url_start < line->xnum) url_end = line_url_end_at(line, x); + has_url = url_end > url_start ? true : false; + } + if (has_url) { + mouse_cursor_shape = HAND; + screen_mark_url(screen, url_start, w->mouse_cell_y, url_end, w->mouse_cell_y); + } else { + mouse_cursor_shape = BEAM; + screen_mark_url(screen, 0, 0, 0, 0); + } +} + + HANDLER(handle_move_event) { unsigned int x, y; if (!cell_for_pos(w, &x, &y)) return; - Line *line = screen_visual_line(w->render_data.screen, y); - mouse_cursor_shape = (line && line_url_start_at(line, x) < line->xnum) ? HAND : BEAM; + Screen *screen = w->render_data.screen; + detect_url(w, screen, x, y); bool mouse_cell_changed = x != w->mouse_cell_x || y != w->mouse_cell_y ? true : false; w->mouse_cell_x = x; w->mouse_cell_y = y; - Screen *screen = w->render_data.screen; bool handle_in_kitty = ( (screen->modes.mouse_tracking_mode == ANY_MODE || (screen->modes.mouse_tracking_mode == MOTION_MODE && button >= 0)) && diff --git a/kitty/screen.c b/kitty/screen.c index 591a4319c..8b54689c9 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -172,6 +172,7 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) { init_tabstops(self->alt_tabstops, self->columns); self->is_dirty = true; self->selection = EMPTY_SELECTION; + self->url_range = EMPTY_SELECTION; if (index_after_resize) screen_index(self); return true; @@ -1121,7 +1122,9 @@ screen_update_cell_data(Screen *self, void *address, size_t UNUSED sz) { linebuf_init_line(self->linebuf, y - self->scrolled_by); update_line_data(self->linebuf->line, y, address); } - if (selection_must_be_cleared) self->selection = EMPTY_SELECTION; + if (selection_must_be_cleared) { + self->selection = EMPTY_SELECTION; self->url_range = EMPTY_SELECTION; + } } static inline bool @@ -1143,13 +1146,12 @@ selection_coord(Screen *self, unsigned int x, unsigned int y, unsigned int ydelt } } -static inline void -selection_limits_(Screen *self, SelectionBoundary *left, SelectionBoundary *right) { - SelectionBoundary a, b; - selection_coord(self, self->selection.start_x, self->selection.start_y, self->selection.start_scrolled_by, &a); - selection_coord(self, self->selection.end_x, self->selection.end_y, self->selection.end_scrolled_by, &b); - if (a.y < b.y || (a.y == b.y && a.x <= b.x)) { *left = a; *right = b; } - else { *left = b; *right = a; } +#define selection_limits_(which, left, right) { \ + SelectionBoundary a, b; \ + selection_coord(self, self->which.start_x, self->which.start_y, self->which.start_scrolled_by, &a); \ + selection_coord(self, self->which.end_x, self->which.end_y, self->which.end_scrolled_by, &b); \ + if (a.y < b.y || (a.y == b.y && a.x <= b.x)) { *(left) = a; *(right) = b; } \ + else { *(left) = b; *(right) = a; } \ } static inline Line* @@ -1171,7 +1173,7 @@ screen_apply_selection(Screen *self, void *address, size_t size) { #define end (self->last_rendered_selection_end) float *data = address; memset(data, 0, size); - selection_limits_(self, &start, &end); + selection_limits_(selection, &start, &end); self->last_selection_scrolled_by = self->scrolled_by; self->selection_updated_once = true; if (is_selection_empty(self, start.x, start.y, end.x, end.y)) { return; } @@ -1186,6 +1188,14 @@ screen_apply_selection(Screen *self, void *address, size_t size) { #undef end } +void +screen_url_range(Screen *self, unsigned int *start_x, unsigned int *start_y, unsigned int *end_x, unsigned int *end_y) { + SelectionBoundary start, end; + selection_limits_(url_range, &start, &end); + if (is_selection_empty(self, start.x, start.y, end.x, end.y)) { *start_y = self->lines; *end_y = self->lines; *start_x = self->columns; *end_x = self->columns; } + else { *start_x = start.x; *start_y = start.y; *end_x = end.x; *end_y = end.y; } +} + // }}} // Python interface {{{ @@ -1314,7 +1324,7 @@ change_scrollback_size(Screen *self, PyObject *args) { static PyObject* text_for_selection(Screen *self) { SelectionBoundary start, end; - selection_limits_(self, &start, &end); + selection_limits_(selection, &start, &end); if (is_selection_empty(self, start.x, start.y, end.x, end.y)) return PyTuple_New(0); Py_ssize_t i = 0, num_of_lines = end.y - start.y + 1; PyObject *ans = PyTuple_New(num_of_lines); @@ -1410,7 +1420,7 @@ scroll(Screen *self, PyObject *args) { bool screen_is_selection_dirty(Screen *self) { SelectionBoundary start, end; - selection_limits_(self, &start, &end); + selection_limits_(selection, &start, &end); if (self->last_selection_scrolled_by != self->scrolled_by || start.x != self->last_rendered_selection_start.x || start.y != self->last_rendered_selection_start.y || end.x != self->last_rendered_selection_end.x || end.y != self->last_rendered_selection_end.y || !self->selection_updated_once) return true; return false; } @@ -1422,6 +1432,13 @@ screen_start_selection(Screen *self, index_type x, index_type y) { #undef A } +void +screen_mark_url(Screen *self, index_type start_x, index_type start_y, index_type end_x, index_type end_y) { +#define A(attr, val) self->url_range.attr = val; + A(start_x, start_x); A(end_x, end_x); A(start_y, start_y); A(end_y, end_y); A(start_scrolled_by, self->scrolled_by); A(end_scrolled_by, self->scrolled_by); +#undef A +} + void screen_update_selection(Screen *self, index_type x, index_type y, bool ended) { self->selection.end_x = x; self->selection.end_y = y; self->selection.end_scrolled_by = self->scrolled_by; diff --git a/kitty/screen.h b/kitty/screen.h index 6fc229d61..32200b691 100644 --- a/kitty/screen.h +++ b/kitty/screen.h @@ -73,6 +73,8 @@ void screen_update_selection(Screen *self, index_type x, index_type y, bool ende bool screen_history_scroll(Screen *self, int amt, bool upwards); Line* screen_visual_line(Screen *self, index_type y); unsigned long screen_current_char_width(Screen *self); +void screen_url_range(Screen *self, unsigned int *start_x, unsigned int *start_y, unsigned int *limit_x, unsigned int *limit_y); +void screen_mark_url(Screen *self, index_type start_x, index_type start_y, index_type end_x, index_type end_y); #define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen); DECLARE_CH_SCREEN_HANDLER(bell) DECLARE_CH_SCREEN_HANDLER(backspace) diff --git a/kitty/shaders.c b/kitty/shaders.c index 9ed3a56cd..999f7c179 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -552,7 +552,7 @@ destroy_sprite_map() { // Cell {{{ -enum CellUniforms { CELL_dimensions, CELL_default_colors, CELL_color_indices, CELL_steps, CELL_sprites, CELL_sprite_layout, 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_color_table_stride = 0, cell_color_table_offset = 0, cell_color_table_size = 0, cell_color_table_block_index = 0; @@ -569,6 +569,7 @@ init_cell_program() { else SET_LOC(steps); else SET_LOC(sprites); else SET_LOC(sprite_layout); + else SET_LOC(url_range); else if (strcmp(p->uniforms[i].name, "color_table[0]") == 0) { ctable_idx = i; cell_uniform_locations[CELL_color_table] = p->uniforms[i].location; } else { fatal("Unknown uniform in cell program: %s", p->uniforms[i].name); } } @@ -637,6 +638,10 @@ draw_cells_impl(ssize_t vao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLf glUniform4ui(UL(default_colors), COLOR(default_fg), COLOR(default_bg), COLOR(highlight_fg), COLOR(highlight_bg)); 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); + glUniform4ui(UL(url_range), start_x, end_x, start_y, end_y); + check_gl(); glUniform1i(UL(sprites), sprite_map_unit); check_gl(); unsigned int x, y, z; @@ -767,14 +772,11 @@ compile_program(PyObject UNUSED *self, PyObject *args) { if (programs[which].id != 0) { PyErr_SetString(PyExc_ValueError, "program already compiled"); return NULL; } programs[which].id = glCreateProgram(); check_gl(); - vertex_shader_id = compile_shader(GL_VERTEX_SHADER, vertex_shader); - fragment_shader_id = compile_shader(GL_FRAGMENT_SHADER, fragment_shader); - glAttachShader(programs[which].id, vertex_shader_id); - check_gl(); - glAttachShader(programs[which].id, fragment_shader_id); - check_gl(); - glLinkProgram(programs[which].id); - check_gl(); + vertex_shader_id = compile_shader(GL_VERTEX_SHADER, vertex_shader); check_gl(); + fragment_shader_id = compile_shader(GL_FRAGMENT_SHADER, fragment_shader); check_gl(); + glAttachShader(programs[which].id, vertex_shader_id); check_gl(); + glAttachShader(programs[which].id, fragment_shader_id); check_gl(); + glLinkProgram(programs[which].id); check_gl(); GLint ret = GL_FALSE; glGetProgramiv(programs[which].id, GL_LINK_STATUS, &ret); if (ret != GL_TRUE) {