From d6f244f7d9f52462bafb608f23494707eeb24751 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sun, 20 Apr 2014 22:57:28 -0400 Subject: clean up the directory structure a bit --- src/display.c | 563 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 563 insertions(+) create mode 100644 src/display.c (limited to 'src/display.c') diff --git a/src/display.c b/src/display.c new file mode 100644 index 0000000..474d972 --- /dev/null +++ b/src/display.c @@ -0,0 +1,563 @@ +#include +#include +#include + +#include "runes.h" + +static cairo_pattern_t *runes_display_get_fgcolor(RunesTerm *t); +static cairo_pattern_t *runes_display_get_bgcolor(RunesTerm *t); +static void runes_display_recalculate_font_metrics(RunesTerm *t); +static void runes_display_position_cursor(RunesTerm *t, cairo_t *cr); +static void runes_display_paint_rectangle( + RunesTerm *t, cairo_t *cr, cairo_pattern_t *pattern, + int x, int y, int width, int height); +static void runes_display_scroll_down(RunesTerm *t, int rows); + +void runes_display_init(RunesTerm *t) +{ + runes_display_recalculate_font_metrics(t); + + t->cursorcolor = cairo_pattern_create_rgba(0.0, 1.0, 0.0, 0.5); + + t->fgcolor = -1; + t->bgcolor = -1; +} + +void runes_display_set_window_size(RunesTerm *t) +{ + int width, height; + cairo_t *old_cr = NULL; + cairo_surface_t *surface; + + runes_window_backend_get_size(t, &width, &height); + + if (width == t->xpixel && height == t->ypixel) { + return; + } + + t->xpixel = width; + t->ypixel = height; + + t->rows = t->ypixel / t->fonty; + t->cols = t->xpixel / t->fontx; + + old_cr = t->cr; + + /* XXX this should really use cairo_surface_create_similar_image, but when + * i did that, drawing calls would occasionally block until an X event + * occurred for some reason. should look into this, because i think + * create_similar_image does things that are more efficient (using some + * xlib shm stuff) */ + surface = cairo_image_surface_create( + CAIRO_FORMAT_RGB24, t->xpixel, t->ypixel); + t->cr = cairo_create(surface); + cairo_surface_destroy(surface); + cairo_set_source(t->cr, runes_display_get_fgcolor(t)); + if (t->layout) { + pango_cairo_update_layout(t->cr, t->layout); + } + else { + PangoAttrList *attrs; + PangoFontDescription *font_desc; + + attrs = pango_attr_list_new(); + font_desc = pango_font_description_from_string(t->font_name); + + t->layout = pango_cairo_create_layout(t->cr); + pango_layout_set_attributes(t->layout, attrs); + pango_layout_set_font_description(t->layout, font_desc); + + pango_attr_list_unref(attrs); + pango_font_description_free(font_desc); + } + + cairo_save(t->cr); + + if (old_cr) { + cairo_set_source_surface(t->cr, cairo_get_target(old_cr), 0.0, 0.0); + } + else { + cairo_set_source(t->cr, t->bgdefault); + } + + cairo_paint(t->cr); + + cairo_restore(t->cr); + + if (old_cr) { + cairo_destroy(old_cr); + } + + runes_pty_backend_set_window_size(t); + runes_display_position_cursor(t, t->cr); +} + +void runes_display_focus_in(RunesTerm *t) +{ + t->unfocused = 0; +} + +void runes_display_focus_out(RunesTerm *t) +{ + t->unfocused = 1; +} + +void runes_display_move_to(RunesTerm *t, int row, int col) +{ + int height = t->scroll_bottom - t->scroll_top; + + t->row = row + t->scroll_top; + t->col = col; + + if (row > height) { + runes_display_scroll_down(t, row - height); + t->row = t->scroll_bottom; + } + else if (row < 0) { + runes_display_scroll_up(t, -row); + t->row = t->scroll_top; + } + + runes_display_position_cursor(t, t->cr); +} + +void runes_display_show_string_ascii(RunesTerm *t, char *buf, size_t len) +{ + if (len) { + int remaining = len, space_in_row = t->cols - t->col; + + do { + int to_write = remaining > space_in_row ? space_in_row : remaining; + + runes_display_paint_rectangle( + t, t->cr, runes_display_get_bgcolor(t), + t->col, t->row, to_write, 1); + + pango_layout_set_text(t->layout, buf, to_write); + pango_cairo_update_layout(t->cr, t->layout); + pango_cairo_show_layout(t->cr, t->layout); + + buf += to_write; + remaining -= to_write; + space_in_row = t->cols; + if (remaining) { + runes_display_move_to(t, t->row + 1, 0); + } + else { + t->col += len; + runes_display_position_cursor(t, t->cr); + } + } while (remaining > 0); + } +} + +/* XXX this is gross and kind of slow, but i can't find a better way to do it. + * i need to be able to convert the string into clusters before laying it out, + * since i'm not going to be using the full layout anyway, but the only way i + * can see to do that is with pango_get_log_attrs, which only returns character + * (not byte) offsets, so i have to reparse the utf8 myself once i get the + * results. not ideal. */ +void runes_display_show_string_utf8(RunesTerm *t, char *buf, size_t len) +{ + size_t i, pos, char_len; + PangoLogAttr *attrs; + + char_len = g_utf8_strlen(buf, len); + attrs = malloc(sizeof(PangoLogAttr) * (char_len + 1)); + pango_get_log_attrs(buf, len, -1, NULL, attrs, char_len + 1); + + pos = 0; + for (i = 1; i < char_len + 1; ++i) { + if (attrs[i].is_cursor_position) { + char *startpos, *c; + size_t cluster_len; + char width = 1; + + startpos = g_utf8_offset_to_pointer(buf, pos); + cluster_len = g_utf8_offset_to_pointer(buf, i) - startpos; + + for (c = startpos; + (size_t)(c - startpos) < cluster_len; + c = g_utf8_next_char(c)) { + if (g_unichar_iswide(g_utf8_get_char(c))) { + width = 2; + } + } + + runes_display_paint_rectangle( + t, t->cr, runes_display_get_bgcolor(t), + t->col, t->row, width, 1); + + pango_layout_set_text(t->layout, startpos, cluster_len); + pango_cairo_update_layout(t->cr, t->layout); + pango_cairo_show_layout(t->cr, t->layout); + + if (t->col + width >= t->cols) { + runes_display_move_to(t, t->row + 1, 0); + } + else { + runes_display_move_to(t, t->row, t->col + width); + } + pos = i; + } + } + + free(attrs); +} + +void runes_display_clear_screen(RunesTerm *t) +{ + runes_display_paint_rectangle( + t, t->cr, t->bgdefault, 0, 0, t->cols, t->rows); +} + +void runes_display_clear_screen_forward(RunesTerm *t) +{ + runes_display_kill_line_forward(t); + runes_display_paint_rectangle( + t, t->cr, t->bgdefault, 0, t->row, t->cols, t->rows - t->row); +} + +void runes_display_kill_line_forward(RunesTerm *t) +{ + runes_display_paint_rectangle( + t, t->cr, t->bgdefault, t->col, t->row, t->cols - t->col, 1); +} + +void runes_display_delete_characters(RunesTerm *t, int count) +{ + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + + cairo_save(t->cr); + cairo_push_group(t->cr); + pattern = cairo_pattern_create_for_surface(cairo_get_target(t->cr)); + cairo_matrix_init_translate(&matrix, count * t->fontx, 0.0); + cairo_pattern_set_matrix(pattern, &matrix); + runes_display_paint_rectangle( + t, t->cr, pattern, t->col, t->row, t->cols - t->col - count, 1); + cairo_pattern_destroy(pattern); + cairo_pop_group_to_source(t->cr); + cairo_paint(t->cr); + runes_display_paint_rectangle( + t, t->cr, t->colors[0], t->cols - count, t->row, count, 1); + cairo_restore(t->cr); +} + +void runes_display_reset_text_attributes(RunesTerm *t) +{ + runes_display_reset_fg_color(t); + runes_display_reset_bg_color(t); + runes_display_reset_bold(t); + runes_display_reset_italic(t); + runes_display_reset_underline(t); + runes_display_reset_inverse(t); +} + +void runes_display_set_bold(RunesTerm *t) +{ + PangoAttrList *attrs; + + attrs = pango_layout_get_attributes(t->layout); + if (t->bold_is_bold) { + pango_attr_list_change( + attrs, pango_attr_weight_new(PANGO_WEIGHT_BOLD)); + } + t->bold = 1; + cairo_set_source(t->cr, runes_display_get_fgcolor(t)); +} + +void runes_display_reset_bold(RunesTerm *t) +{ + PangoAttrList *attrs; + + attrs = pango_layout_get_attributes(t->layout); + if (t->bold_is_bold) { + pango_attr_list_change( + attrs, pango_attr_weight_new(PANGO_WEIGHT_NORMAL)); + } + t->bold = 0; + cairo_set_source(t->cr, runes_display_get_fgcolor(t)); +} + +void runes_display_set_italic(RunesTerm *t) +{ + PangoAttrList *attrs; + + attrs = pango_layout_get_attributes(t->layout); + pango_attr_list_change(attrs, pango_attr_style_new(PANGO_STYLE_ITALIC)); +} + +void runes_display_reset_italic(RunesTerm *t) +{ + PangoAttrList *attrs; + + attrs = pango_layout_get_attributes(t->layout); + pango_attr_list_change(attrs, pango_attr_style_new(PANGO_STYLE_NORMAL)); +} + +void runes_display_set_underline(RunesTerm *t) +{ + PangoAttrList *attrs; + + attrs = pango_layout_get_attributes(t->layout); + pango_attr_list_change( + attrs, pango_attr_underline_new(PANGO_UNDERLINE_SINGLE)); +} + +void runes_display_reset_underline(RunesTerm *t) +{ + PangoAttrList *attrs; + + attrs = pango_layout_get_attributes(t->layout); + pango_attr_list_change( + attrs, pango_attr_underline_new(PANGO_UNDERLINE_NONE)); +} + +void runes_display_set_inverse(RunesTerm *t) +{ + t->inverse = 1; + cairo_set_source(t->cr, runes_display_get_fgcolor(t)); +} + +void runes_display_reset_inverse(RunesTerm *t) +{ + t->inverse = 0; + cairo_set_source(t->cr, runes_display_get_fgcolor(t)); +} + +void runes_display_set_fg_color(RunesTerm *t, int color) +{ + t->fgcolor = color; + cairo_set_source(t->cr, runes_display_get_fgcolor(t)); +} + +void runes_display_reset_fg_color(RunesTerm *t) +{ + runes_display_set_fg_color(t, -1); +} + +void runes_display_set_bg_color(RunesTerm *t, int color) +{ + t->bgcolor = color; + cairo_set_source(t->cr, runes_display_get_fgcolor(t)); +} + +void runes_display_reset_bg_color(RunesTerm *t) +{ + runes_display_set_bg_color(t, -1); +} + +void runes_display_show_cursor(RunesTerm *t) +{ + t->hide_cursor = 0; +} + +void runes_display_hide_cursor(RunesTerm *t) +{ + t->hide_cursor = 1; +} + +void runes_display_save_cursor(RunesTerm *t) +{ + t->saved_row = t->row; + t->saved_col = t->col; + /* XXX do other stuff here? */ +} + +void runes_display_restore_cursor(RunesTerm *t) +{ + t->row = t->saved_row; + t->col = t->saved_col; +} + +void runes_display_use_alternate_buffer(RunesTerm *t) +{ + if (t->alternate_cr) { + return; + } + + runes_display_save_cursor(t); + t->alternate_cr = t->cr; + t->cr = NULL; + t->xpixel = -1; + t->ypixel = -1; + runes_display_set_window_size(t); +} + +void runes_display_use_normal_buffer(RunesTerm *t) +{ + if (!t->alternate_cr) { + return; + } + + runes_display_restore_cursor(t); + cairo_destroy(t->cr); + t->cr = t->alternate_cr; + t->alternate_cr = NULL; + t->xpixel = -1; + t->ypixel = -1; + runes_display_set_window_size(t); +} + +void runes_display_set_scroll_region( + RunesTerm *t, int top, int bottom, int left, int right) +{ + top = (top < 1 ? 1 : top) - 1; + bottom = (bottom > t->rows ? t->rows : bottom) - 1; + left = (left < 1 ? 1 : left) - 1; + right = (right > t->cols ? t->cols : right) - 1; + + if (left != 0 || right != t->cols - 1) { + fprintf(stderr, "vertical scroll regions not yet supported\n"); + } + + if (top >= bottom || left >= right) { + t->scroll_top = 0; + t->scroll_bottom = t->rows - 1; + return; + } + + t->scroll_top = top; + t->scroll_bottom = bottom; + runes_display_move_to(t, 0, 0); +} + +void runes_display_scroll_up(RunesTerm *t, int rows) +{ + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + + cairo_save(t->cr); + cairo_push_group(t->cr); + pattern = cairo_pattern_create_for_surface(cairo_get_target(t->cr)); + cairo_matrix_init_translate(&matrix, 0.0, -rows * t->fonty); + cairo_pattern_set_matrix(pattern, &matrix); + runes_display_paint_rectangle( + t, t->cr, pattern, + 0, t->scroll_top + rows, + t->cols, t->scroll_bottom - t->scroll_top + 1 - rows); + cairo_pattern_destroy(pattern); + cairo_pop_group_to_source(t->cr); + cairo_paint(t->cr); + runes_display_paint_rectangle( + t, t->cr, t->bgdefault, 0, t->scroll_top, t->cols, rows); + cairo_restore(t->cr); +} + +void runes_display_cleanup(RunesTerm *t) +{ + int i; + + g_object_unref(t->layout); + for (i = 0; i < 8; ++i) { + cairo_pattern_destroy(t->colors[i]); + cairo_pattern_destroy(t->brightcolors[i]); + } + cairo_pattern_destroy(t->cursorcolor); + cairo_destroy(t->cr); +} + +static cairo_pattern_t *runes_display_get_fgcolor(RunesTerm *t) +{ + int color = t->inverse ? t->bgcolor : t->fgcolor; + + if (t->inverse && t->bgcolor == t->fgcolor) { + return t->bgdefault; + } + else if (color == -1) { + return t->inverse ? t->bgdefault : t->fgdefault; + } + else if (t->bold_is_bright && t->bold) { + return t->brightcolors[color]; + } + else { + return t->colors[color]; + } +} + +static cairo_pattern_t *runes_display_get_bgcolor(RunesTerm *t) +{ + int color = t->inverse ? t->fgcolor : t->bgcolor; + + if (t->inverse && t->bgcolor == t->fgcolor) { + return t->fgdefault; + } + else if (color == -1) { + return t->inverse ? t->fgdefault : t->bgdefault; + } + else { + return t->colors[color]; + } +} + +static void runes_display_recalculate_font_metrics(RunesTerm *t) +{ + PangoFontDescription *desc; + PangoContext *context; + PangoFontMetrics *metrics; + int ascent, descent; + + desc = pango_font_description_from_string(t->font_name); + + if (t->layout) { + context = pango_layout_get_context(t->layout); + } + else { + context = pango_font_map_create_context( + pango_cairo_font_map_get_default()); + } + + metrics = pango_context_get_metrics(context, desc, NULL); + + t->fontx = PANGO_PIXELS( + pango_font_metrics_get_approximate_digit_width(metrics)); + ascent = pango_font_metrics_get_ascent(metrics); + descent = pango_font_metrics_get_descent(metrics); + t->fonty = PANGO_PIXELS(ascent + descent); + + pango_font_metrics_unref(metrics); + pango_font_description_free(desc); + if (!t->layout) { + g_object_unref(context); + } +} + +static void runes_display_position_cursor(RunesTerm *t, cairo_t *cr) +{ + cairo_move_to(cr, t->col * t->fontx, t->row * t->fonty); +} + +static void runes_display_paint_rectangle( + RunesTerm *t, cairo_t *cr, cairo_pattern_t *pattern, + int x, int y, int width, int height) +{ + cairo_save(cr); + cairo_set_source(cr, pattern); + cairo_rectangle( + cr, x * t->fontx, y * t->fonty, width * t->fontx, height * t->fonty); + cairo_fill(cr); + cairo_restore(cr); + runes_display_position_cursor(t, t->cr); +} + +static void runes_display_scroll_down(RunesTerm *t, int rows) +{ + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + + cairo_save(t->cr); + cairo_push_group(t->cr); + pattern = cairo_pattern_create_for_surface(cairo_get_target(t->cr)); + cairo_matrix_init_translate(&matrix, 0.0, rows * t->fonty); + cairo_pattern_set_matrix(pattern, &matrix); + runes_display_paint_rectangle( + t, t->cr, pattern, + 0, t->scroll_top, t->cols, t->scroll_bottom - t->scroll_top); + cairo_pattern_destroy(pattern); + cairo_pop_group_to_source(t->cr); + cairo_paint(t->cr); + runes_display_paint_rectangle( + t, t->cr, t->bgdefault, 0, t->scroll_bottom + 1 - rows, t->cols, rows); + cairo_restore(t->cr); +} -- cgit v1.2.3-54-g00ecf