/* * Copyright (C) 2003,2008 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "debug.h" #include "vtedraw.h" #include "vte-private.h" #include /* Overview: * * * This file implements vte rendering using pangocairo. Note that this does * NOT implement any kind of complex text rendering. That's not currently a * goal. * * The aim is to be super-fast and avoid unneeded work as much as possible. * Here is an overview of how that is accomplished: * * - We attach a font_info to the draw. A font_info has all the information * to quickly draw text. * * - A font_info keeps uses unistr_font_info structs that represent all * information needed to quickly draw a single vteunistr. The font_info * creates those unistr_font_info structs on demand and caches them * indefinitely. It uses a direct array for the ASCII range and a hash * table for the rest. * * * Fast rendering of unistrs: * * A unistr_font_info (uinfo) calls Pango to set text for the unistr upon * initialization and then caches information needed to draw the results * later. It uses three different internal representations and respectively * three drawing paths: * * - COVERAGE_USE_CAIRO_GLYPH: * Keeping a single glyph index and a cairo scaled-font. This is the * fastest way to draw text as it bypasses Pango completely and allows * for stuffing multiple glyphs into a single cairo_show_glyphs() request * (if scaled-fonts match). This method is used if the glyphs used for * the vteunistr as determined by Pango consists of a single regular glyph * positioned at 0,0 using a regular font. This method is used for more * than 99% of the cases. Only exceptional cases fall through to the * other two methods. * * - COVERAGE_USE_PANGO_GLYPH_STRING: * Keeping a pango glyphstring and a pango font. This is slightly slower * than the previous case as drawing each glyph goes through pango * separately and causes a separate cairo_show_glyphs() call. This method * is used when the previous method cannot be used but the glyphs for the * character all use a single font. This is the method used for hexboxes * and "empty" characters like U+200C ZERO WIDTH NON-JOINER for example. * * - COVERAGE_USE_PANGO_LAYOUT_LINE: * Keeping a pango layout line. This method is used only in the very * weird and exceptional case that a single vteunistr uses more than one * font to be drawn. This happens for example if some diacretics is not * available in the font chosen for the base character. * * * Caching of font infos: * * To avoid recreating font info structs for the same font again and again we * do the following: * * - Use a global cache to share font info structs across different widgets. * We use pango language, cairo font options, resolution, and font description * as the key for our hash table. * * - When a font info struct is no longer used by any widget, we delay * destroying it for a while (FONT_CACHE_TIMEOUT seconds). This is * supposed to serve two purposes: * * * Destroying a terminal widget and creating it again right after will * reuse the font info struct from the previous widget. * * * Zooming in and out a terminal reuses the font info structs. * * * Pre-caching ASCII letters: * * When initializing a font info struct we measure a string consisting of all * ASCII letters and some other ASCII characters. Since we have a shaped pango * layout at hand, we walk over it and cache unistr font info for the ASCII * letters if we can do that easily using COVERAGE_USE_CAIRO_GLYPH. This * means that we precache all ASCII letters without any extra pango shaping * involved. */ #define FONT_CACHE_TIMEOUT (30) /* seconds */ /* All shared data structures are implicitly protected by GDK mutex, because * that's how vte.c works and we only get called from there. */ /* cairo_show_glyphs accepts runs up to 102 glyphs before it allocates a * temporary array. * * Setting this to a large value can cause dramatic slow-downs for some * xservers (notably fglrx), see bug #410534. * * Moreover, setting it larger than %VTE_DRAW_MAX_LENGTH is nonsensical, * as the higher layers will not submit runs longer than that value. */ #define MAX_RUN_LENGTH 100 enum unistr_coverage { /* in increasing order of speed */ COVERAGE_UNKNOWN = 0, /* we don't know about the character yet */ COVERAGE_USE_PANGO_LAYOUT_LINE, /* use a PangoLayoutLine for the character */ COVERAGE_USE_PANGO_GLYPH_STRING, /* use a PangoGlyphString for the character */ COVERAGE_USE_CAIRO_GLYPH /* use a cairo_glyph_t for the character */ }; union unistr_font_info { /* COVERAGE_USE_PANGO_LAYOUT_LINE */ struct { PangoLayoutLine *line; } using_pango_layout_line; /* COVERAGE_USE_PANGO_GLYPH_STRING */ struct { PangoFont *font; PangoGlyphString *glyph_string; } using_pango_glyph_string; /* COVERAGE_USE_CAIRO_GLYPH */ struct { cairo_scaled_font_t *scaled_font; unsigned int glyph_index; } using_cairo_glyph; }; struct unistr_info { guchar coverage; guchar has_unknown_chars; guint16 width; union unistr_font_info ufi; }; static struct unistr_info * unistr_info_create (void) { return g_slice_new0 (struct unistr_info); } static void unistr_info_finish (struct unistr_info *uinfo) { union unistr_font_info *ufi = &uinfo->ufi; switch (uinfo->coverage) { default: case COVERAGE_UNKNOWN: break; case COVERAGE_USE_PANGO_LAYOUT_LINE: /* we hold a manual reference on layout */ g_object_unref (ufi->using_pango_layout_line.line->layout); ufi->using_pango_layout_line.line->layout = NULL; pango_layout_line_unref (ufi->using_pango_layout_line.line); ufi->using_pango_layout_line.line = NULL; break; case COVERAGE_USE_PANGO_GLYPH_STRING: if (ufi->using_pango_glyph_string.font) g_object_unref (ufi->using_pango_glyph_string.font); ufi->using_pango_glyph_string.font = NULL; pango_glyph_string_free (ufi->using_pango_glyph_string.glyph_string); ufi->using_pango_glyph_string.glyph_string = NULL; break; case COVERAGE_USE_CAIRO_GLYPH: cairo_scaled_font_destroy (ufi->using_cairo_glyph.scaled_font); ufi->using_cairo_glyph.scaled_font = NULL; break; } } static void unistr_info_destroy (struct unistr_info *uinfo) { unistr_info_finish (uinfo); g_slice_free (struct unistr_info, uinfo); } struct font_info { /* lifecycle */ int ref_count; guint destroy_timeout; /* only used when ref_count == 0 */ /* reusable layout set with font and everything set */ PangoLayout *layout; /* cache of character info */ struct unistr_info ascii_unistr_info[128]; GHashTable *other_unistr_info; /* cell metrics */ gint width, height, ascent; /* reusable string for UTF-8 conversion */ GString *string; #ifdef VTE_DEBUG /* profiling info */ int coverage_count[4]; #endif }; static struct unistr_info * font_info_find_unistr_info (struct font_info *info, vteunistr c) { struct unistr_info *uinfo; if (G_LIKELY (c < G_N_ELEMENTS (info->ascii_unistr_info))) return &info->ascii_unistr_info[c]; if (G_UNLIKELY (info->other_unistr_info == NULL)) info->other_unistr_info = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) unistr_info_destroy); uinfo = (struct unistr_info *)g_hash_table_lookup (info->other_unistr_info, GINT_TO_POINTER (c)); if (G_LIKELY (uinfo)) return uinfo; uinfo = unistr_info_create (); g_hash_table_insert (info->other_unistr_info, GINT_TO_POINTER (c), uinfo); return uinfo; } static void font_info_cache_ascii (struct font_info *info) { PangoLayoutLine *line; PangoGlyphItemIter iter; PangoGlyphItem *glyph_item; PangoGlyphString *glyph_string; PangoFont *pango_font; cairo_scaled_font_t *scaled_font; const char *text; gboolean more; PangoLanguage *language; gboolean latin_uses_default_language; /* We have info->layout holding most ASCII characters. We want to * cache as much info as we can about the ASCII letters so we don't * have to look them up again later */ /* Don't cache if unknown glyphs found in layout */ if (pango_layout_get_unknown_glyphs_count (info->layout) != 0) return; language = pango_context_get_language (pango_layout_get_context (info->layout)); if (language == NULL) language = pango_language_get_default (); latin_uses_default_language = pango_language_includes_script (language, PANGO_SCRIPT_LATIN); text = pango_layout_get_text (info->layout); line = pango_layout_get_line_readonly (info->layout, 0); /* Don't cache if more than one font used for the line */ if (G_UNLIKELY (!line || !line->runs || line->runs->next)) return; glyph_item = (PangoGlyphItem *)line->runs->data; glyph_string = glyph_item->glyphs; pango_font = glyph_item->item->analysis.font; if (!pango_font) return; scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *) pango_font); if (!scaled_font) return; for (more = pango_glyph_item_iter_init_start (&iter, glyph_item, text); more; more = pango_glyph_item_iter_next_cluster (&iter)) { struct unistr_info *uinfo; union unistr_font_info *ufi; PangoGlyphGeometry *geometry; PangoGlyph glyph; vteunistr c; /* Only cache simple clusters */ if (iter.start_char +1 != iter.end_char || iter.start_index+1 != iter.end_index || iter.start_glyph+1 != iter.end_glyph) continue; c = text[iter.start_index]; glyph = glyph_string->glyphs[iter.start_glyph].glyph; geometry = &glyph_string->glyphs[iter.start_glyph].geometry; /* If not using the default locale language, only cache non-common * characters as common characters get their font from their neighbors * and we don't want to force Latin on them. */ if (!latin_uses_default_language && pango_script_for_unichar (c) <= PANGO_SCRIPT_INHERITED) continue; /* Only cache simple glyphs */ if (!(glyph <= 0xFFFF) || (geometry->x_offset | geometry->y_offset) != 0) continue; uinfo = font_info_find_unistr_info (info, c); if (G_UNLIKELY (uinfo->coverage != COVERAGE_UNKNOWN)) continue; ufi = &uinfo->ufi; uinfo->width = PANGO_PIXELS_CEIL (geometry->width); uinfo->has_unknown_chars = FALSE; uinfo->coverage = COVERAGE_USE_CAIRO_GLYPH; ufi->using_cairo_glyph.scaled_font = cairo_scaled_font_reference (scaled_font); ufi->using_cairo_glyph.glyph_index = glyph; #ifdef VTE_DEBUG info->coverage_count[0]++; info->coverage_count[uinfo->coverage]++; #endif } #ifdef VTE_DEBUG _vte_debug_print (VTE_DEBUG_PANGOCAIRO, "vtepangocairo: %p cached %d ASCII letters\n", info, info->coverage_count[0]); #endif } static void font_info_measure_font (struct font_info *info) { PangoRectangle logical; /* Estimate for ASCII characters. */ pango_layout_set_text (info->layout, VTE_DRAW_SINGLE_WIDE_CHARACTERS, -1); pango_layout_get_extents (info->layout, NULL, &logical); /* We don't do CEIL for width since we are averaging; * rounding is more accurate */ info->width = PANGO_PIXELS (howmany (logical.width, strlen(VTE_DRAW_SINGLE_WIDE_CHARACTERS))); /* Guard against pathological font since width=0 causes a FPE later on */ info->width = MAX (info->width, 1); info->height = PANGO_PIXELS_CEIL (logical.height); info->ascent = PANGO_PIXELS_CEIL (pango_layout_get_baseline (info->layout)); /* Now that we shaped the entire ASCII character string, cache glyph * info for them */ font_info_cache_ascii (info); if (info->height == 0) { info->height = PANGO_PIXELS_CEIL (logical.height); } if (info->ascent == 0) { info->ascent = PANGO_PIXELS_CEIL (pango_layout_get_baseline (info->layout)); } _vte_debug_print (VTE_DEBUG_MISC, "vtepangocairo: %p font metrics = %dx%d (%d)\n", info, info->width, info->height, info->ascent); } static struct font_info * font_info_allocate (PangoContext *context) { struct font_info *info; PangoTabArray *tabs; info = g_slice_new0 (struct font_info); _vte_debug_print (VTE_DEBUG_PANGOCAIRO, "vtepangocairo: %p allocating font_info\n", info); info->layout = pango_layout_new (context); tabs = pango_tab_array_new_with_positions (1, FALSE, PANGO_TAB_LEFT, 1); pango_layout_set_tabs (info->layout, tabs); pango_tab_array_free (tabs); info->string = g_string_sized_new (VTE_UTF8_BPC+1); font_info_measure_font (info); return info; } static void font_info_free (struct font_info *info) { vteunistr i; #ifdef VTE_DEBUG _vte_debug_print (VTE_DEBUG_PANGOCAIRO, "vtepangocairo: %p freeing font_info. coverages %d = %d + %d + %d\n", info, info->coverage_count[0], info->coverage_count[1], info->coverage_count[2], info->coverage_count[3]); #endif g_string_free (info->string, TRUE); g_object_unref (info->layout); for (i = 0; i < G_N_ELEMENTS (info->ascii_unistr_info); i++) unistr_info_finish (&info->ascii_unistr_info[i]); if (info->other_unistr_info) { g_hash_table_destroy (info->other_unistr_info); } g_slice_free (struct font_info, info); } static GHashTable *font_info_for_context; static struct font_info * font_info_register (struct font_info *info) { g_hash_table_insert (font_info_for_context, pango_layout_get_context (info->layout), info); return info; } static void font_info_unregister (struct font_info *info) { g_hash_table_remove (font_info_for_context, pango_layout_get_context (info->layout)); } static struct font_info * font_info_reference (struct font_info *info) { if (!info) return info; g_return_val_if_fail (info->ref_count >= 0, info); if (info->destroy_timeout) { g_source_remove (info->destroy_timeout); info->destroy_timeout = 0; } info->ref_count++; return info; } static gboolean font_info_destroy_delayed (struct font_info *info) { info->destroy_timeout = 0; font_info_unregister (info); font_info_free (info); return FALSE; } static void font_info_destroy (struct font_info *info) { if (!info) return; g_return_if_fail (info->ref_count > 0); info->ref_count--; if (info->ref_count) return; /* Delay destruction by a few seconds, in case we need it again */ info->destroy_timeout = gdk_threads_add_timeout_seconds (FONT_CACHE_TIMEOUT, (GSourceFunc) font_info_destroy_delayed, info); } static GQuark fontconfig_timestamp_quark (void) { static GQuark quark; if (G_UNLIKELY (!quark)) quark = g_quark_from_static_string ("vte-fontconfig-timestamp"); return quark; } static void vte_pango_context_set_fontconfig_timestamp (PangoContext *context, guint fontconfig_timestamp) { g_object_set_qdata ((GObject *) context, fontconfig_timestamp_quark (), GUINT_TO_POINTER (fontconfig_timestamp)); } static guint vte_pango_context_get_fontconfig_timestamp (PangoContext *context) { return GPOINTER_TO_UINT (g_object_get_qdata ((GObject *) context, fontconfig_timestamp_quark ())); } static guint context_hash (PangoContext *context) { return pango_units_from_double (pango_cairo_context_get_resolution (context)) ^ pango_font_description_hash (pango_context_get_font_description (context)) ^ cairo_font_options_hash (pango_cairo_context_get_font_options (context)) ^ GPOINTER_TO_UINT (pango_context_get_language (context)) ^ vte_pango_context_get_fontconfig_timestamp (context); } static gboolean context_equal (PangoContext *a, PangoContext *b) { return _vte_double_equal(pango_cairo_context_get_resolution(a), pango_cairo_context_get_resolution (b)) && pango_font_description_equal (pango_context_get_font_description (a), pango_context_get_font_description (b)) && cairo_font_options_equal (pango_cairo_context_get_font_options (a), pango_cairo_context_get_font_options (b)) && pango_context_get_language (a) == pango_context_get_language (b) && vte_pango_context_get_fontconfig_timestamp (a) == vte_pango_context_get_fontconfig_timestamp (b); } static struct font_info * font_info_find_for_context (PangoContext *context) { struct font_info *info; if (G_UNLIKELY (font_info_for_context == NULL)) font_info_for_context = g_hash_table_new ((GHashFunc) context_hash, (GEqualFunc) context_equal); info = (struct font_info *)g_hash_table_lookup (font_info_for_context, context); if (G_LIKELY (info)) { _vte_debug_print (VTE_DEBUG_PANGOCAIRO, "vtepangocairo: %p found font_info in cache\n", info); info = font_info_reference (info); } else { info = font_info_allocate (context); info->ref_count = 1; font_info_register (info); } g_object_unref (context); return info; } /* assumes ownership/reference of context */ static struct font_info * font_info_create_for_context (PangoContext *context, const PangoFontDescription *desc, PangoLanguage *language, guint fontconfig_timestamp) { if (!PANGO_IS_CAIRO_FONT_MAP (pango_context_get_font_map (context))) { /* Ouch, Gtk+ switched over to some drawing system? * Lets just create one from the default font map. */ g_object_unref (context); context = pango_font_map_create_context (pango_cairo_font_map_get_default ()); } vte_pango_context_set_fontconfig_timestamp (context, fontconfig_timestamp); pango_context_set_base_dir (context, PANGO_DIRECTION_LTR); if (desc) pango_context_set_font_description (context, desc); pango_context_set_language (context, language); /* Make sure our contexts have a font_options set. We use * this invariant in our context hash and equal functions. */ if (!pango_cairo_context_get_font_options (context)) { cairo_font_options_t *font_options; font_options = cairo_font_options_create (); pango_cairo_context_set_font_options (context, font_options); cairo_font_options_destroy (font_options); } return font_info_find_for_context (context); } static struct font_info * font_info_create_for_screen (GdkScreen *screen, const PangoFontDescription *desc, PangoLanguage *language) { GtkSettings *settings = gtk_settings_get_for_screen (screen); int fontconfig_timestamp; g_object_get (settings, "gtk-fontconfig-timestamp", &fontconfig_timestamp, NULL); return font_info_create_for_context (gdk_pango_context_get_for_screen (screen), desc, language, fontconfig_timestamp); } static struct font_info * font_info_create_for_widget (GtkWidget *widget, const PangoFontDescription *desc) { GdkScreen *screen = gtk_widget_get_screen (widget); PangoLanguage *language = pango_context_get_language (gtk_widget_get_pango_context (widget)); return font_info_create_for_screen (screen, desc, language); } static struct unistr_info * font_info_get_unistr_info (struct font_info *info, vteunistr c) { struct unistr_info *uinfo; union unistr_font_info *ufi; PangoRectangle logical; PangoLayoutLine *line; uinfo = font_info_find_unistr_info (info, c); if (G_LIKELY (uinfo->coverage != COVERAGE_UNKNOWN)) return uinfo; ufi = &uinfo->ufi; g_string_set_size (info->string, 0); _vte_unistr_append_to_string (c, info->string); pango_layout_set_text (info->layout, info->string->str, -1); pango_layout_get_extents (info->layout, NULL, &logical); uinfo->width = PANGO_PIXELS_CEIL (logical.width); line = pango_layout_get_line_readonly (info->layout, 0); uinfo->has_unknown_chars = pango_layout_get_unknown_glyphs_count (info->layout) != 0; /* we use PangoLayoutRun rendering unless there is exactly one run in the line. */ if (G_UNLIKELY (!line || !line->runs || line->runs->next)) { uinfo->coverage = COVERAGE_USE_PANGO_LAYOUT_LINE; ufi->using_pango_layout_line.line = pango_layout_line_ref (line); /* we hold a manual reference on layout. pango currently * doesn't work if line->layout is NULL. ugh! */ pango_layout_set_text (info->layout, "", -1); /* make layout disassociate from the line */ ufi->using_pango_layout_line.line->layout = (PangoLayout *)g_object_ref (info->layout); } else { PangoGlyphItem *glyph_item = (PangoGlyphItem *)line->runs->data; PangoFont *pango_font = glyph_item->item->analysis.font; PangoGlyphString *glyph_string = glyph_item->glyphs; /* we use fast cairo path if glyph string has only one real * glyph and at origin */ if (!uinfo->has_unknown_chars && glyph_string->num_glyphs == 1 && glyph_string->glyphs[0].glyph <= 0xFFFF && (glyph_string->glyphs[0].geometry.x_offset | glyph_string->glyphs[0].geometry.y_offset) == 0) { cairo_scaled_font_t *scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *) pango_font); if (scaled_font) { uinfo->coverage = COVERAGE_USE_CAIRO_GLYPH; ufi->using_cairo_glyph.scaled_font = cairo_scaled_font_reference (scaled_font); ufi->using_cairo_glyph.glyph_index = glyph_string->glyphs[0].glyph; } } /* use pango fast path otherwise */ if (G_UNLIKELY (uinfo->coverage == COVERAGE_UNKNOWN)) { uinfo->coverage = COVERAGE_USE_PANGO_GLYPH_STRING; ufi->using_pango_glyph_string.font = pango_font ? (PangoFont *)g_object_ref (pango_font) : NULL; ufi->using_pango_glyph_string.glyph_string = pango_glyph_string_copy (glyph_string); } } /* release internal layout resources */ pango_layout_set_text (info->layout, "", -1); #ifdef VTE_DEBUG info->coverage_count[0]++; info->coverage_count[uinfo->coverage]++; #endif return uinfo; } guint _vte_draw_get_style(gboolean bold, gboolean italic) { guint style = 0; if (bold) style |= VTE_DRAW_BOLD; if (italic) style |= VTE_DRAW_ITALIC; return style; } struct _vte_draw { GtkWidget *widget; struct font_info *fonts[4]; cairo_pattern_t *bg_pattern; cairo_t *cr; }; struct _vte_draw * _vte_draw_new (void) { struct _vte_draw *draw; /* Create the structure. */ draw = g_slice_new0 (struct _vte_draw); _vte_debug_print (VTE_DEBUG_DRAW, "draw_new\n"); return draw; } cairo_t * _vte_draw_get_context (struct _vte_draw *draw) { return draw->cr; } void _vte_draw_free (struct _vte_draw *draw) { gint style; _vte_debug_print (VTE_DEBUG_DRAW, "draw_free\n"); if (draw->bg_pattern != NULL) { cairo_pattern_destroy (draw->bg_pattern); draw->bg_pattern = NULL; } /* Free all fonts (make sure to destroy every font only once)*/ for (style = 3; style >= 0; style--) { if (draw->fonts[style] != NULL && (style == 0 || draw->fonts[style] != draw->fonts[style-1])) { font_info_destroy (draw->fonts[style]); draw->fonts[style] = NULL; } } g_slice_free (struct _vte_draw, draw); } void _vte_draw_set_cairo (struct _vte_draw *draw, cairo_t *cr) { _vte_debug_print (VTE_DEBUG_DRAW, "%s cairo context\n", cr ? "Settings" : "Unsetting"); if (cr) { g_assert (draw->cr == NULL); draw->cr = cr; } else { g_assert (draw->cr != NULL); draw->cr = NULL; } } void _vte_draw_set_background_solid(struct _vte_draw *draw, const GdkRGBA *color) { if (draw->bg_pattern) cairo_pattern_destroy (draw->bg_pattern); draw->bg_pattern = cairo_pattern_create_rgba (color->red, color->green, color->blue, color->alpha); } void _vte_draw_clear (struct _vte_draw *draw, gint x, gint y, gint width, gint height) { g_return_if_fail (draw->bg_pattern != NULL); _vte_debug_print (VTE_DEBUG_DRAW, "draw_clear (%d, %d, %d, %d)\n", x,y,width, height); g_assert(draw->cr); cairo_rectangle (draw->cr, x, y, width, height); cairo_set_operator (draw->cr, CAIRO_OPERATOR_SOURCE); cairo_set_source (draw->cr, draw->bg_pattern); cairo_fill (draw->cr); } void _vte_draw_set_text_font (struct _vte_draw *draw, GtkWidget *widget, const PangoFontDescription *fontdesc) { PangoFontDescription *bolddesc = NULL; PangoFontDescription *italicdesc = NULL; PangoFontDescription *bolditalicdesc = NULL; gint style, normal, bold, ratio; _vte_debug_print (VTE_DEBUG_DRAW, "draw_set_text_font\n"); /* Free all fonts (make sure to destroy every font only once)*/ for (style = 3; style >= 0; style--) { if (draw->fonts[style] != NULL && (style == 0 || draw->fonts[style] != draw->fonts[style-1])) { font_info_destroy (draw->fonts[style]); draw->fonts[style] = NULL; } } /* calculate bold font desc */ bolddesc = pango_font_description_copy (fontdesc); pango_font_description_set_weight (bolddesc, PANGO_WEIGHT_BOLD); /* calculate italic font desc */ italicdesc = pango_font_description_copy (fontdesc); pango_font_description_set_style (italicdesc, PANGO_STYLE_ITALIC); /* calculate bold italic font desc */ bolditalicdesc = pango_font_description_copy (bolddesc); pango_font_description_set_style (bolditalicdesc, PANGO_STYLE_ITALIC); draw->fonts[VTE_DRAW_NORMAL] = font_info_create_for_widget (widget, fontdesc); draw->fonts[VTE_DRAW_BOLD] = font_info_create_for_widget (widget, bolddesc); draw->fonts[VTE_DRAW_ITALIC] = font_info_create_for_widget (widget, italicdesc); draw->fonts[VTE_DRAW_ITALIC | VTE_DRAW_BOLD] = font_info_create_for_widget (widget, bolditalicdesc); pango_font_description_free (bolddesc); pango_font_description_free (italicdesc); pango_font_description_free (bolditalicdesc); /* Decide if we should keep this bold font face, per bug 54926: * - reject bold font if it is not within 10% of normal font width */ normal = VTE_DRAW_NORMAL; bold = normal | VTE_DRAW_BOLD; ratio = draw->fonts[bold]->width * 100 / draw->fonts[normal]->width; if (abs(ratio - 100) > 10) { _vte_debug_print (VTE_DEBUG_DRAW, "Rejecting bold font (%i%%).\n", ratio); font_info_destroy (draw->fonts[bold]); draw->fonts[bold] = draw->fonts[normal]; } normal = VTE_DRAW_ITALIC; bold = normal | VTE_DRAW_BOLD; ratio = draw->fonts[bold]->width * 100 / draw->fonts[normal]->width; if (abs(ratio - 100) > 10) { _vte_debug_print (VTE_DEBUG_DRAW, "Rejecting italic bold font (%i%%).\n", ratio); font_info_destroy (draw->fonts[bold]); draw->fonts[bold] = draw->fonts[normal]; } } void _vte_draw_get_text_metrics(struct _vte_draw *draw, gint *width, gint *height, gint *ascent) { g_return_if_fail (draw->fonts[VTE_DRAW_NORMAL] != NULL); if (width) *width = draw->fonts[VTE_DRAW_NORMAL]->width; if (height) *height = draw->fonts[VTE_DRAW_NORMAL]->height; if (ascent) *ascent = draw->fonts[VTE_DRAW_NORMAL]->ascent; } int _vte_draw_get_char_width (struct _vte_draw *draw, vteunistr c, int columns, guint style) { struct unistr_info *uinfo; g_return_val_if_fail (draw->fonts[VTE_DRAW_NORMAL] != NULL, 0); uinfo = font_info_get_unistr_info (draw->fonts[style], c); return uinfo->width; } gboolean _vte_draw_has_bold (struct _vte_draw *draw, guint style) { return (draw->fonts[style ^ VTE_DRAW_BOLD] != draw->fonts[style]); } static void _vte_draw_set_source_color_alpha (struct _vte_draw *draw, const PangoColor *color, guchar alpha) { g_assert(draw->cr); cairo_set_source_rgba (draw->cr, color->red / 65535., color->green / 65535., color->blue / 65535., alpha / 255.); } /* Check if a unicode character is actually a graphic character we draw * ourselves to handle cases where fonts don't have glyphs for them. */ static gboolean _vte_draw_unichar_is_local_graphic(vteunistr c) { /* Box Drawing & Block Elements */ return (c >= 0x2500) && (c <= 0x259f); } #include "box_drawing.h" /* Draw the graphic representation of a line-drawing or special graphics * character. */ static void _vte_draw_terminal_draw_graphic(struct _vte_draw *draw, vteunistr c, const PangoColor *fg, gint x, gint y, gint column_width, gint columns, gint row_height) { gint width, xcenter, xright, ycenter, ybottom; int upper_half, lower_half, left_half, right_half; int light_line_width, heavy_line_width; double adjust; cairo_t *cr = draw->cr; cairo_save (cr); width = column_width * columns; upper_half = row_height / 2; lower_half = row_height - upper_half; left_half = width / 2; right_half = width - left_half; /* Note that the upper/left halves above are the same as 4 eights */ /* FIXME: this could be smarter for very small n (< 8 resp. < 4) */ #define EIGHTS(n, k) \ ({ int k_eights = (n) * (k) / 8; \ k_eights = MAX(k_eights, 1); \ k_eights; \ }) light_line_width = column_width / 5; light_line_width = MAX (light_line_width, 1); if (c >= 0x2550 && c <= 0x256c) { heavy_line_width = 3 * light_line_width; } else { heavy_line_width = light_line_width + 2; } xcenter = x + left_half; ycenter = y + upper_half; xright = x + width; ybottom = y + row_height; switch (c) { /* Box Drawing */ case 0x2500: /* box drawings light horizontal */ case 0x2501: /* box drawings heavy horizontal */ case 0x2502: /* box drawings light vertical */ case 0x2503: /* box drawings heavy vertical */ case 0x250c: /* box drawings light down and right */ case 0x250d: /* box drawings down light and right heavy */ case 0x250e: /* box drawings down heavy and right light */ case 0x250f: /* box drawings heavy down and right */ case 0x2510: /* box drawings light down and left */ case 0x2511: /* box drawings down light and left heavy */ case 0x2512: /* box drawings down heavy and left light */ case 0x2513: /* box drawings heavy down and left */ case 0x2514: /* box drawings light up and right */ case 0x2515: /* box drawings up light and right heavy */ case 0x2516: /* box drawings up heavy and right light */ case 0x2517: /* box drawings heavy up and right */ case 0x2518: /* box drawings light up and left */ case 0x2519: /* box drawings up light and left heavy */ case 0x251a: /* box drawings up heavy and left light */ case 0x251b: /* box drawings heavy up and left */ case 0x251c: /* box drawings light vertical and right */ case 0x251d: /* box drawings vertical light and right heavy */ case 0x251e: /* box drawings up heavy and right down light */ case 0x251f: /* box drawings down heavy and right up light */ case 0x2520: /* box drawings vertical heavy and right light */ case 0x2521: /* box drawings down light and right up heavy */ case 0x2522: /* box drawings up light and right down heavy */ case 0x2523: /* box drawings heavy vertical and right */ case 0x2524: /* box drawings light vertical and left */ case 0x2525: /* box drawings vertical light and left heavy */ case 0x2526: /* box drawings up heavy and left down light */ case 0x2527: /* box drawings down heavy and left up light */ case 0x2528: /* box drawings vertical heavy and left light */ case 0x2529: /* box drawings down light and left up heavy */ case 0x252a: /* box drawings up light and left down heavy */ case 0x252b: /* box drawings heavy vertical and left */ case 0x252c: /* box drawings light down and horizontal */ case 0x252d: /* box drawings left heavy and right down light */ case 0x252e: /* box drawings right heavy and left down light */ case 0x252f: /* box drawings down light and horizontal heavy */ case 0x2530: /* box drawings down heavy and horizontal light */ case 0x2531: /* box drawings right light and left down heavy */ case 0x2532: /* box drawings left light and right down heavy */ case 0x2533: /* box drawings heavy down and horizontal */ case 0x2534: /* box drawings light up and horizontal */ case 0x2535: /* box drawings left heavy and right up light */ case 0x2536: /* box drawings right heavy and left up light */ case 0x2537: /* box drawings up light and horizontal heavy */ case 0x2538: /* box drawings up heavy and horizontal light */ case 0x2539: /* box drawings right light and left up heavy */ case 0x253a: /* box drawings left light and right up heavy */ case 0x253b: /* box drawings heavy up and horizontal */ case 0x253c: /* box drawings light vertical and horizontal */ case 0x253d: /* box drawings left heavy and right vertical light */ case 0x253e: /* box drawings right heavy and left vertical light */ case 0x253f: /* box drawings vertical light and horizontal heavy */ case 0x2540: /* box drawings up heavy and down horizontal light */ case 0x2541: /* box drawings down heavy and up horizontal light */ case 0x2542: /* box drawings vertical heavy and horizontal light */ case 0x2543: /* box drawings left up heavy and right down light */ case 0x2544: /* box drawings right up heavy and left down light */ case 0x2545: /* box drawings left down heavy and right up light */ case 0x2546: /* box drawings right down heavy and left up light */ case 0x2547: /* box drawings down light and up horizontal heavy */ case 0x2548: /* box drawings up light and down horizontal heavy */ case 0x2549: /* box drawings right light and left vertical heavy */ case 0x254a: /* box drawings left light and right vertical heavy */ case 0x254b: /* box drawings heavy vertical and horizontal */ case 0x2550: /* box drawings double horizontal */ case 0x2551: /* box drawings double vertical */ case 0x2552: /* box drawings down single and right double */ case 0x2553: /* box drawings down double and right single */ case 0x2554: /* box drawings double down and right */ case 0x2555: /* box drawings down single and left double */ case 0x2556: /* box drawings down double and left single */ case 0x2557: /* box drawings double down and left */ case 0x2558: /* box drawings up single and right double */ case 0x2559: /* box drawings up double and right single */ case 0x255a: /* box drawings double up and right */ case 0x255b: /* box drawings up single and left double */ case 0x255c: /* box drawings up double and left single */ case 0x255d: /* box drawings double up and left */ case 0x255e: /* box drawings vertical single and right double */ case 0x255f: /* box drawings vertical double and right single */ case 0x2560: /* box drawings double vertical and right */ case 0x2561: /* box drawings vertical single and left double */ case 0x2562: /* box drawings vertical double and left single */ case 0x2563: /* box drawings double vertical and left */ case 0x2564: /* box drawings down single and horizontal double */ case 0x2565: /* box drawings down double and horizontal single */ case 0x2566: /* box drawings double down and horizontal */ case 0x2567: /* box drawings up single and horizontal double */ case 0x2568: /* box drawings up double and horizontal single */ case 0x2569: /* box drawings double up and horizontal */ case 0x256a: /* box drawings vertical single and horizontal double */ case 0x256b: /* box drawings vertical double and horizontal single */ case 0x256c: /* box drawings double vertical and horizontal */ case 0x2574: /* box drawings light left */ case 0x2575: /* box drawings light up */ case 0x2576: /* box drawings light right */ case 0x2577: /* box drawings light down */ case 0x2578: /* box drawings heavy left */ case 0x2579: /* box drawings heavy up */ case 0x257a: /* box drawings heavy right */ case 0x257b: /* box drawings heavy down */ case 0x257c: /* box drawings light left and heavy right */ case 0x257d: /* box drawings light up and heavy down */ case 0x257e: /* box drawings heavy left and light right */ case 0x257f: /* box drawings heavy up and light down */ { guint32 bitmap = _vte_draw_box_drawing_bitmaps[c - 0x2500]; int xboundaries[6] = { 0, left_half - heavy_line_width / 2, left_half - light_line_width / 2, left_half - light_line_width / 2 + light_line_width, left_half - heavy_line_width / 2 + heavy_line_width, width}; int yboundaries[6] = { 0, upper_half - heavy_line_width / 2, upper_half - light_line_width / 2, upper_half - light_line_width / 2 + light_line_width, upper_half - heavy_line_width / 2 + heavy_line_width, row_height}; int xi, yi; cairo_set_line_width(cr, 0); for (yi = 4; yi >= 0; yi--) { for (xi = 4; xi >= 0; xi--) { if (bitmap & 1) { cairo_rectangle(cr, x + xboundaries[xi], y + yboundaries[yi], xboundaries[xi + 1] - xboundaries[xi], yboundaries[yi + 1] - yboundaries[yi]); cairo_fill(cr); } bitmap >>= 1; } } break; } case 0x2504: /* box drawings light triple dash horizontal */ case 0x2505: /* box drawings heavy triple dash horizontal */ case 0x2506: /* box drawings light triple dash vertical */ case 0x2507: /* box drawings heavy triple dash vertical */ case 0x2508: /* box drawings light quadruple dash horizontal */ case 0x2509: /* box drawings heavy quadruple dash horizontal */ case 0x250a: /* box drawings light quadruple dash vertical */ case 0x250b: /* box drawings heavy quadruple dash vertical */ case 0x254c: /* box drawings light double dash horizontal */ case 0x254d: /* box drawings heavy double dash horizontal */ case 0x254e: /* box drawings light double dash vertical */ case 0x254f: /* box drawings heavy double dash vertical */ { const guint v = c - 0x2500; int size, line_width; size = (v & 2) ? row_height : width; switch (v >> 2) { case 1: /* triple dash */ { double segment = size / 8.; double dashes[2] = { segment * 2., segment }; cairo_set_dash(cr, dashes, G_N_ELEMENTS(dashes), 0.); break; } case 2: /* quadruple dash */ { double segment = size / 11.; double dashes[2] = { segment * 2., segment }; cairo_set_dash(cr, dashes, G_N_ELEMENTS(dashes), 0.); break; } case 19: /* double dash */ { double segment = size / 5.; double dashes[2] = { segment * 2., segment }; cairo_set_dash(cr, dashes, G_N_ELEMENTS(dashes), 0.); break; } } line_width = (v & 1) ? heavy_line_width : light_line_width; adjust = (line_width & 1) ? .5 : 0.; cairo_set_line_width(cr, line_width); cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); if (v & 2) { cairo_move_to(cr, xcenter + adjust, y); cairo_line_to(cr, xcenter + adjust, y + row_height); } else { cairo_move_to(cr, x, ycenter + adjust); cairo_line_to(cr, x + width, ycenter + adjust); } cairo_stroke(cr); break; } case 0x256d: /* box drawings light arc down and right */ case 0x256e: /* box drawings light arc down and left */ case 0x256f: /* box drawings light arc up and left */ case 0x2570: /* box drawings light arc up and right */ { const guint v = c - 0x256d; int line_width; int radius; cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); line_width = light_line_width; adjust = (line_width & 1) ? .5 : 0.; cairo_set_line_width(cr, line_width); radius = (column_width + 2) / 3; radius = MAX(radius, heavy_line_width); if (v & 2) { cairo_move_to(cr, xcenter + adjust, y); cairo_line_to(cr, xcenter + adjust, ycenter - radius + 2 * adjust); } else { cairo_move_to(cr, xcenter + adjust, ybottom); cairo_line_to(cr, xcenter + adjust, ycenter + radius); } cairo_stroke(cr); cairo_arc(cr, (v == 1 || v == 2) ? xcenter - radius + 2 * adjust : xcenter + radius, (v & 2) ? ycenter - radius + 2 * adjust : ycenter + radius, radius - adjust, (v + 2) * M_PI / 2.0, (v + 3) * M_PI / 2.0); cairo_stroke(cr); if (v == 1 || v == 2) { cairo_move_to(cr, xcenter - radius + 2 * adjust, ycenter + adjust); cairo_line_to(cr, x, ycenter + adjust); } else { cairo_move_to(cr, xcenter + radius, ycenter + adjust); cairo_line_to(cr, xright, ycenter + adjust); } cairo_stroke(cr); break; } case 0x2571: /* box drawings light diagonal upper right to lower left */ case 0x2572: /* box drawings light diagonal upper left to lower right */ case 0x2573: /* box drawings light diagonal cross */ { cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); cairo_set_line_width(cr, light_line_width); adjust = light_line_width / 2.; if (c != 0x2571) { cairo_move_to(cr, x + adjust, y + adjust); cairo_line_to(cr, xright - adjust, ybottom - adjust); cairo_stroke(cr); } if (c != 0x2572) { cairo_move_to(cr, xright - adjust, y + adjust); cairo_line_to(cr, x + adjust, ybottom - adjust); cairo_stroke(cr); } break; } /* Block Elements */ case 0x2580: /* upper half block */ cairo_rectangle(cr, x, y, width, upper_half); cairo_fill (cr); break; case 0x2581: /* lower one eighth block */ case 0x2582: /* lower one quarter block */ case 0x2583: /* lower three eighths block */ case 0x2584: /* lower half block */ case 0x2585: /* lower five eighths block */ case 0x2586: /* lower three quarters block */ case 0x2587: /* lower seven eighths block */ { const guint v = c - 0x2580; /* Use the number of eights from the top, so that * U+2584 aligns with U+2596..U+259f. */ const int h = EIGHTS (row_height, 8 - v); cairo_rectangle(cr, x, y + h, width, row_height - h); cairo_fill (cr); break; } case 0x2588: /* full block */ case 0x2589: /* left seven eighths block */ case 0x258a: /* left three quarters block */ case 0x258b: /* left five eighths block */ case 0x258c: /* left half block */ case 0x258d: /* left three eighths block */ case 0x258e: /* left one quarter block */ case 0x258f: /* left one eighth block */ { const guint v = c - 0x2588; /* Use the number of eights from the top, so that * U+258c aligns with U+2596..U+259f. */ const int w = EIGHTS (width, 8 - v); cairo_rectangle(cr, x, y, w, row_height); cairo_fill (cr); break; } case 0x2590: /* right half block */ cairo_rectangle(cr, x + left_half, y, right_half, row_height); cairo_fill (cr); break; case 0x2591: /* light shade */ case 0x2592: /* medium shade */ case 0x2593: /* dark shade */ cairo_set_source_rgba (cr, fg->red / 65535., fg->green / 65535., fg->blue / 65535., (c - 0x2590) / 4.); cairo_rectangle(cr, x, y, width, row_height); cairo_fill (cr); break; case 0x2594: /* upper one eighth block */ { const int h = EIGHTS (row_height, 1); /* Align with U+2587 */ cairo_rectangle(cr, x, y, width, h); cairo_fill (cr); break; } case 0x2595: /* right one eighth block */ { const int w = EIGHTS (width, 7); /* Align with U+2589 */ cairo_rectangle(cr, x + w, y, width - w, row_height); cairo_fill (cr); break; } case 0x2596: /* quadrant lower left */ cairo_rectangle(cr, x, y + upper_half, left_half, lower_half); cairo_fill (cr); break; case 0x2597: /* quadrant lower right */ cairo_rectangle(cr, x + left_half, y + upper_half, right_half, lower_half); cairo_fill (cr); break; case 0x2598: /* quadrant upper left */ cairo_rectangle(cr, x, y, left_half, upper_half); cairo_fill (cr); break; case 0x2599: /* quadrant upper left and lower left and lower right */ cairo_rectangle(cr, x, y, left_half, upper_half); cairo_rectangle(cr, x, y + upper_half, left_half, lower_half); cairo_rectangle(cr, x + left_half, y + upper_half, right_half, lower_half); cairo_fill (cr); break; case 0x259a: /* quadrant upper left and lower right */ cairo_rectangle(cr, x, y, left_half, upper_half); cairo_rectangle(cr, x + left_half, y + upper_half, right_half, lower_half); cairo_fill (cr); break; case 0x259b: /* quadrant upper left and upper right and lower left */ cairo_rectangle(cr, x, y, left_half, upper_half); cairo_rectangle(cr, x + left_half, y, right_half, upper_half); cairo_rectangle(cr, x, y + upper_half, left_half, lower_half); cairo_fill (cr); break; case 0x259c: /* quadrant upper left and upper right and lower right */ cairo_rectangle(cr, x, y, left_half, upper_half); cairo_rectangle(cr, x + left_half, y, right_half, upper_half); cairo_rectangle(cr, x + left_half, y + upper_half, right_half, lower_half); cairo_fill (cr); break; case 0x259d: /* quadrant upper right */ cairo_rectangle(cr, x + left_half, y, right_half, upper_half); cairo_fill (cr); break; case 0x259e: /* quadrant upper right and lower left */ cairo_rectangle(cr, x + left_half, y, right_half, upper_half); cairo_rectangle(cr, x, y + upper_half, left_half, lower_half); cairo_fill (cr); break; case 0x259f: /* quadrant upper right and lower left and lower right */ cairo_rectangle(cr, x + left_half, y, right_half, upper_half); cairo_rectangle(cr, x, y + upper_half, left_half, lower_half); cairo_rectangle(cr, x + left_half, y + upper_half, right_half, lower_half); cairo_fill (cr); break; default: g_assert_not_reached(); } #undef EIGHTS cairo_restore(cr); } static void _vte_draw_text_internal (struct _vte_draw *draw, struct _vte_draw_text_request *requests, gsize n_requests, const PangoColor *color, guchar alpha, guint style) { gsize i; cairo_scaled_font_t *last_scaled_font = NULL; int n_cr_glyphs = 0; cairo_glyph_t cr_glyphs[MAX_RUN_LENGTH]; struct font_info *font = draw->fonts[style]; g_return_if_fail (font != NULL); g_assert(draw->cr); _vte_draw_set_source_color_alpha (draw, color, alpha); cairo_set_operator (draw->cr, CAIRO_OPERATOR_OVER); for (i = 0; i < n_requests; i++) { vteunistr c = requests[i].c; int x = requests[i].x; int y = requests[i].y + font->ascent; struct unistr_info *uinfo = font_info_get_unistr_info (font, c); union unistr_font_info *ufi = &uinfo->ufi; if (_vte_draw_unichar_is_local_graphic(c)) { _vte_draw_terminal_draw_graphic(draw, c, color, requests[i].x, requests[i].y, font->width, requests[i].columns, font->height); continue; } switch (uinfo->coverage) { default: case COVERAGE_UNKNOWN: g_assert_not_reached (); break; case COVERAGE_USE_PANGO_LAYOUT_LINE: cairo_move_to (draw->cr, x, y); pango_cairo_show_layout_line (draw->cr, ufi->using_pango_layout_line.line); break; case COVERAGE_USE_PANGO_GLYPH_STRING: cairo_move_to (draw->cr, x, y); pango_cairo_show_glyph_string (draw->cr, ufi->using_pango_glyph_string.font, ufi->using_pango_glyph_string.glyph_string); break; case COVERAGE_USE_CAIRO_GLYPH: if (last_scaled_font != ufi->using_cairo_glyph.scaled_font || n_cr_glyphs == MAX_RUN_LENGTH) { if (n_cr_glyphs) { cairo_set_scaled_font (draw->cr, last_scaled_font); cairo_show_glyphs (draw->cr, cr_glyphs, n_cr_glyphs); n_cr_glyphs = 0; } last_scaled_font = ufi->using_cairo_glyph.scaled_font; } cr_glyphs[n_cr_glyphs].index = ufi->using_cairo_glyph.glyph_index; cr_glyphs[n_cr_glyphs].x = x; cr_glyphs[n_cr_glyphs].y = y; n_cr_glyphs++; break; } } if (n_cr_glyphs) { cairo_set_scaled_font (draw->cr, last_scaled_font); cairo_show_glyphs (draw->cr, cr_glyphs, n_cr_glyphs); n_cr_glyphs = 0; } } void _vte_draw_text (struct _vte_draw *draw, struct _vte_draw_text_request *requests, gsize n_requests, const PangoColor *color, guchar alpha, guint style) { g_assert(draw->cr); if (_vte_debug_on (VTE_DEBUG_DRAW)) { GString *string = g_string_new (""); gchar *str; gsize n; for (n = 0; n < n_requests; n++) { g_string_append_unichar (string, requests[n].c); } str = g_string_free (string, FALSE); g_printerr ("draw_text (\"%s\", len=%" G_GSIZE_FORMAT ", color=(%d,%d,%d,%d), %s - %s)\n", str, n_requests, color->red, color->green, color->blue, alpha, (style & VTE_DRAW_BOLD) ? "bold" : "normal", (style & VTE_DRAW_ITALIC) ? "italic" : "regular"); g_free (str); } _vte_draw_text_internal (draw, requests, n_requests, color, alpha, style); /* handle fonts that lack a bold face by double-striking */ if ((style & VTE_DRAW_BOLD) && !_vte_draw_has_bold (draw, style)) { gsize i; /* Take a step to the right. */ for (i = 0; i < n_requests; i++) { requests[i].x++; } _vte_draw_text_internal (draw, requests, n_requests, color, alpha, style); /* Now take a step back. */ for (i = 0; i < n_requests; i++) { requests[i].x--; } } } /* The following two functions are unused since commit 154abade902850afb44115cccf8fcac51fc082f0, * but let's keep them for now since they may become used again. */ gboolean _vte_draw_has_char (struct _vte_draw *draw, vteunistr c, guint style) { struct unistr_info *uinfo; _vte_debug_print (VTE_DEBUG_DRAW, "draw_has_char ('0x%04X', %s - %s)\n", c, (style & VTE_DRAW_BOLD) ? "bold" : "normal", (style & VTE_DRAW_ITALIC) ? "italic" : "regular"); g_return_val_if_fail (draw->fonts[VTE_DRAW_NORMAL] != NULL, FALSE); uinfo = font_info_get_unistr_info (draw->fonts[style], c); return !uinfo->has_unknown_chars; } gboolean _vte_draw_char (struct _vte_draw *draw, struct _vte_draw_text_request *request, const PangoColor *color, guchar alpha, guint style) { gboolean has_char; _vte_debug_print (VTE_DEBUG_DRAW, "draw_char ('%c', color=(%d,%d,%d,%d), %s, %s)\n", request->c, color->red, color->green, color->blue, alpha, (style & VTE_DRAW_BOLD) ? "bold" : "normal", (style & VTE_DRAW_ITALIC) ? "italic" : "regular"); has_char =_vte_draw_has_char (draw, request->c, style); if (has_char) _vte_draw_text (draw, request, 1, color, alpha, style); return has_char; } void _vte_draw_draw_rectangle (struct _vte_draw *draw, gint x, gint y, gint width, gint height, const PangoColor *color, guchar alpha) { g_assert(draw->cr); _vte_debug_print (VTE_DEBUG_DRAW, "draw_rectangle (%d, %d, %d, %d, color=(%d,%d,%d,%d))\n", x,y,width,height, color->red, color->green, color->blue, alpha); cairo_set_operator (draw->cr, CAIRO_OPERATOR_OVER); cairo_rectangle (draw->cr, x+VTE_LINE_WIDTH/2., y+VTE_LINE_WIDTH/2., width-VTE_LINE_WIDTH, height-VTE_LINE_WIDTH); _vte_draw_set_source_color_alpha (draw, color, alpha); cairo_set_line_width (draw->cr, VTE_LINE_WIDTH); cairo_stroke (draw->cr); } void _vte_draw_fill_rectangle (struct _vte_draw *draw, gint x, gint y, gint width, gint height, const PangoColor *color, guchar alpha) { g_assert(draw->cr); _vte_debug_print (VTE_DEBUG_DRAW, "draw_fill_rectangle (%d, %d, %d, %d, color=(%d,%d,%d,%d))\n", x,y,width,height, color->red, color->green, color->blue, alpha); cairo_set_operator (draw->cr, CAIRO_OPERATOR_OVER); cairo_rectangle (draw->cr, x, y, width, height); _vte_draw_set_source_color_alpha (draw, color, alpha); cairo_fill (draw->cr); }