diff options
author | Kristian Høgsberg <krh@bitplanet.net> | 2010-01-06 10:54:16 -0500 |
---|---|---|
committer | Kristian Høgsberg <krh@bitplanet.net> | 2010-01-06 10:54:16 -0500 |
commit | 3b5c1662f9215d66a56506a3858b14ed03d5061b (patch) | |
tree | aa97a78fa9569c887bc7a70772d66af1483c482d | |
parent | 422519de06c76c89a6df5cea7c344a1d32c4b856 (diff) | |
parent | 021e4a5082c46ffdf6998beaacca948749079b15 (diff) |
Merge branch 'backend-cleanup'
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/vte.c | 42 | ||||
-rw-r--r-- | src/vtedraw.c | 1168 | ||||
-rw-r--r-- | src/vtedraw.h | 68 | ||||
-rw-r--r-- | src/vtepangocairo.c | 1143 | ||||
-rw-r--r-- | src/vtepangocairo.h | 33 | ||||
-rw-r--r-- | src/vteskel.c | 86 | ||||
-rw-r--r-- | src/vteskel.h | 33 |
8 files changed, 995 insertions, 1582 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 453f8d9..62a9a71 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -65,16 +65,12 @@ libvte_la_SOURCES = \ vtedraw.c \ vtedraw.h \ vteint.h \ - vtepangocairo.c \ - vtepangocairo.h \ vteregex.c \ vteregex.h \ vterowdata.c \ vterowdata.h \ vteseq.c \ vteseq-list.h \ - vteskel.c \ - vteskel.h \ vtestream.c \ vtestream.h \ vtestream-base.h \ @@ -8515,9 +8515,8 @@ vte_terminal_realize(GtkWidget *widget) attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; - attributes.visual = _vte_draw_get_visual(terminal->pvt->draw); - attributes.colormap = _vte_draw_get_colormap(terminal->pvt->draw, - FALSE); + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK | GDK_VISIBILITY_NOTIFY_MASK | @@ -8728,12 +8727,8 @@ vte_terminal_fill_rectangle(VteTerminal *terminal, gint height) { GdkColor color; - gboolean wasdrawing; - wasdrawing = terminal->pvt->draw->started; - if (!wasdrawing) { - _vte_draw_start(terminal->pvt->draw); - } + _vte_draw_start(terminal->pvt->draw); color.red = entry->red; color.green = entry->green; color.blue = entry->blue; @@ -8742,9 +8737,7 @@ vte_terminal_fill_rectangle(VteTerminal *terminal, y + terminal->pvt->inner_border.top, width, height, &color, VTE_DRAW_OPAQUE); - if (!wasdrawing) { - _vte_draw_end(terminal->pvt->draw); - } + _vte_draw_end(terminal->pvt->draw); } static void @@ -12505,9 +12498,9 @@ vte_terminal_im_append_menuitems(VteTerminal *terminal, GtkMenuShell *menushell) static gboolean vte_terminal_background_update(VteTerminal *terminal) { - GdkColormap *colormap; GdkColor bgcolor; double saturation; + struct vte_palette_entry *entry; /* If we're not realized yet, don't worry about it, because we get * called when we realize. */ @@ -12521,23 +12514,16 @@ vte_terminal_background_update(VteTerminal *terminal) _vte_debug_print(VTE_DEBUG_MISC|VTE_DEBUG_EVENTS, "Updating background image.\n"); - /* Set the default background color. */ - bgcolor.red = terminal->pvt->palette[VTE_DEF_BG].red; - bgcolor.green = terminal->pvt->palette[VTE_DEF_BG].green; - bgcolor.blue = terminal->pvt->palette[VTE_DEF_BG].blue; - bgcolor.pixel = 0; - gtk_widget_ensure_style(&terminal->widget); - /*colormap = gdk_gc_get_colormap(terminal->widget.style->fg_gc[GTK_WIDGET_STATE(terminal)]);*/ - colormap = gtk_widget_get_colormap (&terminal->widget); - if (colormap) { - gdk_rgb_find_color(colormap, &bgcolor); - } + entry = &terminal->pvt->palette[VTE_DEF_BG]; _vte_debug_print(VTE_DEBUG_MISC, - "Setting background color to (%d, %d, %d) cmap index=%d.\n", - bgcolor.red, bgcolor.green, bgcolor.blue, - bgcolor.pixel); - gdk_window_set_background(terminal->widget.window, &bgcolor); - _vte_draw_set_background_solid (terminal->pvt->draw, &bgcolor, terminal->pvt->bg_opacity); + "Setting background color to (%d, %d, %d).\n", + bgcolor.red, bgcolor.green, bgcolor.blue); + + _vte_draw_set_background_solid (terminal->pvt->draw, + entry->red / 65535., + entry->green / 65535., + entry->blue / 65535., + terminal->pvt->bg_opacity / 65535.); /* If we're transparent, and either have no root image or are being * told to update it, get a new copy of the root window. */ diff --git a/src/vtedraw.c b/src/vtedraw.c index 3ff3ec5..f796b3c 100644 --- a/src/vtedraw.c +++ b/src/vtedraw.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003 Red Hat, Inc. + * Copyright (C) 2003,2008 Red Hat, Inc. * * This is free software; you can redistribute it and/or modify it under * the terms of the GNU Library General Public License as published by @@ -16,172 +16,820 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* The interfaces in this file are subject to change at any time. */ - #include <config.h> -#include <stdio.h> -#include <stdlib.h> +#include <sys/param.h> #include <string.h> -#include <glib.h> #include <gtk/gtk.h> +#include <glib.h> #include "debug.h" +#include "vtebg.h" #include "vtedraw.h" -#include "vtepangocairo.h" -#include "vteskel.h" +#include "vte-private.h" + +#include <pango/pangocairo.h> + + +/* 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 draw as our private data. 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 by 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 exception case that a single vteunistr uses more than one font + * to be drawn. This is not expected to happen, but exists for + * completeness, to make sure we can deal with any junk pango decides to + * throw at us. + * + * + * 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. + * + * Since we use gdk timeout to schedule the delayed destruction, we also + * add a gtk quit handler which is run when the innermost main loop exits + * to cleanup any pending delayed destructions. + * + * + * 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 + -static const struct _vte_draw_impl -*_vte_draw_impls[] = { - &_vte_draw_pangocairo, +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 */ }; -static gboolean -_vte_draw_init_user (struct _vte_draw *draw) +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) { - const gchar *env; - gchar **strv, **s; - guint i; - gboolean success = TRUE; + return g_slice_new0 (struct unistr_info); +} - env = g_getenv ("VTE_BACKEND"); - if (!env) { - return FALSE; +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; } +} - strv = g_strsplit (env, ":;, \t", -1); - for (s = strv; *s; s++) { - char *p; +static void +unistr_info_destroy (struct unistr_info *uinfo) +{ + unistr_info_finish (uinfo); + g_slice_free (struct unistr_info, uinfo); +} - /* lower it */ - for (p = *s; *p; p++) - *p = g_ascii_tolower (*p); +struct font_info { + /* lifecycle */ + int ref_count; + guint destroy_timeout; /* only used when ref_count == 0 */ - /* match null draw */ - if (strcmp (*s, _vte_draw_skel.name) == 0) { - draw->impl = &_vte_draw_skel; - goto out; - } + /* reusable layout set with font and everything set */ + PangoLayout *layout; - /* list available draws */ - if (strcmp (*s, "list") == 0) { - for (i = 0; i < G_N_ELEMENTS (_vte_draw_impls); i++) { - g_printerr ("vte backend: %s\n", _vte_draw_impls[i]->name); - } + /* 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 = 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 = 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; - } - /* find among available draws */ - for (i = 0; i < G_N_ELEMENTS (_vte_draw_impls); i++) { - if (strcmp (*s, _vte_draw_impls[i]->name) == 0) { - if (_vte_draw_impls[i]->check == NULL || - _vte_draw_impls[i]->check (draw, draw->widget)) { - draw->impl = _vte_draw_impls[i]; - goto out; - } - } - } + 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))); + 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; + + 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); + 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); } - success = FALSE; -out: - g_strfreev (strv); - return success; + g_slice_free (struct font_info, info); } +static GHashTable *font_info_for_context; +static guint quit_id; + static gboolean -_vte_draw_init_default (struct _vte_draw *draw) +cleanup_delayed_font_info_destroys_predicate (PangoContext *context, + struct font_info *info) { - guint i; + if (info->destroy_timeout) { + g_source_remove (info->destroy_timeout); + info->destroy_timeout = 0; - for (i = 0; i < G_N_ELEMENTS (_vte_draw_impls); i++) { - if (_vte_draw_impls[i]->check == NULL || - _vte_draw_impls[i]->check (draw, draw->widget)) { - draw->impl = _vte_draw_impls[i]; - return TRUE; - } + font_info_free (info); + return TRUE; } return FALSE; } +static gboolean +cleanup_delayed_font_info_destroys (void) +{ + g_hash_table_foreach_remove (font_info_for_context, + (GHRFunc) cleanup_delayed_font_info_destroys_predicate, + NULL); -struct _vte_draw * -_vte_draw_new (GtkWidget *widget) + quit_id = 0; + return 0; +} + +static void +ensure_quit_handler (void) { - struct _vte_draw *draw; + if (G_UNLIKELY (quit_id == 0)) + quit_id = gtk_quit_add (1, + (GtkFunction) cleanup_delayed_font_info_destroys, + NULL); +} - /* Create the structure. */ - draw = g_slice_new0 (struct _vte_draw); - draw->widget = g_object_ref (widget); +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; +} - /* Allow the user to specify her preferred backends */ - if (!_vte_draw_init_user (draw) && - /* Otherwise use the first thing that works */ - !_vte_draw_init_default (draw)) { - /* Something has to work. */ - g_assert_not_reached (); - draw->impl = &_vte_draw_skel; +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; } - draw->requires_clear = draw->impl->always_requires_clear; + info->ref_count++; - _vte_debug_print (VTE_DEBUG_DRAW, - "draw_new (%s)\n", draw->impl->name); - _vte_debug_print (VTE_DEBUG_MISC, "Using %s.\n", draw->impl->name); + return info; +} - if (draw->impl->create) - draw->impl->create (draw, draw->widget); +static gboolean +font_info_destroy_delayed (struct font_info *info) +{ + info->destroy_timeout = 0; - return draw; + font_info_unregister (info); + font_info_free (info); + + return FALSE; } -void -_vte_draw_free (struct _vte_draw *draw) +static void +font_info_destroy (struct font_info *info) { - _vte_debug_print (VTE_DEBUG_DRAW, "draw_free\n"); + if (!info) + return; - if (draw->impl->destroy) - draw->impl->destroy (draw); + g_return_if_fail (info->ref_count > 0); - if (draw->widget != NULL) { - g_object_unref (draw->widget); + info->ref_count--; + if (info->ref_count) + return; + + /* Delay destruction by a few seconds, in case we need it again */ + ensure_quit_handler (); + 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_cairo_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_cairo_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_cairo_get_fontconfig_timestamp (context); +} + +static gboolean +context_equal (PangoContext *a, + PangoContext *b) +{ + return 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_cairo_get_fontconfig_timestamp (a) == vte_pango_cairo_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 = 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); + return font_info_reference (info); } - g_slice_free (struct _vte_draw, draw); + info = font_info_allocate (context); + info->ref_count = 1; + font_info_register (info); + + g_object_unref (context); + + return info; } -GdkVisual * -_vte_draw_get_visual (struct _vte_draw *draw) +/* assumes ownership/reference of context */ +static struct font_info * +font_info_create_for_context (PangoContext *context, + const PangoFontDescription *desc, + VteTerminalAntiAlias antialias, + PangoLanguage *language, + guint fontconfig_timestamp) { - GdkVisual *visual = NULL; + 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_cairo_set_fontconfig_timestamp (context, fontconfig_timestamp); - _vte_debug_print (VTE_DEBUG_DRAW, "draw_get_visual\n"); + pango_context_set_base_dir (context, PANGO_DIRECTION_LTR); - if (draw->impl->get_visual) - visual = draw->impl->get_visual (draw); + if (desc) + pango_context_set_font_description (context, desc); - return visual ? visual : gtk_widget_get_visual (draw->widget); + pango_context_set_language (context, language); + + switch (antialias) { + cairo_font_options_t *font_options; + cairo_antialias_t cr_aa; + + case VTE_ANTI_ALIAS_FORCE_ENABLE: + case VTE_ANTI_ALIAS_FORCE_DISABLE: + + if (antialias == VTE_ANTI_ALIAS_FORCE_ENABLE) + cr_aa = CAIRO_ANTIALIAS_DEFAULT; /* let surface decide between gray and subpixel */ + else + cr_aa = CAIRO_ANTIALIAS_NONE; + + font_options = cairo_font_options_copy (pango_cairo_context_get_font_options (context)); + cairo_font_options_set_antialias (font_options, cr_aa); + pango_cairo_context_set_font_options (context, font_options); + cairo_font_options_destroy (font_options); + + break; + + default: + case VTE_ANTI_ALIAS_USE_DEFAULT: + /* 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)) { + font_options = cairo_font_options_create (); + pango_cairo_context_set_font_options (context, font_options); + cairo_font_options_destroy (font_options); + } + break; + } + + return font_info_find_for_context (context); } -GdkColormap * -_vte_draw_get_colormap (struct _vte_draw *draw, gboolean maybe_use_default) +static struct font_info * +font_info_create_for_screen (GdkScreen *screen, + const PangoFontDescription *desc, + VteTerminalAntiAlias antialias, + PangoLanguage *language) { - GdkColormap *colormap; + 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, antialias, language, fontconfig_timestamp); +} - _vte_debug_print (VTE_DEBUG_DRAW, "draw_get_colormap\n"); +static struct font_info * +font_info_create_for_widget (GtkWidget *widget, + const PangoFontDescription *desc, + VteTerminalAntiAlias antialias) +{ + GdkScreen *screen = gtk_widget_get_screen (widget); + PangoLanguage *language = pango_context_get_language (gtk_widget_get_pango_context (widget)); - if (draw->impl->get_colormap) - colormap = draw->impl->get_colormap (draw); - else - colormap = gtk_widget_get_colormap (draw->widget); + return font_info_create_for_screen (screen, desc, antialias, language); +} - if (colormap == NULL && maybe_use_default) { - colormap = gdk_screen_get_default_colormap (gtk_widget_get_screen (draw->widget)); +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 = g_object_ref (info->layout); + + } else { + PangoGlyphItem *glyph_item = 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 ? g_object_ref (pango_font) : NULL; + ufi->using_pango_glyph_string.glyph_string = pango_glyph_string_copy (glyph_string); + } } - return colormap; + /* 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; +} + +struct _vte_draw { + GtkWidget *widget; + + gint started; + + gboolean requires_clear; + + struct font_info *font; + struct font_info *font_bold; + cairo_pattern_t *bg_pattern; + + cairo_t *cr; +}; + +struct _vte_draw * +_vte_draw_new (GtkWidget *widget) +{ + struct _vte_draw *draw; + + /* Create the structure. */ + draw = g_slice_new0 (struct _vte_draw); + draw->widget = g_object_ref (widget); + draw->requires_clear = FALSE; + + _vte_debug_print (VTE_DEBUG_DRAW, "draw_new\n"); + + return draw; +} + +void +_vte_draw_free (struct _vte_draw *draw) +{ + _vte_debug_print (VTE_DEBUG_DRAW, "draw_free\n"); + + if (draw->bg_pattern != NULL) { + cairo_pattern_destroy (draw->bg_pattern); + draw->bg_pattern = NULL; + } + + if (draw->font != NULL) { + font_info_destroy (draw->font); + draw->font = NULL; + } + + if (draw->widget != NULL) { + g_object_unref (draw->widget); + } + + g_slice_free (struct _vte_draw, draw); } void @@ -193,36 +841,45 @@ _vte_draw_start (struct _vte_draw *draw) g_object_ref (draw->widget->window); - if (draw->impl->start) - draw->impl->start (draw); + if (draw->started == 0) + draw->cr = gdk_cairo_create (draw->widget->window); - draw->started = TRUE; + draw->started++; } void _vte_draw_end (struct _vte_draw *draw) { g_return_if_fail (draw->started == TRUE); + g_assert (draw->started > 0); - if (draw->impl->end) - draw->impl->end (draw); + draw->started--; + if (draw->started == 0) { + cairo_destroy (draw->cr); + draw->cr = NULL; + } g_object_unref (draw->widget->window); - draw->started = FALSE; - _vte_debug_print (VTE_DEBUG_DRAW, "draw_end\n"); } void _vte_draw_set_background_solid(struct _vte_draw *draw, - GdkColor *color, - guint16 opacity) + double red, + double green, + double blue, + double opacity) { - draw->requires_clear = draw->impl->always_requires_clear || opacity != 0xFFFF; + draw->requires_clear = opacity != 0xFFFF; - if (draw->impl->set_background_solid) - draw->impl->set_background_solid (draw, color, opacity); + if (draw->bg_pattern) + cairo_pattern_destroy (draw->bg_pattern); + + draw->bg_pattern = cairo_pattern_create_rgba (red, + green, + blue, + opacity); } void @@ -233,97 +890,223 @@ _vte_draw_set_background_image (struct _vte_draw *draw, const GdkColor *color, double saturation) { + GdkPixmap *pixmap; + cairo_surface_t *surface; + cairo_t *cr; + if (type != VTE_BG_SOURCE_NONE) draw->requires_clear = TRUE; - if (draw->impl->set_background_image) - draw->impl->set_background_image (draw, type, pixbuf, filename, - color, saturation); + pixmap = vte_bg_get_pixmap (vte_bg_get_for_screen (gtk_widget_get_screen (draw->widget)), + type, pixbuf, filename, + color, saturation, + gtk_widget_get_colormap (draw->widget)); + + if (!pixmap) + return; + + if (draw->bg_pattern) + cairo_pattern_destroy (draw->bg_pattern); + + /* Ugh... We need to create a dummy cairo_t */ + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); + cr = cairo_create (surface); + + gdk_cairo_set_source_pixmap (cr, pixmap, 0, 0); + draw->bg_pattern = cairo_pattern_reference (cairo_get_source (cr)); + + cairo_destroy (cr); + cairo_surface_destroy (surface); + + /* Transfer the pixmap ownership to the pattern */ + cairo_pattern_set_user_data (draw->bg_pattern, + (cairo_user_data_key_t *) draw, + pixmap, + (cairo_destroy_func_t) g_object_unref); + + cairo_pattern_set_extend (draw->bg_pattern, CAIRO_EXTEND_REPEAT); } void _vte_draw_set_background_scroll (struct _vte_draw *draw, gint x, gint y) { + cairo_matrix_t matrix; + _vte_debug_print (VTE_DEBUG_DRAW, "draw_set_scroll (%d, %d)\n", x, y); - if (draw->impl->set_background_scroll) - draw->impl->set_background_scroll (draw, x, y); -} + g_return_if_fail (draw->bg_pattern != NULL); -gboolean -_vte_draw_requires_clear (struct _vte_draw *draw) -{ - return draw->requires_clear; + cairo_matrix_init_translate (&matrix, x, y); + cairo_pattern_set_matrix (draw->bg_pattern, &matrix); } gboolean _vte_draw_clip (struct _vte_draw *draw, GdkRegion *region) { - gboolean clip = FALSE; _vte_debug_print (VTE_DEBUG_DRAW, "draw_clip\n"); + gdk_cairo_region(draw->cr, region); + cairo_clip (draw->cr); - if (draw->impl->clip) { - draw->impl->clip (draw, region); - clip = TRUE; - } - - return clip; + return TRUE; } void _vte_draw_clear (struct _vte_draw *draw, gint x, gint y, gint width, gint height) { - g_return_if_fail (draw->impl->clear != NULL); + 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); - draw->impl->clear (draw, x, y, width, height); + 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, const PangoFontDescription *fontdesc, - VteTerminalAntiAlias anti_alias) + VteTerminalAntiAlias antialias) { - _vte_debug_print (VTE_DEBUG_DRAW, "draw_set_text_font (aa=%d)\n", - anti_alias); + PangoFontDescription *bolddesc = NULL; - if (draw->impl->set_text_font) - draw->impl->set_text_font (draw, fontdesc, anti_alias); + _vte_debug_print (VTE_DEBUG_DRAW, "draw_set_text_font (aa=%d)\n", + antialias); + + if (draw->font_bold != draw->font) + font_info_destroy (draw->font_bold); + font_info_destroy (draw->font); + draw->font = font_info_create_for_widget (draw->widget, fontdesc, antialias); + + /* calculate bold font desc */ + bolddesc = pango_font_description_copy (fontdesc); + pango_font_description_set_weight (bolddesc, PANGO_WEIGHT_BOLD); + + draw->font_bold = font_info_create_for_widget (draw->widget, bolddesc, antialias); + pango_font_description_free (bolddesc); + + /* 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 + */ + if ( abs((draw->font_bold->width * 100 / draw->font->width) - 100) > 10 ) { + font_info_destroy (draw->font_bold); + draw->font_bold = draw->font; + } } void _vte_draw_get_text_metrics(struct _vte_draw *draw, gint *width, gint *height, gint *ascent) { - gint swidth = 0, sheight = 0, sascent = 0; - - g_return_if_fail (draw->impl->get_text_metrics != NULL); - - draw->impl->get_text_metrics (draw, &swidth, &sheight, &sascent); - - if (width) *width = swidth; - if (height) *height = sheight; - if (ascent) *ascent = sascent; + g_return_if_fail (draw->font != NULL); + + if (width) + *width = draw->font->width; + if (height) + *height = draw->font->height; + if (ascent) + *ascent = draw->font->ascent; } + int _vte_draw_get_char_width (struct _vte_draw *draw, vteunistr c, int columns, gboolean bold) { - int width = 0; + struct unistr_info *uinfo; - if (draw->impl->get_char_width) - width = draw->impl->get_char_width (draw, c, columns, bold); + g_return_val_if_fail (draw->font != NULL, 0); - if (width == 0) - _vte_draw_get_text_metrics (draw, &width, NULL, NULL); + uinfo = font_info_get_unistr_info (bold ? draw->font_bold : draw->font, c); + return uinfo->width; +} + +static gboolean +_vte_pangocairo_has_bold (struct _vte_draw *draw) +{ + return (draw->font != draw->font_bold); +} - return width; +static void +set_source_color_alpha (cairo_t *cr, + const GdkColor *color, + guchar alpha) +{ + cairo_set_source_rgba (cr, + color->red / 65535., + color->green / 65535., + color->blue / 65535., + alpha / 255.); +} + +static void +_vte_pangocairo_draw_text (struct _vte_draw *draw, + struct _vte_draw_text_request *requests, gsize n_requests, + GdkColor *color, guchar alpha, gboolean bold) +{ + 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 = bold ? draw->font_bold : draw->font; + + g_return_if_fail (font != NULL); + + set_source_color_alpha (draw->cr, 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; + + 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 @@ -332,7 +1115,6 @@ _vte_draw_text (struct _vte_draw *draw, GdkColor *color, guchar alpha, gboolean bold) { g_return_if_fail (draw->started == TRUE); - g_return_if_fail (draw->impl->draw_text != NULL); if (_vte_debug_on (VTE_DEBUG_DRAW)) { GString *string = g_string_new (""); @@ -348,17 +1130,19 @@ _vte_draw_text (struct _vte_draw *draw, g_free (str); } - draw->impl->draw_text (draw, requests, n_requests, color, alpha, bold); + _vte_pangocairo_draw_text (draw, requests, + n_requests, color, alpha, bold); /* handle fonts that lack a bold face by double-striking */ - if (bold && !(draw->impl->has_bold && draw->impl->has_bold (draw))) { + if (bold && !_vte_pangocairo_has_bold (draw)) { gsize i; /* Take a step to the right. */ for (i = 0; i < n_requests; i++) { requests[i].x++; } - draw->impl->draw_text (draw, requests, n_requests, color, alpha, FALSE); + _vte_pangocairo_draw_text (draw, requests, + n_requests, color, alpha, FALSE); /* Now take a step back. */ for (i = 0; i < n_requests; i++) { requests[i].x--; @@ -367,6 +1151,20 @@ _vte_draw_text (struct _vte_draw *draw, } gboolean +_vte_draw_has_char (struct _vte_draw *draw, vteunistr c, gboolean bold) +{ + struct unistr_info *uinfo; + + _vte_debug_print (VTE_DEBUG_DRAW, "draw_has_char ('0x%04X', %s)\n", c, + bold ? "bold" : "normal"); + + g_return_val_if_fail (draw->font != NULL, FALSE); + + uinfo = font_info_get_unistr_info (bold ? draw->font_bold : draw->font, c); + return !uinfo->has_unknown_chars; +} + +gboolean _vte_draw_char (struct _vte_draw *draw, struct _vte_draw_text_request *request, GdkColor *color, guchar alpha, gboolean bold) @@ -385,60 +1183,48 @@ _vte_draw_char (struct _vte_draw *draw, return has_char; } -gboolean -_vte_draw_has_char (struct _vte_draw *draw, vteunistr c, gboolean bold) -{ - gboolean has_char = TRUE; - - _vte_debug_print (VTE_DEBUG_DRAW, "draw_has_char ('0x%04X', %s)\n", c, - bold ? "bold" : "normal"); - - if (draw->impl->has_char) - has_char = draw->impl->has_char (draw, c, bold); - - return has_char; -} void -_vte_draw_fill_rectangle (struct _vte_draw *draw, +_vte_draw_draw_rectangle (struct _vte_draw *draw, gint x, gint y, gint width, gint height, GdkColor *color, guchar alpha) { g_return_if_fail (draw->started == TRUE); - g_return_if_fail (draw->impl->fill_rectangle != NULL); _vte_debug_print (VTE_DEBUG_DRAW, - "draw_fill_rectangle (%d, %d, %d, %d, color=(%d,%d,%d,%d))\n", + "draw_rectangle (%d, %d, %d, %d, color=(%d,%d,%d,%d))\n", x,y,width,height, color->red, color->green, color->blue, alpha); - draw->impl->fill_rectangle (draw, x, y, width, height, color, alpha); + cairo_set_operator (draw->cr, CAIRO_OPERATOR_OVER); + cairo_rectangle (draw->cr, x+.5, y+.5, width-1, height-1); + set_source_color_alpha (draw->cr, color, alpha); + cairo_set_line_width (draw->cr, 1); + cairo_stroke (draw->cr); } void -_vte_draw_draw_rectangle (struct _vte_draw *draw, +_vte_draw_fill_rectangle (struct _vte_draw *draw, gint x, gint y, gint width, gint height, GdkColor *color, guchar alpha) { g_return_if_fail (draw->started == TRUE); _vte_debug_print (VTE_DEBUG_DRAW, - "draw_rectangle (%d, %d, %d, %d, color=(%d,%d,%d,%d))\n", + "draw_fill_rectangle (%d, %d, %d, %d, color=(%d,%d,%d,%d))\n", x,y,width,height, color->red, color->green, color->blue, alpha); - if (draw->impl->draw_rectangle) - draw->impl->draw_rectangle (draw, x, y, width, height, color, alpha); - else { - if (width > 0) { - _vte_draw_fill_rectangle (draw, x, y, width-1, 1, color, alpha); - _vte_draw_fill_rectangle (draw, x+1, y+height-1, width-1, 1, color, alpha); - } - if (height > 0) { - _vte_draw_fill_rectangle (draw, x, y+1, 1, height-1, color, alpha); - _vte_draw_fill_rectangle (draw, x+width-1, y, 1, height-1, color, alpha); - } - } + cairo_set_operator (draw->cr, CAIRO_OPERATOR_OVER); + cairo_rectangle (draw->cr, x, y, width, height); + set_source_color_alpha (draw->cr, color, alpha); + cairo_fill (draw->cr); +} + +gboolean +_vte_draw_requires_clear (struct _vte_draw *draw) +{ + return draw->requires_clear; } diff --git a/src/vtedraw.h b/src/vtedraw.h index cfdbdef..56ef310 100644 --- a/src/vtedraw.h +++ b/src/vtedraw.h @@ -59,80 +59,20 @@ struct _vte_draw_text_request { gshort x, y, columns; }; -struct _vte_draw_impl { - const char *name; - gboolean (*check)(struct _vte_draw *draw, GtkWidget *widget); - void (*create)(struct _vte_draw *draw, GtkWidget *widget); - void (*destroy)(struct _vte_draw *draw); - GdkVisual* (*get_visual)(struct _vte_draw *draw); - GdkColormap* (*get_colormap)(struct _vte_draw *draw); - void (*start)(struct _vte_draw *draw); - void (*end)(struct _vte_draw *draw); - void (*set_background_solid)(struct _vte_draw *, - GdkColor *color, - guint16 opacity); - void (*set_background_image)(struct _vte_draw *, - enum VteBgSourceType type, - GdkPixbuf *pixbuf, - const char *file, - const GdkColor *color, - double saturation); - void (*set_background_scroll)(struct _vte_draw *, - gint, gint); - void (*clip)(struct _vte_draw *, GdkRegion *); - gboolean always_requires_clear; - void (*clear)(struct _vte_draw *, gint, gint, gint, gint); - void (*set_text_font)(struct _vte_draw *, - const PangoFontDescription *, - VteTerminalAntiAlias); - void (*get_text_metrics)(struct _vte_draw *, gint *, gint *, gint *); - int (*get_char_width)(struct _vte_draw *, vteunistr c, int columns, - gboolean); - gboolean (*has_bold)(struct _vte_draw *); - void (*draw_text)(struct _vte_draw *, - struct _vte_draw_text_request *, gsize, - GdkColor *, guchar, gboolean); - gboolean (*has_char)(struct _vte_draw *, vteunistr, gboolean); - void (*draw_rectangle)(struct _vte_draw *, - gint, gint, gint, gint, - GdkColor *, guchar); - void (*fill_rectangle)(struct _vte_draw *, - gint, gint, gint, gint, - GdkColor *, guchar); -}; - -struct _vte_draw { - GtkWidget *widget; - - gboolean started; - - gboolean requires_clear; - - const struct _vte_draw_impl *impl; - - /* for use by impl */ - gpointer impl_data; -}; - /* Create and destroy a draw structure. */ struct _vte_draw *_vte_draw_new(GtkWidget *widget); void _vte_draw_free(struct _vte_draw *draw); -/* Get the visual and colormap the draw structure desires. Certain draw - implementations may require that this visual/colormap pair be used when - creating a window, and may fail otherwise. */ -GdkVisual *_vte_draw_get_visual(struct _vte_draw *draw); -GdkColormap *_vte_draw_get_colormap(struct _vte_draw *draw, - gboolean maybe_use_default); - /* Begin and end a drawing operation. If anything is buffered locally, it is flushed to the window system when _end() is called. */ void _vte_draw_start(struct _vte_draw *draw); void _vte_draw_end(struct _vte_draw *draw); void _vte_draw_set_background_solid(struct _vte_draw *draw, - GdkColor *color, - guint16 opacity); + double red, + double green, + double blue, + double opacity); void _vte_draw_set_background_image(struct _vte_draw *draw, enum VteBgSourceType type, GdkPixbuf *pixbuf, diff --git a/src/vtepangocairo.c b/src/vtepangocairo.c deleted file mode 100644 index 7532c51..0000000 --- a/src/vtepangocairo.c +++ /dev/null @@ -1,1143 +0,0 @@ -/* - * Copyright (C) 2003,2008 Red Hat, Inc. - * - * This is free software; you can redistribute it and/or modify it under - * the terms of the GNU Library General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 - * General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -#include <config.h> - -#include <sys/param.h> -#include <string.h> -#include <gtk/gtk.h> -#include <glib.h> -#include "debug.h" -#include "vtebg.h" -#include "vtedraw.h" -#include "vtepangocairo.h" -#include "vte-private.h" - -#include <pango/pangocairo.h> - - -/* 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 draw as our private data. 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 by 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 exception case that a single vteunistr uses more than one font - * to be drawn. This is not expected to happen, but exists for - * completeness, to make sure we can deal with any junk pango decides to - * throw at us. - * - * - * 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. - * - * Since we use gdk timeout to schedule the delayed destruction, we also - * add a gtk quit handler which is run when the innermost main loop exits - * to cleanup any pending delayed destructions. - * - * - * 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 = 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 = 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))); - 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; - - 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); - 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 guint quit_id; - -static gboolean -cleanup_delayed_font_info_destroys_predicate (PangoContext *context, - struct font_info *info) -{ - if (info->destroy_timeout) { - g_source_remove (info->destroy_timeout); - info->destroy_timeout = 0; - - font_info_free (info); - return TRUE; - } - - return FALSE; -} - -static gboolean -cleanup_delayed_font_info_destroys (void) -{ - g_hash_table_foreach_remove (font_info_for_context, - (GHRFunc) cleanup_delayed_font_info_destroys_predicate, - NULL); - - quit_id = 0; - return 0; -} - -static void -ensure_quit_handler (void) -{ - if (G_UNLIKELY (quit_id == 0)) - quit_id = gtk_quit_add (1, - (GtkFunction) cleanup_delayed_font_info_destroys, - NULL); -} - -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 */ - ensure_quit_handler (); - 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_cairo_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_cairo_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_cairo_get_fontconfig_timestamp (context); -} - -static gboolean -context_equal (PangoContext *a, - PangoContext *b) -{ - return 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_cairo_get_fontconfig_timestamp (a) == vte_pango_cairo_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 = 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); - return font_info_reference (info); - } - - 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, - VteTerminalAntiAlias antialias, - 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_cairo_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); - - switch (antialias) { - cairo_font_options_t *font_options; - cairo_antialias_t cr_aa; - - case VTE_ANTI_ALIAS_FORCE_ENABLE: - case VTE_ANTI_ALIAS_FORCE_DISABLE: - - if (antialias == VTE_ANTI_ALIAS_FORCE_ENABLE) - cr_aa = CAIRO_ANTIALIAS_DEFAULT; /* let surface decide between gray and subpixel */ - else - cr_aa = CAIRO_ANTIALIAS_NONE; - - font_options = cairo_font_options_copy (pango_cairo_context_get_font_options (context)); - cairo_font_options_set_antialias (font_options, cr_aa); - pango_cairo_context_set_font_options (context, font_options); - cairo_font_options_destroy (font_options); - - break; - - default: - case VTE_ANTI_ALIAS_USE_DEFAULT: - /* 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)) { - font_options = cairo_font_options_create (); - pango_cairo_context_set_font_options (context, font_options); - cairo_font_options_destroy (font_options); - } - break; - } - - return font_info_find_for_context (context); -} - -static struct font_info * -font_info_create_for_screen (GdkScreen *screen, - const PangoFontDescription *desc, - VteTerminalAntiAlias antialias, - 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, antialias, language, fontconfig_timestamp); -} - -static struct font_info * -font_info_create_for_widget (GtkWidget *widget, - const PangoFontDescription *desc, - VteTerminalAntiAlias antialias) -{ - 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, antialias, 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 = g_object_ref (info->layout); - - } else { - PangoGlyphItem *glyph_item = 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 ? 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; -} - - -struct _vte_pangocairo_data { - struct font_info *font; - struct font_info *font_bold; - cairo_pattern_t *bg_pattern; - - cairo_t *cr; -}; - -static void -_vte_pangocairo_create (struct _vte_draw *draw, GtkWidget *widget) -{ - struct _vte_pangocairo_data *data; - - data = g_slice_new0 (struct _vte_pangocairo_data); - draw->impl_data = data; -} - -static void -_vte_pangocairo_destroy (struct _vte_draw *draw) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - - if (data->bg_pattern != NULL) { - cairo_pattern_destroy (data->bg_pattern); - data->bg_pattern = NULL; - } - - if (data->font != NULL) { - font_info_destroy (data->font); - data->font = NULL; - } - - g_slice_free (struct _vte_pangocairo_data, draw->impl_data); - draw->impl_data = NULL; -} - -static void -_vte_pangocairo_start (struct _vte_draw *draw) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - - data->cr = gdk_cairo_create (draw->widget->window); -} - -static void -_vte_pangocairo_end (struct _vte_draw *draw) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - - if (data->cr != NULL) { - cairo_destroy (data->cr); - data->cr = NULL; - } -} - -static void -_vte_pangocairo_set_background_solid(struct _vte_draw *draw, - GdkColor *color, - guint16 opacity) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - - if (data->bg_pattern) - cairo_pattern_destroy (data->bg_pattern); - - data->bg_pattern = cairo_pattern_create_rgba (color->red / 65535., - color->green / 65535., - color->blue / 65535., - opacity / 65535.); -} - -static void -_vte_pangocairo_set_background_image (struct _vte_draw *draw, - enum VteBgSourceType type, - GdkPixbuf *pixbuf, - const char *file, - const GdkColor *color, - double saturation) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - GdkPixmap *pixmap; - cairo_surface_t *surface; - cairo_t *cr; - - pixmap = vte_bg_get_pixmap (vte_bg_get_for_screen (gtk_widget_get_screen (draw->widget)), - type, pixbuf, file, - color, saturation, - _vte_draw_get_colormap(draw, TRUE)); - - if (!pixmap) - return; - - if (data->bg_pattern) - cairo_pattern_destroy (data->bg_pattern); - - /* Ugh... We need to create a dummy cairo_t */ - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); - cr = cairo_create (surface); - - gdk_cairo_set_source_pixmap (cr, pixmap, 0, 0); - data->bg_pattern = cairo_pattern_reference (cairo_get_source (cr)); - - cairo_destroy (cr); - cairo_surface_destroy (surface); - - /* Transfer the pixmap ownership to the pattern */ - cairo_pattern_set_user_data (data->bg_pattern, - (cairo_user_data_key_t *) data, - pixmap, - (cairo_destroy_func_t) g_object_unref); - - cairo_pattern_set_extend (data->bg_pattern, CAIRO_EXTEND_REPEAT); -} - -static void -_vte_pangocairo_set_background_scroll (struct _vte_draw *draw, - gint x, gint y) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - cairo_matrix_t matrix; - - g_return_if_fail (data->bg_pattern != NULL); - - cairo_matrix_init_translate (&matrix, x, y); - cairo_pattern_set_matrix (data->bg_pattern, &matrix); -} - -static void -_vte_pangocairo_clear (struct _vte_draw *draw, - gint x, gint y, gint width, gint height) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - - g_return_if_fail (data->bg_pattern != NULL); - - cairo_rectangle (data->cr, x, y, width, height); - cairo_set_operator (data->cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source (data->cr, data->bg_pattern); - cairo_fill (data->cr); -} - -static void -_vte_pangocairo_clip (struct _vte_draw *draw, - GdkRegion *region) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - - gdk_cairo_region(data->cr, region); - cairo_clip (data->cr); -} - -static void -_vte_pangocairo_set_text_font (struct _vte_draw *draw, - const PangoFontDescription *fontdesc, - VteTerminalAntiAlias antialias) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - PangoFontDescription *bolddesc = NULL; - - if (data->font_bold != data->font) - font_info_destroy (data->font_bold); - font_info_destroy (data->font); - data->font = font_info_create_for_widget (draw->widget, fontdesc, antialias); - - /* calculate bold font desc */ - bolddesc = pango_font_description_copy (fontdesc); - pango_font_description_set_weight (bolddesc, PANGO_WEIGHT_BOLD); - - data->font_bold = font_info_create_for_widget (draw->widget, bolddesc, antialias); - pango_font_description_free (bolddesc); - - /* 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 - */ - if ( abs((data->font_bold->width * 100 / data->font->width) - 100) > 10 ) { - font_info_destroy (data->font_bold); - data->font_bold = data->font; - } -} - -static void -_vte_pangocairo_get_text_metrics(struct _vte_draw *draw, - gint *width, gint *height, gint *ascent) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - - g_return_if_fail (data->font != NULL); - - *width = data->font->width; - *height = data->font->height; - *ascent = data->font->ascent; -} - - -static int -_vte_pangocairo_get_char_width (struct _vte_draw *draw, vteunistr c, int columns, - gboolean bold) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - struct unistr_info *uinfo; - - g_return_val_if_fail (data->font != NULL, 0); - - uinfo = font_info_get_unistr_info (bold ? data->font_bold : data->font, c); - return uinfo->width; -} - -static gboolean -_vte_pangocairo_has_bold (struct _vte_draw *draw) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - - return (data->font != data->font_bold); -} - -static void -set_source_color_alpha (cairo_t *cr, - const GdkColor *color, - guchar alpha) -{ - cairo_set_source_rgba (cr, - color->red / 65535., - color->green / 65535., - color->blue / 65535., - alpha / 255.); -} - -static void -_vte_pangocairo_draw_text (struct _vte_draw *draw, - struct _vte_draw_text_request *requests, gsize n_requests, - GdkColor *color, guchar alpha, gboolean bold) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - 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 = bold ? data->font_bold : data->font; - - g_return_if_fail (font != NULL); - - set_source_color_alpha (data->cr, color, alpha); - cairo_set_operator (data->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; - - switch (uinfo->coverage) { - default: - case COVERAGE_UNKNOWN: - g_assert_not_reached (); - break; - case COVERAGE_USE_PANGO_LAYOUT_LINE: - cairo_move_to (data->cr, x, y); - pango_cairo_show_layout_line (data->cr, - ufi->using_pango_layout_line.line); - break; - case COVERAGE_USE_PANGO_GLYPH_STRING: - cairo_move_to (data->cr, x, y); - pango_cairo_show_glyph_string (data->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 (data->cr, last_scaled_font); - cairo_show_glyphs (data->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 (data->cr, last_scaled_font); - cairo_show_glyphs (data->cr, - cr_glyphs, - n_cr_glyphs); - n_cr_glyphs = 0; - } -} - -static gboolean -_vte_pangocairo_draw_has_char (struct _vte_draw *draw, vteunistr c, - gboolean bold) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - struct unistr_info *uinfo; - - g_return_val_if_fail (data->font != NULL, FALSE); - - uinfo = font_info_get_unistr_info (bold ? data->font_bold : data->font, c); - return !uinfo->has_unknown_chars; -} - -static void -_vte_pangocairo_draw_rectangle (struct _vte_draw *draw, - gint x, gint y, gint width, gint height, - GdkColor *color, guchar alpha) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - - cairo_set_operator (data->cr, CAIRO_OPERATOR_OVER); - cairo_rectangle (data->cr, x+.5, y+.5, width-1, height-1); - set_source_color_alpha (data->cr, color, alpha); - cairo_set_line_width (data->cr, 1); - cairo_stroke (data->cr); -} - -static void -_vte_pangocairo_fill_rectangle (struct _vte_draw *draw, - gint x, gint y, gint width, gint height, - GdkColor *color, guchar alpha) -{ - struct _vte_pangocairo_data *data = draw->impl_data; - - cairo_set_operator (data->cr, CAIRO_OPERATOR_OVER); - cairo_rectangle (data->cr, x, y, width, height); - set_source_color_alpha (data->cr, color, alpha); - cairo_fill (data->cr); -} - -const struct _vte_draw_impl _vte_draw_pangocairo = { - "pangocairo", - NULL, /* check */ - _vte_pangocairo_create, - _vte_pangocairo_destroy, - NULL, /* get_visual */ - NULL, /* get_colormap */ - _vte_pangocairo_start, - _vte_pangocairo_end, - _vte_pangocairo_set_background_solid, - _vte_pangocairo_set_background_image, - _vte_pangocairo_set_background_scroll, - _vte_pangocairo_clip, - FALSE, /* always_requires_clear */ - _vte_pangocairo_clear, - _vte_pangocairo_set_text_font, - _vte_pangocairo_get_text_metrics, - _vte_pangocairo_get_char_width, - _vte_pangocairo_has_bold, - _vte_pangocairo_draw_text, - _vte_pangocairo_draw_has_char, - _vte_pangocairo_draw_rectangle, - _vte_pangocairo_fill_rectangle -}; diff --git a/src/vtepangocairo.h b/src/vtepangocairo.h deleted file mode 100644 index d1abfc7..0000000 --- a/src/vtepangocairo.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2003 Red Hat, Inc. - * - * This is free software; you can redistribute it and/or modify it under - * the terms of the GNU Library General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 - * General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef vte_vtepangocairo_h_included -#define vte_vtepangocairo_h_included - -/* The interfaces in this file are subject to change at any time. */ - - -#include "vtedraw.h" - -G_BEGIN_DECLS - -extern const struct _vte_draw_impl _vte_draw_pangocairo; - -G_END_DECLS - -#endif diff --git a/src/vteskel.c b/src/vteskel.c deleted file mode 100644 index 274b6de..0000000 --- a/src/vteskel.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2003 Red Hat, Inc. - * - * This is free software; you can redistribute it and/or modify it under - * the terms of the GNU Library General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 - * General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -#include <config.h> - -#include <sys/param.h> -#include <stdio.h> -#include <string.h> -#include <gtk/gtk.h> -#include "debug.h" -#include "vtebg.h" -#include "vtedraw.h" - -static void -_vte_skel_clear(struct _vte_draw *draw, - gint x, gint y, gint width, gint height) -{ - g_message ("_vte_skel_clear: %d,%d+%d,%d", - x, y, width, height); -} - -static void -_vte_skel_get_text_metrics(struct _vte_draw *draw, - gint *width, gint *height, gint *ascent) -{ - g_message ("_vte_skel_get_text_metrics"); -} - -static void -_vte_skel_draw_text(struct _vte_draw *draw, - struct _vte_draw_text_request *requests, gsize n_requests, - GdkColor *color, guchar alpha, gboolean bold) -{ - g_message ("_vte_skel_draw_text: %d chars", - n_requests); -} - -static void -_vte_skel_fill_rectangle(struct _vte_draw *draw, - gint x, gint y, gint width, gint height, - GdkColor *color, guchar alpha) -{ - g_message ("_vte_skel_fill_rectangle: %d,%d+%d,%d", - x, y, width, height); -} - -const struct _vte_draw_impl _vte_draw_skel = { - "null", - NULL, /* check */ - NULL, /* create */ - NULL, /* destroy */ - NULL, /* get_visual */ - NULL, /* get_colormap */ - NULL, /* start */ - NULL, /* end */ - NULL, /* set_background_solid */ - NULL, /* set_background_image */ - NULL, /* set_background_scroll */ - NULL, /* clip */ - TRUE, /* always_requires_clear */ - _vte_skel_clear, - NULL, /* set_text_font */ - _vte_skel_get_text_metrics, - NULL, /* get_char_width */ - NULL, /* has_bold */ - _vte_skel_draw_text, - NULL, /* draw_has_char */ - NULL, /* draw_rectangle */ - _vte_skel_fill_rectangle -}; diff --git a/src/vteskel.h b/src/vteskel.h deleted file mode 100644 index e202632..0000000 --- a/src/vteskel.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2003 Red Hat, Inc. - * - * This is free software; you can redistribute it and/or modify it under - * the terms of the GNU Library General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 - * General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef vte_vteskel_h_included -#define vte_vteskel_h_included - -/* The interfaces in this file are subject to change at any time. */ - - -#include "vtedraw.h" - -G_BEGIN_DECLS - -extern const struct _vte_draw_impl _vte_draw_skel; - -G_END_DECLS - -#endif |