From 6ff3f7abad8bed8c82302a671091d2ad3ac0fa6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Tarl=C3=A1=20Cardoso=20Lemos?= Date: Sun, 5 Aug 2012 21:36:49 +0200 Subject: [PATCH] libi3: Implement Pango rendering --- i3-config-wizard/i3-config-wizard.mk | 4 +- i3-config-wizard/main.c | 3 +- i3-dump-log/i3-dump-log.mk | 2 +- i3-input/i3-input.mk | 4 +- i3-input/main.c | 3 +- i3-msg/i3-msg.mk | 2 +- i3-nagbar/i3-nagbar.mk | 4 +- i3-nagbar/main.c | 3 +- i3bar/i3bar.mk | 4 +- i3bar/include/util.h | 2 + include/libi3.h | 9 ++ libi3/font.c | 148 +++++++++++++++++++++++++++ libi3/libi3.mk | 2 +- src/i3.mk | 4 +- 14 files changed, 178 insertions(+), 16 deletions(-) diff --git a/i3-config-wizard/i3-config-wizard.mk b/i3-config-wizard/i3-config-wizard.mk index 526a4ecd..7e9c4bee 100644 --- a/i3-config-wizard/i3-config-wizard.mk +++ b/i3-config-wizard/i3-config-wizard.mk @@ -5,8 +5,8 @@ CLEAN_TARGETS += clean-i3-config-wizard i3_config_wizard_SOURCES_GENERATED = i3-config-wizard/cfgparse.tab.c i3-config-wizard/cfgparse.yy.c i3_config_wizard_SOURCES := $(filter-out $(i3_config_wizard_SOURCES_GENERATED),$(wildcard i3-config-wizard/*.c)) i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h) -i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(X11_CFLAGS) -i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS) +i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS) +i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS) $(PANGO_LIBS) i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES_GENERATED:.c=.o) $(i3_config_wizard_SOURCES:.c=.o) diff --git a/i3-config-wizard/main.c b/i3-config-wizard/main.c index 4c04a6d2..46cf8aa8 100644 --- a/i3-config-wizard/main.c +++ b/i3-config-wizard/main.c @@ -69,6 +69,7 @@ enum { MOD_Mod1, MOD_Mod4 } modifier = MOD_Mod4; static char *config_path; static uint32_t xcb_numlock_mask; xcb_connection_t *conn; +xcb_screen_t *root_screen; static xcb_get_modifier_mapping_reply_t *modmap_reply; static i3Font font; static i3Font bold_font; @@ -456,7 +457,7 @@ int main(int argc, char *argv[]) { #include "atoms.xmacro" #undef xmacro - xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens); + root_screen = xcb_aux_get_screen(conn, screens); root = root_screen->root; if (!(modmap_reply = xcb_get_modifier_mapping_reply(conn, modmap_cookie, NULL))) diff --git a/i3-dump-log/i3-dump-log.mk b/i3-dump-log/i3-dump-log.mk index 17e53a6d..e696546d 100644 --- a/i3-dump-log/i3-dump-log.mk +++ b/i3-dump-log/i3-dump-log.mk @@ -4,7 +4,7 @@ CLEAN_TARGETS += clean-i3-dump-log i3_dump_log_SOURCES := $(wildcard i3-dump-log/*.c) i3_dump_log_HEADERS := $(wildcard i3-dump-log/*.h) -i3_dump_log_CFLAGS = $(XCB_CFLAGS) +i3_dump_log_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) i3_dump_log_LIBS = $(XCB_LIBS) i3_dump_log_OBJECTS := $(i3_dump_log_SOURCES:.c=.o) diff --git a/i3-input/i3-input.mk b/i3-input/i3-input.mk index 98131e76..d31a11ff 100644 --- a/i3-input/i3-input.mk +++ b/i3-input/i3-input.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-input i3_input_SOURCES := $(wildcard i3-input/*.c) i3_input_HEADERS := $(wildcard i3-input/*.h) -i3_input_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) -i3_input_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) +i3_input_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(PANGO_CFLAGS) +i3_input_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(PANGO_LIBS) i3_input_OBJECTS := $(i3_input_SOURCES:.c=.o) diff --git a/i3-input/main.c b/i3-input/main.c index 2de5a41e..d18502cd 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -54,6 +54,7 @@ static int prompt_offset = 0; static int limit; xcb_window_t root; xcb_connection_t *conn; +xcb_screen_t *root_screen; /* * Concats the glyphs (either UCS-2 or UTF-8) to a single string, suitable for @@ -340,7 +341,7 @@ int main(int argc, char *argv[]) { if (!conn || xcb_connection_has_error(conn)) die("Cannot open display\n"); - xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens); + root_screen = xcb_aux_get_screen(conn, screens); root = root_screen->root; symbols = xcb_key_symbols_alloc(conn); diff --git a/i3-msg/i3-msg.mk b/i3-msg/i3-msg.mk index 01d5fc7e..c3a59303 100644 --- a/i3-msg/i3-msg.mk +++ b/i3-msg/i3-msg.mk @@ -4,7 +4,7 @@ CLEAN_TARGETS += clean-i3-msg i3_msg_SOURCES := $(wildcard i3-msg/*.c) i3_msg_HEADERS := $(wildcard i3-msg/*.h) -i3_msg_CFLAGS = $(XCB_CFLAGS) +i3_msg_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) i3_msg_LIBS = $(XCB_LIBS) i3_msg_OBJECTS := $(i3_msg_SOURCES:.c=.o) diff --git a/i3-nagbar/i3-nagbar.mk b/i3-nagbar/i3-nagbar.mk index 5e5f2c4d..4fea1629 100644 --- a/i3-nagbar/i3-nagbar.mk +++ b/i3-nagbar/i3-nagbar.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-nagbar i3_nagbar_SOURCES := $(wildcard i3-nagbar/*.c) i3_nagbar_HEADERS := $(wildcard i3-nagbar/*.h) -i3_nagbar_CFLAGS = $(XCB_CFLAGS) -i3_nagbar_LIBS = $(XCB_LIBS) +i3_nagbar_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) +i3_nagbar_LIBS = $(XCB_LIBS) $(PANGO_LIBS) i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o) diff --git a/i3-nagbar/main.c b/i3-nagbar/main.c index 26a282f6..1588c356 100644 --- a/i3-nagbar/main.c +++ b/i3-nagbar/main.c @@ -54,6 +54,7 @@ static uint32_t color_text; /* color of the text */ xcb_window_t root; xcb_connection_t *conn; +xcb_screen_t *root_screen; /* * Starts the given application by passing it through a shell. We use double fork @@ -280,7 +281,7 @@ int main(int argc, char *argv[]) { #include "atoms.xmacro" #undef xmacro - xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screens); + root_screen = xcb_aux_get_screen(conn, screens); root = root_screen->root; if (bar_type == TYPE_ERROR) { diff --git a/i3bar/i3bar.mk b/i3bar/i3bar.mk index 5b4ddd80..c311adfe 100644 --- a/i3bar/i3bar.mk +++ b/i3bar/i3bar.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar i3bar_SOURCES := $(wildcard i3bar/src/*.c) i3bar_HEADERS := $(wildcard i3bar/include/*.h) -i3bar_CFLAGS = $(XCB_CFLAGS) $(X11_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) -i3bar_LIBS = $(XCB_LIBS) $(X11_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) +i3bar_CFLAGS = $(XCB_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) +i3bar_LIBS = $(XCB_LIBS) $(X11_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o) diff --git a/i3bar/include/util.h b/i3bar/include/util.h index 262d7750..43c56c58 100644 --- a/i3bar/include/util.h +++ b/i3bar/include/util.h @@ -11,7 +11,9 @@ #include "queue.h" /* Get the maximum/minimum of x and y */ +#undef MAX #define MAX(x,y) ((x) > (y) ? (x) : (y)) +#undef MIN #define MIN(x,y) ((x) < (y) ? (x) : (y)) /* Securely free p */ diff --git a/include/libi3.h b/include/libi3.h index 2c468156..7b58b96f 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -18,6 +18,10 @@ #include #include +#if PANGO_SUPPORT +#include +#endif + /** * Opaque data structure for storing strings. * @@ -54,6 +58,11 @@ struct Font { /** Font table for this font (may be NULL) */ xcb_charinfo_t *table; } xcb; + +#if PANGO_SUPPORT + /** The pango font description */ + PangoFontDescription *pango_desc; +#endif } specific; }; diff --git a/libi3/font.c b/libi3/font.c index 7f8c7bb1..d9683e54 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -12,11 +12,118 @@ #include #include +#if PANGO_SUPPORT +#include +#include +#endif + #include "libi3.h" extern xcb_connection_t *conn; +extern xcb_screen_t *root_screen; + static const i3Font *savedFont = NULL; +#if PANGO_SUPPORT +static xcb_visualtype_t *root_visual_type; +static double pango_font_red; +static double pango_font_green; +static double pango_font_blue; + +/* + * Loads a Pango font description into an i3Font structure. Returns true + * on success, false otherwise. + * + */ +static bool load_pango_font(i3Font *font, const char *desc) { + /* Load the font description */ + font->specific.pango_desc = pango_font_description_from_string(desc); + if (!font->specific.pango_desc) + return false; + + /* We cache root_visual_type here, since you must call + * load_pango_font before any other pango function + * that would need root_visual_type */ + root_visual_type = get_visualtype(root_screen); + + /* Create a dummy Pango layout to compute the font height */ + cairo_surface_t *surface = cairo_xcb_surface_create(conn, root_screen->root, root_visual_type, 1, 1); + cairo_t *cr = cairo_create(surface); + PangoLayout *layout = pango_cairo_create_layout(cr); + pango_layout_set_font_description(layout, font->specific.pango_desc); + + /* Get the font height */ + gint height; + pango_layout_get_pixel_size(layout, NULL, &height); + font->height = height; + + /* Free resources */ + g_object_unref(layout); + cairo_destroy(cr); + cairo_surface_destroy(surface); + + /* Set the font type and return successfully */ + font->type = FONT_TYPE_PANGO; + return true; +} + +/* + * Draws text using Pango rendering. + * + */ +static void draw_text_pango(const char *text, size_t text_len, + xcb_drawable_t drawable, int x, int y, int max_width) { + /* Create the Pango layout */ + /* root_visual_type is cached in load_pango_font */ + cairo_surface_t *surface = cairo_xcb_surface_create(conn, drawable, + root_visual_type, x + max_width, y + savedFont->height); + cairo_t *cr = cairo_create(surface); + PangoLayout *layout = pango_cairo_create_layout(cr); + pango_layout_set_font_description(layout, savedFont->specific.pango_desc); + pango_layout_set_width(layout, max_width * PANGO_SCALE); + pango_layout_set_wrap(layout, PANGO_WRAP_CHAR); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + + /* Do the drawing */ + cairo_set_source_rgb(cr, pango_font_red, pango_font_green, pango_font_blue); + cairo_move_to(cr, x, y); + pango_layout_set_text(layout, text, text_len); + pango_cairo_update_layout(cr, layout); + pango_cairo_show_layout(cr, layout); + + /* Free resources */ + g_object_unref(layout); + cairo_destroy(cr); + cairo_surface_destroy(surface); +} + +/* + * Calculate the text width using Pango rendering. + * + */ +static int predict_text_width_pango(const char *text, size_t text_len) { + /* Create a dummy Pango layout */ + /* root_visual_type is cached in load_pango_font */ + cairo_surface_t *surface = cairo_xcb_surface_create(conn, root_screen->root, root_visual_type, 1, 1); + cairo_t *cr = cairo_create(surface); + PangoLayout *layout = pango_cairo_create_layout(cr); + + /* Get the font width */ + gint width; + pango_layout_set_font_description(layout, savedFont->specific.pango_desc); + pango_layout_set_text(layout, text, text_len); + pango_cairo_update_layout(cr, layout); + pango_layout_get_pixel_size(layout, &width, NULL); + + /* Free resources */ + g_object_unref(layout); + cairo_destroy(cr); + cairo_surface_destroy(surface); + + return width; +} +#endif + /* * Loads a font for usage, also getting its metrics. If fallback is true, * the fonts 'fixed' or '-misc-*' will be loaded instead of exiting. @@ -26,6 +133,14 @@ i3Font load_font(const char *pattern, const bool fallback) { i3Font font; font.type = FONT_TYPE_NONE; +#if PANGO_SUPPORT + /* Try to load a pango font if specified */ + if (strlen(pattern) > strlen("xft:") && !strncmp(pattern, "xft:", strlen("xft:"))) { + pattern += strlen("xft:"); + if (load_pango_font(&font, pattern)) + return font; + } +#endif /* Send all our requests first */ font.specific.xcb.id = xcb_generate_id(conn); @@ -105,6 +220,12 @@ void free_font(void) { free(savedFont->specific.xcb.info); break; } +#if PANGO_SUPPORT + case FONT_TYPE_PANGO: + /* Free the font description */ + pango_font_description_free(savedFont->specific.pango_desc); + break; +#endif default: assert(false); break; @@ -129,6 +250,14 @@ void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background xcb_change_gc(conn, gc, mask, values); break; } +#if PANGO_SUPPORT + case FONT_TYPE_PANGO: + /* Save the foreground font */ + pango_font_red = ((foreground >> 16) & 0xff) / 255.0; + pango_font_green = ((foreground >> 8) & 0xff) / 255.0; + pango_font_blue = (foreground & 0xff) / 255.0; + break; +#endif default: assert(false); break; @@ -186,6 +315,13 @@ void draw_text(i3String *text, xcb_drawable_t drawable, draw_text_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text), drawable, gc, x, y, max_width); break; +#if PANGO_SUPPORT + case FONT_TYPE_PANGO: + /* Render the text using Pango */ + draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text), + drawable, x, y, max_width); + return; +#endif default: assert(false); } @@ -219,6 +355,13 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable, } break; } +#if PANGO_SUPPORT + case FONT_TYPE_PANGO: + /* Render the text using Pango */ + draw_text_pango(text, strlen(text), + drawable, x, y, max_width); + return; +#endif default: assert(false); } @@ -309,6 +452,11 @@ int predict_text_width(i3String *text) { return 0; case FONT_TYPE_XCB: return predict_text_width_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text)); +#if PANGO_SUPPORT + case FONT_TYPE_PANGO: + /* Calculate extents using Pango */ + return predict_text_width_pango(i3string_as_utf8(text), i3string_get_num_bytes(text)); +#endif default: assert(false); return 0; diff --git a/libi3/libi3.mk b/libi3/libi3.mk index 6f6bf506..b6a90995 100644 --- a/libi3/libi3.mk +++ b/libi3/libi3.mk @@ -2,7 +2,7 @@ CLEAN_TARGETS += clean-libi3 libi3_SOURCES := $(wildcard libi3/*.c) libi3_HEADERS := $(wildcard libi3/*.h) -libi3_CFLAGS = +libi3_CFLAGS = $(PANGO_CFLAGS) libi3_LIBS = libi3_OBJECTS := $(libi3_SOURCES:.c=.o) diff --git a/src/i3.mk b/src/i3.mk index 9591e178..bc9eabe8 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -6,8 +6,8 @@ i3_SOURCES_GENERATED = src/cfgparse.tab.c src/cfgparse.yy.c i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) -i3_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread +i3_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) +i3_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread # When using clang, we use pre-compiled headers to speed up the build. With # gcc, this actually makes the build slower.