From ec1a14ab331c0abc57bc34972a8a6f69c6ffa17a Mon Sep 17 00:00:00 2001 From: Po Lu Date: Thu, 12 May 2022 04:11:12 +0000 Subject: [PATCH] Make cursor display on Haiku consistent with X * src/haikuterm.c (haiku_draw_image_glyph_string): Merge cursor foregrounds correctly. (haiku_draw_hollow_cursor, haiku_draw_bar_cursor): New functions. Port code from X. (haiku_draw_window_cursor): Port code from X so bar cursors on top of images are treated right. --- src/haikuterm.c | 306 +++++++++++++++++++++++++++++++----------------- 1 file changed, 196 insertions(+), 110 deletions(-) diff --git a/src/haikuterm.c b/src/haikuterm.c index 58855d07fb8..b74584e2dca 100644 --- a/src/haikuterm.c +++ b/src/haikuterm.c @@ -1631,12 +1631,13 @@ static void haiku_draw_image_glyph_string (struct glyph_string *s) { struct face *face = s->face; - + void *view, *bitmap, *mask; int box_line_hwidth = max (face->box_vertical_line_width, 0); int box_line_vwidth = max (face->box_horizontal_line_width, 0); - - int x, y; - int height, width; + int x, y, height, width, relief; + struct haiku_rect nr; + Emacs_Rectangle cr, ir, r; + unsigned long background; height = s->height; if (s->slice.y == 0) @@ -1657,20 +1658,22 @@ haiku_draw_image_glyph_string (struct glyph_string *s) if (s->slice.y == 0) y += box_line_vwidth; - void *view = FRAME_HAIKU_VIEW (s->f); - void *bitmap = s->img->pixmap; + view = FRAME_HAIKU_VIEW (s->f); + bitmap = s->img->pixmap; /* TODO: implement stipples for images with masks. */ s->stippled_p = face->stipple != 0; - BView_SetHighColor (view, face->background); + if (s->hl == DRAW_CURSOR) + haiku_merge_cursor_foreground (s, NULL, &background); + else + background = face->background; + + BView_SetHighColor (view, background); BView_FillRectangle (view, x, y, width, height); if (bitmap) { - struct haiku_rect nr; - Emacs_Rectangle cr, ir, r; - get_glyph_string_clip_rect (s, &nr); CONVERT_TO_EMACS_RECT (cr, nr); x = s->x; @@ -1692,7 +1695,7 @@ haiku_draw_image_glyph_string (struct glyph_string *s) ir.height = s->slice.height; r = ir; - void *mask = s->img->mask; + mask = s->img->mask; if (gui_intersect_rectangles (&cr, &ir, &r)) { @@ -1726,11 +1729,25 @@ haiku_draw_image_glyph_string (struct glyph_string *s) BBitmap_free (bitmap); } - if (s->hl == DRAW_CURSOR) + if (!s->img->mask) { - BView_SetPenSize (view, 1); - BView_SetHighColor (view, FRAME_CURSOR_COLOR (s->f).pixel); - BView_StrokeRectangle (view, r.x, r.y, r.width, r.height); + /* When the image has a mask, we can expect that at + least part of a mouse highlight or a block cursor will + be visible. If the image doesn't have a mask, make + a block cursor visible by drawing a rectangle around + the image. I believe it's looking better if we do + nothing here for mouse-face. */ + + if (s->hl == DRAW_CURSOR) + { + relief = eabs (s->img->relief); + + BView_SetPenSize (view, 1); + BView_SetHighColor (view, FRAME_CURSOR_COLOR (s->f).pixel); + BView_StrokeRectangle (view, x - relief, y - relief, + s->slice.width + relief * 2, + s->slice.height + relief * 2); + } } } @@ -2000,132 +2017,201 @@ haiku_set_window_size (struct frame *f, bool change_gravity, } static void -haiku_draw_window_cursor (struct window *w, - struct glyph_row *glyph_row, - int x, int y, - enum text_cursor_kinds cursor_type, - int cursor_width, bool on_p, bool active_p) +haiku_draw_hollow_cursor (struct window *w, struct glyph_row *row) { - struct frame *f = XFRAME (WINDOW_FRAME (w)); - struct face *face; - struct glyph *phys_cursor_glyph; + struct frame *f; + int x, y, wd, h; struct glyph *cursor_glyph; + uint32_t foreground; + void *view; - void *view = FRAME_HAIKU_VIEW (f); - - int fx, fy, h, cursor_height; + f = XFRAME (WINDOW_FRAME (w)); + view = FRAME_HAIKU_VIEW (f); - if (!on_p) + /* Get the glyph the cursor is on. If we can't tell because + the current matrix is invalid or such, give up. */ + cursor_glyph = get_phys_cursor_glyph (w); + if (cursor_glyph == NULL) return; - if (cursor_type == NO_CURSOR) - { - w->phys_cursor_width = 0; - return; - } + /* Compute frame-relative coordinates for phys cursor. */ + get_phys_cursor_geometry (w, row, cursor_glyph, &x, &y, &h); + wd = w->phys_cursor_width; - w->phys_cursor_on_p = true; - w->phys_cursor_type = cursor_type; + /* The foreground of cursor_gc is typically the same as the normal + background color, which can cause the cursor box to be invisible. */ + foreground = FRAME_CURSOR_COLOR (f).pixel; - phys_cursor_glyph = get_phys_cursor_glyph (w); + /* When on R2L character, show cursor at the right edge of the + glyph, unless the cursor box is as wide as the glyph or wider + (the latter happens when x-stretch-cursor is non-nil). */ + if ((cursor_glyph->resolved_level & 1) != 0 + && cursor_glyph->pixel_width > wd) + x += cursor_glyph->pixel_width - wd; - if (!phys_cursor_glyph) - { - if (glyph_row->exact_window_width_line_p - && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA]) - { - glyph_row->cursor_in_fringe_p = 1; - draw_fringe_bitmap (w, glyph_row, 0); - } - return; - } + /* Set clipping, draw the rectangle, and reset clipping again. + This also marks the region as invalidated. */ - get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h); + BView_draw_lock (view, true, x, y, wd, h); + BView_StartClip (view); + haiku_clip_to_row (w, row, TEXT_AREA); - if (cursor_type == BAR_CURSOR) + /* Now set the foreground color and pen size. */ + BView_SetHighColor (view, foreground); + BView_SetPenSize (view, 1); + + /* Actually draw the rectangle. */ + BView_StrokeRectangle (view, x, y, wd, h); + + /* Reset clipping. */ + BView_EndClip (view); + BView_draw_unlock (view); +} + +static void +haiku_draw_bar_cursor (struct window *w, struct glyph_row *row, + int width, enum text_cursor_kinds kind) +{ + struct frame *f; + struct glyph *cursor_glyph; + struct glyph_row *r; + struct face *face; + uint32_t foreground; + void *view; + int x, y, dummy_x, dummy_y, dummy_h; + + f = XFRAME (w->frame); + + /* If cursor is out of bounds, don't draw garbage. This can happen + in mini-buffer windows when switching between echo area glyphs + and mini-buffer. */ + cursor_glyph = get_phys_cursor_glyph (w); + if (cursor_glyph == NULL) + return; + + /* If on an image, draw like a normal cursor. That's usually better + visible than drawing a bar, esp. if the image is large so that + the bar might not be in the window. */ + if (cursor_glyph->type == IMAGE_GLYPH) { - if (cursor_width < 1) - cursor_width = max (FRAME_CURSOR_WIDTH (f), 1); - if (cursor_width < w->phys_cursor_width) - w->phys_cursor_width = cursor_width; + r = MATRIX_ROW (w->current_matrix, w->phys_cursor.vpos); + draw_phys_cursor_glyph (w, r, DRAW_CURSOR); } - else if (cursor_type == HBAR_CURSOR) + else { - cursor_height = (cursor_width < 1) ? lrint (0.25 * h) : cursor_width; - if (cursor_height > glyph_row->height) - cursor_height = glyph_row->height; - if (h > cursor_height) - fy += h - cursor_height; - h = cursor_height; - } + view = FRAME_HAIKU_VIEW (f); + face = FACE_FROM_ID (f, cursor_glyph->face_id); - BView_draw_lock (view, false, 0, 0, 0, 0); - BView_StartClip (view); + /* If the glyph's background equals the color we normally draw + the bars cursor in, the bar cursor in its normal color is + invisible. Use the glyph's foreground color instead in this + case, on the assumption that the glyph's colors are chosen so + that the glyph is legible. */ + if (face->background == FRAME_CURSOR_COLOR (f).pixel) + foreground = face->foreground; + else + foreground = FRAME_CURSOR_COLOR (f).pixel; - if (cursor_type == BAR_CURSOR) - { - cursor_glyph = get_phys_cursor_glyph (w); - face = FACE_FROM_ID (f, cursor_glyph->face_id); - } + BView_draw_lock (view, false, 0, 0, 0, 0); + BView_StartClip (view); + BView_SetHighColor (view, foreground); + haiku_clip_to_row (w, row, TEXT_AREA); - /* If the glyph's background equals the color we normally draw the - bar cursor in, our cursor in its normal color is invisible. Use - the glyph's foreground color instead in this case, on the - assumption that the glyph's colors are chosen so that the glyph - is legible. */ + if (kind == BAR_CURSOR) + { + x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, w->phys_cursor.x); + y = WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y); - /* xterm.c only does this for bar cursors, and nobody has - complained, so it would be best to do that here as well. */ - if (cursor_type == BAR_CURSOR - && face->background == FRAME_CURSOR_COLOR (f).pixel) - BView_SetHighColor (view, face->foreground); - else - BView_SetHighColor (view, FRAME_CURSOR_COLOR (f).pixel); - haiku_clip_to_row (w, glyph_row, TEXT_AREA); + if (width < 0) + width = FRAME_CURSOR_WIDTH (f); + width = min (cursor_glyph->pixel_width, width); - switch (cursor_type) - { - default: - case DEFAULT_CURSOR: - case NO_CURSOR: - break; + w->phys_cursor_width = width; - case HBAR_CURSOR: - BView_FillRectangle (view, fx, fy, w->phys_cursor_width, h); - BView_invalidate_region (view, fx, fy, w->phys_cursor_width, h); - break; + /* If the character under cursor is R2L, draw the bar cursor + on the right of its glyph, rather than on the left. */ + if ((cursor_glyph->resolved_level & 1) != 0) + x += cursor_glyph->pixel_width - width; - case BAR_CURSOR: - if (cursor_glyph->resolved_level & 1) + BView_FillRectangle (view, x, y, width, row->height); + BView_invalidate_region (view, x, y, width, row->height); + } + else /* HBAR_CURSOR */ { - BView_FillRectangle (view, fx + cursor_glyph->pixel_width - w->phys_cursor_width, - fy, w->phys_cursor_width, h); - BView_invalidate_region (view, fx + cursor_glyph->pixel_width - w->phys_cursor_width, - fy, w->phys_cursor_width, h); + x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, w->phys_cursor.x); + y = WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y + + row->height - width); + + if (width < 0) + width = row->height; + + width = min (row->height, width); + + get_phys_cursor_geometry (w, row, cursor_glyph, &dummy_x, + &dummy_y, &dummy_h); + + if ((cursor_glyph->resolved_level & 1) != 0 + && cursor_glyph->pixel_width > w->phys_cursor_width - 1) + x += cursor_glyph->pixel_width - w->phys_cursor_width + 1; + + BView_FillRectangle (view, x, y, w->phys_cursor_width - 1, + width); + BView_invalidate_region (view, x, y, w->phys_cursor_width - 1, + width); } - else - BView_FillRectangle (view, fx, fy, w->phys_cursor_width, h); - BView_invalidate_region (view, fx, fy, w->phys_cursor_width, h); - break; + BView_EndClip (view); + BView_draw_unlock (view); + } +} - case HOLLOW_BOX_CURSOR: - if (phys_cursor_glyph->type != IMAGE_GLYPH) +static void +haiku_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, + int x, int y, enum text_cursor_kinds cursor_type, + int cursor_width, bool on_p, bool active_p) +{ + if (on_p) + { + w->phys_cursor_type = cursor_type; + w->phys_cursor_on_p = true; + + if (glyph_row->exact_window_width_line_p + && (glyph_row->reversed_p + ? (w->phys_cursor.hpos < 0) + : (w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA]))) { - BView_SetPenSize (view, 1); - BView_StrokeRectangle (view, fx, fy, w->phys_cursor_width, h); + glyph_row->cursor_in_fringe_p = true; + draw_fringe_bitmap (w, glyph_row, glyph_row->reversed_p); } else - draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR); + { + switch (cursor_type) + { + case HOLLOW_BOX_CURSOR: + haiku_draw_hollow_cursor (w, glyph_row); + break; - BView_invalidate_region (view, fx, fy, w->phys_cursor_width, h); - break; + case FILLED_BOX_CURSOR: + draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR); + break; + + case BAR_CURSOR: + haiku_draw_bar_cursor (w, glyph_row, cursor_width, BAR_CURSOR); + break; + + case HBAR_CURSOR: + haiku_draw_bar_cursor (w, glyph_row, cursor_width, HBAR_CURSOR); + break; - case FILLED_BOX_CURSOR: - draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR); + case NO_CURSOR: + w->phys_cursor_width = 0; + break; + + default: + emacs_abort (); + } + } } - BView_EndClip (view); - BView_draw_unlock (view); } static void -- 2.39.2