Re: [PATCH] xcb: Initialize font options from Xft resources

Uli Schlachter <[email protected]>
Newsgroups gmane.comp.lib.cairo
Message-ID <[email protected]>
Hi,

thanks for looking into this.

Where did you look into how these resources are implemented and how they should
be parsed? Any source that should/can be quoted?

On 03.09.2014 23:04, Lukáš Lalinský wrote:
> From 0be3f607078a4dd24273b793a07240d05f0aff1f Mon Sep 17 00:00:00 2001
> From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= <[email protected]>
> Date: Wed, 3 Sep 2014 22:53:55 +0200
> Subject: [PATCH] xcb: Initialize font options from Xft resources
> 
> There is a similar code in the Xlib backend. The logic here is the same, but
> XCB doesn't support X resources directly, so there is some custom code
> to get and parse the resources from the root window.
> ---
>  src/cairo-xcb-private.h |   6 ++
>  src/cairo-xcb-screen.c  | 271 ++++++++++++++++++++++++++++++++++++++++++++++++
>  src/cairo-xcb-surface.c |   6 +-
>  3 files changed, 280 insertions(+), 3 deletions(-)
> 
> diff --git a/src/cairo-xcb-private.h b/src/cairo-xcb-private.h
> index 134100a..b343b75 100644
> --- a/src/cairo-xcb-private.h
> +++ b/src/cairo-xcb-private.h
> @@ -199,6 +199,9 @@ struct _cairo_xcb_screen {
>      cairo_list_t link;
>      cairo_list_t surfaces;
>      cairo_list_t pictures;
> +
> +    cairo_bool_t has_font_options;
> +    cairo_font_options_t font_options;
>  };
>  
>  struct _cairo_xcb_connection {
> @@ -357,6 +360,9 @@ _cairo_xcb_screen_get_gc (cairo_xcb_screen_t *screen,
>  cairo_private void
>  _cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc);
>  
> +cairo_private cairo_font_options_t *
> +_cairo_xcb_screen_get_font_options (cairo_xcb_screen_t *screen);
> +
>  cairo_private cairo_status_t
>  _cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen,
>  					const cairo_linear_pattern_t *linear,
> diff --git a/src/cairo-xcb-screen.c b/src/cairo-xcb-screen.c
> index 2858d23..06594a7 100644
> --- a/src/cairo-xcb-screen.c
> +++ b/src/cairo-xcb-screen.c
> @@ -30,11 +30,263 @@
>   *    Chris Wilson <[email protected]>
>   */
>  
> +#include <ctype.h>
> +
>  #include "cairoint.h"
>  
>  #include "cairo-xcb-private.h"
>  #include "cairo-list-inline.h"

I wonder if this doesn't better belong into something like a new
cairo-xcb-resources.c to isolate it better from the rest. No strong opinion on this.

> +#include "cairo-fontconfig-private.h"
> +
> +static void
> +parse_boolean (const char *v, cairo_bool_t *out)
> +{
> +    char c0, c1;
> +
> +    c0 = *v;
> +    if (c0 == 't' || c0 == 'T' || c0 == 'y' || c0 == 'Y' || c0 == '1')
> +        *out = TRUE;
> +    if (c0 == 'f' || c0 == 'F' || c0 == 'n' || c0 == 'N' || c0 == '0')
> +        *out = FALSE;
> +    if (c0 == 'o') {
> +        c1 = v[1];
> +        if (c1 == 'n' || c1 == 'N')
> +            *out = TRUE;
> +        if (c1 == 'f' || c1 == 'F')
> +            *out = FALSE;
> +    }
> +}
> +
> +static void
> +parse_integer (const char *v, int *out)
> +{
> +    char *e;
> +    int value;
> +
> +#if CAIRO_HAS_FC_FONT
> +    if (FcNameConstant ((FcChar8 *) v, out))
> +        return;
> +#endif
> +
> +    value = strtol (v, &e, 0);
> +    if (e != v)
> +        *out = value;
> +}
>
> +struct resource_state {
> +    cairo_bool_t xft_antialias;
> +    int xft_lcdfilter;
> +    cairo_bool_t xft_hinting;
> +    int xft_hintstyle;
> +    int xft_rgba;
> +};
> +
> +struct resource_parser {
> +    int buffer_size;
> +    int bytes_in_buffer;
> +    char* buffer;
> +    struct resource_state *state;
> +};

Should be renamed to resource_parse_lines(). In the caller below I wondered "why
does this only parse a single line?".

> +static int
> +resource_parse_line (struct resource_parser *parser)
> +{
> +    char *newline, *name, *value;
> +
> +    name = parser->buffer;
> +    while (1) {

I know that this break is necessary to make the parsing-in-parts work, but this
also means that the very last line of the resources property won't be parsed if
it doesn't end in a line break. Is this intended?

> +        newline = strchr (name, '\n');
> +        if (newline == NULL)
> +            break;
> +
> +        value = strchr (name, ':');
> +        if (value == NULL)
> +            break;
> +
> +        *newline++ = 0;
> +        *value++ = 0;

isspace() is locale-dependent. No idea if this actually behaves different in any
locale, but could we please avoid it anyway?

> +        while (isspace(*value))
> +            value++;
> +
> +        if (strcmp(name, "Xft.antialias") == 0)
> +            parse_boolean(value, &(parser->state->xft_antialias));
> +        else if (strcmp(name, "Xft.lcdfilter") == 0)
> +            parse_integer(value, &(parser->state->xft_lcdfilter));
> +        else if (strcmp(name, "Xft.rgba") == 0)
> +            parse_integer(value, &(parser->state->xft_rgba));
> +        else if (strcmp(name, "Xft.hinting") == 0)
> +            parse_integer(value, &(parser->state->xft_hinting));
> +        else if (strcmp(name, "Xft.hintstyle") == 0)
> +            parse_integer(value, &(parser->state->xft_hintstyle));
> +
> +        name = newline;
> +    }
> +
> +    return name - parser->buffer;
> +}
> +
> +static void
> +resource_parser_init (struct resource_parser *parser, struct resource_state *state)
> +{
> +    parser->buffer_size = 0;
> +    parser->bytes_in_buffer = 0;
> +    parser->buffer = 0;
> +    parser->state = state;
> +}
> +
> +static void
> +resource_parser_done (struct resource_parser *parser)
> +{
> +    free (parser->buffer);
> +}
> +
> +static void
> +resource_parse (struct resource_parser *parser, const char *data, int length)
> +{
> +    int bytes_parsed;
> +
> +    if (parser->bytes_in_buffer + length > parser->buffer_size) {
> +	parser->buffer_size = parser->bytes_in_buffer + length + 1;

realloc() can fail, this needs error handling.

> +	parser->buffer = realloc(parser->buffer, parser->buffer_size);
> +    }
> +
> +    memmove (parser->buffer + parser->bytes_in_buffer, data, length);
> +    parser->bytes_in_buffer += length;
> +    parser->buffer[parser->bytes_in_buffer] = 0;
> +
> +    bytes_parsed = resource_parse_line (parser);
> +
> +    if (parser->bytes_in_buffer > bytes_parsed) {
> +	memmove (parser->buffer, parser->buffer + bytes_parsed, parser->bytes_in_buffer - bytes_parsed);

Shouldn't this always subtract bytes_parsed, not just when it happens to be
smaller than bytes_in_buffer? In other words: What happens if the buffer end was
on a line break?

> +	parser->bytes_in_buffer -= bytes_parsed;
> +    }
> +}
> +
> +static void
> +get_resources(xcb_connection_t *connection, xcb_screen_t *screen, struct resource_state *state)
> +{
> +    xcb_get_property_cookie_t rm_cookie;
> +    xcb_get_property_reply_t *rm_reply;
> +    struct resource_parser parser;

has_more_data should be of type cairo_bool_t.

> +    int offset = 0, has_more_data = 0;
> +
> +    resource_parser_init (&parser, state);
> +
> +    do {

Please set has_more_data to 0 / FALSE here to make it easier to follow the
logic. I know that this doesn't actually change the behavior of the code.

How come you chose 1024?

> +        rm_cookie = xcb_get_property (connection, 0, screen->root, XCB_ATOM_RESOURCE_MANAGER, XCB_ATOM_STRING, offset, 1024);
> +        rm_reply = xcb_get_property_reply (connection, rm_cookie, NULL);
> +
> +        if (rm_reply) {
> +            if (rm_reply->format == 8 && rm_reply->type == XCB_ATOM_STRING) {
> +                char *value = (char *) xcb_get_property_value (rm_reply);
> +                int length = xcb_get_property_value_length (rm_reply);
> +
> +                resource_parse (&parser, value, length);
> +
> +                has_more_data = rm_reply->bytes_after > 0;

I love X11 for its intuitivity. Yup, this division is correct...

> +                offset += length / 4;
> +            }
> +
> +            free (rm_reply);
> +        }
> +    } while (has_more_data);
> +
> +    resource_parser_done (&parser);
> +}
> +
> +static void
> +_cairo_xcb_init_screen_font_options (cairo_xcb_screen_t *screen)
> +{
> +    struct resource_state res;
> +    cairo_antialias_t antialias;
> +    cairo_subpixel_order_t subpixel_order;
> +    cairo_lcd_filter_t lcd_filter;
> +    cairo_hint_style_t hint_style;

Would it be better to initialize "res" in get_resources() instead of here?

> +    res.xft_antialias = TRUE;
> +    res.xft_lcdfilter = -1;
> +    res.xft_hinting = TRUE;
> +    res.xft_hintstyle = FC_HINT_FULL;

I looked at cairo-xlib. If there is no xft.rgba entry, it uses RENDER to query
the subpixel order. Could you prepare the code for this and leave behind a
comment saying that this is missing? Perhaps even implement the necessary call
to xcb_render_query_subpixel_order?

> +    res.xft_rgba = FC_RGBA_UNKNOWN;
> +
> +    get_resources(screen->connection->xcb_connection, screen->xcb_screen, &res);

Hm. Could you add a comment here and in cairo-xlib-screen.c saying that the
following code was copy-pasted between the two files?

> +    if (res.xft_hinting) {
> +	switch (res.xft_hintstyle) {
> +	case FC_HINT_NONE:
> +	    hint_style = CAIRO_HINT_STYLE_NONE;
> +	    break;
> +	case FC_HINT_SLIGHT:
> +	    hint_style = CAIRO_HINT_STYLE_SLIGHT;
> +	    break;
> +	case FC_HINT_MEDIUM:
> +	    hint_style = CAIRO_HINT_STYLE_MEDIUM;
> +	    break;
> +	case FC_HINT_FULL:
> +	    hint_style = CAIRO_HINT_STYLE_FULL;
> +	    break;
> +	default:
> +	    hint_style = CAIRO_HINT_STYLE_DEFAULT;
> +	}
> +    } else {
> +	hint_style = CAIRO_HINT_STYLE_NONE;
> +    }
> +
> +    switch (res.xft_rgba) {
> +    case FC_RGBA_RGB:
> +	subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
> +	break;
> +    case FC_RGBA_BGR:
> +	subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
> +	break;
> +    case FC_RGBA_VRGB:
> +	subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
> +	break;
> +    case FC_RGBA_VBGR:
> +	subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
> +	break;
> +    case FC_RGBA_UNKNOWN:
> +    case FC_RGBA_NONE:
> +    default:
> +	subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
> +    }
> +
> +    switch (res.xft_lcdfilter) {
> +    case FC_LCD_NONE:
> +	lcd_filter = CAIRO_LCD_FILTER_NONE;
> +	break;
> +    case FC_LCD_DEFAULT:
> +	lcd_filter = CAIRO_LCD_FILTER_FIR5;
> +	break;
> +    case FC_LCD_LIGHT:
> +	lcd_filter = CAIRO_LCD_FILTER_FIR3;
> +	break;
> +    case FC_LCD_LEGACY:
> +	lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL;
> +	break;
> +    default:
> +	lcd_filter = CAIRO_LCD_FILTER_DEFAULT;
> +	break;
> +    }
> +
> +    if (res.xft_antialias) {
> +	if (subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT)
> +	    antialias = CAIRO_ANTIALIAS_GRAY;
> +	else
> +	    antialias = CAIRO_ANTIALIAS_SUBPIXEL;
> +    } else {
> +	antialias = CAIRO_ANTIALIAS_NONE;
> +    }
> +
> +    cairo_font_options_set_hint_style (&screen->font_options, hint_style);
> +    cairo_font_options_set_antialias (&screen->font_options, antialias);
> +    cairo_font_options_set_subpixel_order (&screen->font_options, subpixel_order);
> +    _cairo_font_options_set_lcd_filter (&screen->font_options, lcd_filter);
> +    cairo_font_options_set_hint_metrics (&screen->font_options, CAIRO_HINT_METRICS_ON);
> +}
> +
>  struct pattern_cache_entry {
>      cairo_cache_entry_t key;
>      cairo_xcb_screen_t *screen;
> @@ -362,3 +614,22 @@ _cairo_xcb_screen_lookup_radial_picture (cairo_xcb_screen_t *screen,
>  
>      return picture;
>  }
> +
> +cairo_font_options_t *
> +_cairo_xcb_screen_get_font_options (cairo_xcb_screen_t *screen)
> +{

Who actually acquires this mutex? In cairo-xlib,
_cairo_xlib_screen_get_font_options() does this explicitly. In your patch,
nothing does.

> +    assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
> +
> +    if (! screen->has_font_options) {
> +	_cairo_font_options_init_default (&screen->font_options);
> +	_cairo_font_options_set_round_glyph_positions (&screen->font_options, CAIRO_ROUND_GLYPH_POS_ON);

Why this if? _cairo_xcb_screen_get() sets the xcb_screen member right after
allocating the screen and, as far as I can tell, nothing ever unsets it.

> +	if (screen->xcb_screen != NULL) {
> +            _cairo_xcb_init_screen_font_options (screen);
> +	}
> +
> +	screen->has_font_options = TRUE;
> +    }
> +
> +    return &screen->font_options;
> +}
> diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c
> index 02e7a19..d7e0d73 100644
> --- a/src/cairo-xcb-surface.c
> +++ b/src/cairo-xcb-surface.c
> @@ -530,9 +530,9 @@ static void
>  _cairo_xcb_surface_get_font_options (void *abstract_surface,
>  				     cairo_font_options_t *options)
>  {
> -    /* XXX  copy from xlib */
> -    _cairo_font_options_init_default (options);
> -    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
> +    cairo_xcb_surface_t *surface = abstract_surface;
> +
> +    *options = *_cairo_xcb_screen_get_font_options (surface->screen);
>  }
>  
>  static cairo_status_t
> -- 1.8.3.2

Cheers,
Uli
-- 
"Do you know that books smell like nutmeg or some spice from a foreign land?"
                                                  -- Faber in Fahrenheit 451
-- 
cairo mailing list
[email protected]
http://lists.cairographics.org/mailman/listinfo/cairo
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.