1 /* Cairo - a vector graphics library with display and print output
2  *
3  * Copyright © 2005 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it either under the terms of the GNU Lesser General Public
7  * License version 2.1 as published by the Free Software Foundation
8  * (the "LGPL") or, at your option, under the terms of the Mozilla
9  * Public License Version 1.1 (the "MPL"). If you do not alter this
10  * notice, a recipient may use your version of this file under either
11  * the MPL or the LGPL.
12  *
13  * You should have received a copy of the LGPL along with this library
14  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16  * You should have received a copy of the MPL along with this library
17  * in the file COPYING-MPL-1.1
18  *
19  * The contents of this file are subject to the Mozilla Public License
20  * Version 1.1 (the "License"); you may not use this file except in
21  * compliance with the License. You may obtain a copy of the License at
22  * http://www.mozilla.org/MPL/
23  *
24  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26  * the specific language governing rights and limitations.
27  *
28  * The Original Code is the cairo graphics library.
29  *
30  * The Initial Developer of the Original Code is Red Hat, Inc.
31  *
32  * Partially on code from xftdpy.c
33  *
34  * Copyright © 2000 Keith Packard
35  *
36  * Permission to use, copy, modify, distribute, and sell this software and its
37  * documentation for any purpose is hereby granted without fee, provided that
38  * the above copyright notice appear in all copies and that both that
39  * copyright notice and this permission notice appear in supporting
40  * documentation, and that the name of Keith Packard not be used in
41  * advertising or publicity pertaining to distribution of the software without
42  * specific, written prior permission.  Keith Packard makes no
43  * representations about the suitability of this software for any purpose.  It
44  * is provided "as is" without express or implied warranty.
45  *
46  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
47  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
48  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
49  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
50  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
51  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
52  * PERFORMANCE OF THIS SOFTWARE.
53  */
54 
55 #include "cairoint.h"
56 
57 #include "cairo-xlib-private.h"
58 #include "cairo-xlib-xrender-private.h"
59 
60 #include "cairo-xlib-surface-private.h"
61 #include "cairo-error-private.h"
62 
63 #include "cairo-fontconfig-private.h"
64 
65 static int
parse_boolean(const char * v)66 parse_boolean (const char *v)
67 {
68     char c0, c1;
69 
70     c0 = *v;
71     if (c0 == 't' || c0 == 'T' || c0 == 'y' || c0 == 'Y' || c0 == '1')
72 	return 1;
73     if (c0 == 'f' || c0 == 'F' || c0 == 'n' || c0 == 'N' || c0 == '0')
74 	return 0;
75     if (c0 == 'o')
76     {
77 	c1 = v[1];
78 	if (c1 == 'n' || c1 == 'N')
79 	    return 1;
80 	if (c1 == 'f' || c1 == 'F')
81 	    return 0;
82     }
83 
84     return -1;
85 }
86 
87 static cairo_bool_t
get_boolean_default(Display * dpy,const char * option,cairo_bool_t * value)88 get_boolean_default (Display       *dpy,
89 		     const char    *option,
90 		     cairo_bool_t  *value)
91 {
92     char *v;
93     int i;
94 
95     v = XGetDefault (dpy, "Xft", option);
96     if (v) {
97 	i = parse_boolean (v);
98 	if (i >= 0) {
99 	    *value = i;
100 	    return TRUE;
101 	}
102     }
103 
104     return FALSE;
105 }
106 
107 static cairo_bool_t
get_integer_default(Display * dpy,const char * option,int * value)108 get_integer_default (Display    *dpy,
109 		     const char *option,
110 		     int        *value)
111 {
112     char *v, *e;
113 
114     v = XGetDefault (dpy, "Xft", option);
115     if (v) {
116 #if CAIRO_HAS_FC_FONT
117 	if (FcNameConstant ((FcChar8 *) v, value))
118 	    return TRUE;
119 #endif
120 
121 	*value = strtol (v, &e, 0);
122 	if (e != v)
123 	    return TRUE;
124     }
125 
126     return FALSE;
127 }
128 
129 static void
_cairo_xlib_init_screen_font_options(Display * dpy,cairo_xlib_screen_t * info)130 _cairo_xlib_init_screen_font_options (Display *dpy,
131 				      cairo_xlib_screen_t *info)
132 {
133     cairo_bool_t xft_hinting;
134     cairo_bool_t xft_antialias;
135     int xft_hintstyle;
136     int xft_rgba;
137     int xft_lcdfilter;
138     cairo_antialias_t antialias;
139     cairo_subpixel_order_t subpixel_order;
140     cairo_lcd_filter_t lcd_filter;
141     cairo_hint_style_t hint_style;
142 
143     if (!get_boolean_default (dpy, "antialias", &xft_antialias))
144 	xft_antialias = TRUE;
145 
146     if (!get_integer_default (dpy, "lcdfilter", &xft_lcdfilter)) {
147 	/* -1 is an non-existant Fontconfig constant used to differentiate
148 	 * the case when no lcdfilter property is available.
149 	 */
150 	xft_lcdfilter = -1;
151     }
152 
153     if (!get_boolean_default (dpy, "hinting", &xft_hinting))
154 	xft_hinting = TRUE;
155 
156     if (!get_integer_default (dpy, "hintstyle", &xft_hintstyle))
157 	xft_hintstyle = FC_HINT_FULL;
158 
159     if (!get_integer_default (dpy, "rgba", &xft_rgba))
160     {
161         cairo_xlib_display_t *display = (cairo_xlib_display_t *) info->device;
162 
163 	xft_rgba = FC_RGBA_UNKNOWN;
164 
165 #if RENDER_MAJOR > 0 || RENDER_MINOR >= 6
166         if (display->render_major > 0 || display->render_minor >= 6) {
167 	    int render_order = XRenderQuerySubpixelOrder (dpy,
168 							  XScreenNumberOfScreen (info->screen));
169 
170 	    switch (render_order) {
171 	    default:
172 	    case SubPixelUnknown:
173 		xft_rgba = FC_RGBA_UNKNOWN;
174 		break;
175 	    case SubPixelHorizontalRGB:
176 		xft_rgba = FC_RGBA_RGB;
177 		break;
178 	    case SubPixelHorizontalBGR:
179 		xft_rgba = FC_RGBA_BGR;
180 		break;
181 	    case SubPixelVerticalRGB:
182 		xft_rgba = FC_RGBA_VRGB;
183 		break;
184 	    case SubPixelVerticalBGR:
185 		xft_rgba = FC_RGBA_VBGR;
186 		break;
187 	    case SubPixelNone:
188 		xft_rgba = FC_RGBA_NONE;
189 		break;
190 	    }
191 	}
192 #endif
193     }
194 
195     if (xft_hinting) {
196 	switch (xft_hintstyle) {
197 	case FC_HINT_NONE:
198 	    hint_style = CAIRO_HINT_STYLE_NONE;
199 	    break;
200 	case FC_HINT_SLIGHT:
201 	    hint_style = CAIRO_HINT_STYLE_SLIGHT;
202 	    break;
203 	case FC_HINT_MEDIUM:
204 	    hint_style = CAIRO_HINT_STYLE_MEDIUM;
205 	    break;
206 	case FC_HINT_FULL:
207 	    hint_style = CAIRO_HINT_STYLE_FULL;
208 	    break;
209 	default:
210 	    hint_style = CAIRO_HINT_STYLE_DEFAULT;
211 	}
212     } else {
213 	hint_style = CAIRO_HINT_STYLE_NONE;
214     }
215 
216     switch (xft_rgba) {
217     case FC_RGBA_RGB:
218 	subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
219 	break;
220     case FC_RGBA_BGR:
221 	subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
222 	break;
223     case FC_RGBA_VRGB:
224 	subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
225 	break;
226     case FC_RGBA_VBGR:
227 	subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
228 	break;
229     case FC_RGBA_UNKNOWN:
230     case FC_RGBA_NONE:
231     default:
232 	subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
233     }
234 
235     switch (xft_lcdfilter) {
236     case FC_LCD_NONE:
237 	lcd_filter = CAIRO_LCD_FILTER_NONE;
238 	break;
239     case FC_LCD_DEFAULT:
240 	lcd_filter = CAIRO_LCD_FILTER_FIR5;
241 	break;
242     case FC_LCD_LIGHT:
243 	lcd_filter = CAIRO_LCD_FILTER_FIR3;
244 	break;
245     case FC_LCD_LEGACY:
246 	lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL;
247 	break;
248     default:
249 	lcd_filter = CAIRO_LCD_FILTER_DEFAULT;
250 	break;
251     }
252 
253     if (xft_antialias) {
254 	if (subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT)
255 	    antialias = CAIRO_ANTIALIAS_GRAY;
256 	else
257 	    antialias = CAIRO_ANTIALIAS_SUBPIXEL;
258     } else {
259 	antialias = CAIRO_ANTIALIAS_NONE;
260     }
261 
262     cairo_font_options_set_hint_style (&info->font_options, hint_style);
263     cairo_font_options_set_antialias (&info->font_options, antialias);
264     cairo_font_options_set_subpixel_order (&info->font_options, subpixel_order);
265     cairo_font_options_set_lcd_filter (&info->font_options, lcd_filter);
266     cairo_font_options_set_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON);
267 }
268 
269 void
_cairo_xlib_screen_close_display(cairo_xlib_display_t * display,cairo_xlib_screen_t * info)270 _cairo_xlib_screen_close_display (cairo_xlib_display_t *display,
271                                   cairo_xlib_screen_t  *info)
272 {
273     Display *dpy;
274     int i;
275 
276     dpy = display->display;
277 
278     for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
279 	if ((info->gc_depths >> (8*i)) & 0xff)
280 	    XFreeGC (dpy, info->gc[i]);
281     }
282     info->gc_depths = 0;
283 }
284 
285 void
_cairo_xlib_screen_destroy(cairo_xlib_screen_t * info)286 _cairo_xlib_screen_destroy (cairo_xlib_screen_t *info)
287 {
288     while (! cairo_list_is_empty (&info->visuals)) {
289         _cairo_xlib_visual_info_destroy (cairo_list_first_entry (&info->visuals,
290                                                                  cairo_xlib_visual_info_t,
291                                                                  link));
292     }
293 
294     cairo_list_del (&info->link);
295 
296     free (info);
297 }
298 
299 cairo_status_t
_cairo_xlib_screen_get(Display * dpy,Screen * screen,cairo_xlib_screen_t ** out)300 _cairo_xlib_screen_get (Display *dpy,
301 			Screen *screen,
302 			cairo_xlib_screen_t **out)
303 {
304     cairo_xlib_display_t *display;
305     cairo_device_t *device;
306     cairo_xlib_screen_t *info;
307     cairo_status_t status;
308 
309     device = _cairo_xlib_device_create (dpy);
310     status = device->status;
311     if (unlikely (status))
312         goto CLEANUP_DEVICE;
313 
314     status =  _cairo_xlib_display_acquire (device, &display);
315     if (unlikely (status))
316         goto CLEANUP_DEVICE;
317 
318     info = _cairo_xlib_display_get_screen (display, screen);
319     if (info != NULL) {
320 	*out = info;
321 	goto CLEANUP_DISPLAY;
322     }
323 
324     info = malloc (sizeof (cairo_xlib_screen_t));
325     if (unlikely (info == NULL)) {
326 	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
327 	goto CLEANUP_DISPLAY;
328     }
329 
330     info->device = device;
331     info->screen = screen;
332     info->has_font_options = FALSE;
333     info->gc_depths = 0;
334     memset (info->gc, 0, sizeof (info->gc));
335 
336     cairo_list_init (&info->visuals);
337     cairo_list_add (&info->link, &display->screens);
338 
339     *out = info;
340 
341   CLEANUP_DISPLAY:
342     cairo_device_release (&display->base);
343 
344   CLEANUP_DEVICE:
345     cairo_device_destroy (device);
346     return status;
347 }
348 
349 GC
_cairo_xlib_screen_get_gc(cairo_xlib_display_t * display,cairo_xlib_screen_t * info,int depth,Drawable drawable)350 _cairo_xlib_screen_get_gc (cairo_xlib_display_t *display,
351                            cairo_xlib_screen_t *info,
352 			   int depth,
353 			   Drawable drawable)
354 {
355     GC gc = NULL;
356     int i;
357 
358     for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
359 	if (((info->gc_depths >> (8*i)) & 0xff) == depth) {
360 	    info->gc_depths &= ~(0xff << (8*i));
361 	    gc = info->gc[i];
362 	    break;
363 	}
364     }
365 
366     if (gc == NULL) {
367 	XGCValues gcv;
368 
369 	gcv.graphics_exposures = False;
370 	gcv.fill_style = FillTiled;
371 	gc = XCreateGC (display->display,
372 			drawable,
373 			GCGraphicsExposures | GCFillStyle, &gcv);
374     }
375 
376     return gc;
377 }
378 
379 void
_cairo_xlib_screen_put_gc(cairo_xlib_display_t * display,cairo_xlib_screen_t * info,int depth,GC gc)380 _cairo_xlib_screen_put_gc (cairo_xlib_display_t *display,
381                            cairo_xlib_screen_t *info,
382 			   int depth,
383 			   GC gc)
384 {
385     int i;
386 
387     for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
388 	if (((info->gc_depths >> (8*i)) & 0xff) == 0)
389 	    break;
390     }
391 
392     if (i == ARRAY_LENGTH (info->gc)) {
393 	cairo_status_t status;
394 
395 	/* perform random substitution to ensure fair caching over depths */
396 	i = rand () % ARRAY_LENGTH (info->gc);
397 	status =
398 	    _cairo_xlib_display_queue_work (display,
399 					    (cairo_xlib_notify_func) XFreeGC,
400 					    info->gc[i],
401 					    NULL);
402 	if (unlikely (status)) {
403 	    /* leak the server side resource... */
404 	    XFree ((char *) info->gc[i]);
405 	}
406     }
407 
408     info->gc[i] = gc;
409     info->gc_depths &= ~(0xff << (8*i));
410     info->gc_depths |= depth << (8*i);
411 }
412 
413 cairo_status_t
_cairo_xlib_screen_get_visual_info(cairo_xlib_display_t * display,cairo_xlib_screen_t * info,Visual * v,cairo_xlib_visual_info_t ** out)414 _cairo_xlib_screen_get_visual_info (cairo_xlib_display_t *display,
415                                     cairo_xlib_screen_t *info,
416 				    Visual *v,
417 				    cairo_xlib_visual_info_t **out)
418 {
419     cairo_xlib_visual_info_t *visual;
420     cairo_status_t status;
421 
422     cairo_list_foreach_entry (visual,
423                               cairo_xlib_visual_info_t,
424                               &info->visuals,
425                               link)
426     {
427 	if (visual->visualid == v->visualid) {
428             *out = visual;
429             return CAIRO_STATUS_SUCCESS;
430 	}
431     }
432 
433     status = _cairo_xlib_visual_info_create (display->display,
434 					     XScreenNumberOfScreen (info->screen),
435 					     v->visualid,
436 					     &visual);
437     if (unlikely (status))
438 	return status;
439 
440     cairo_list_add (&visual->link, &info->visuals);
441     *out = visual;
442     return CAIRO_STATUS_SUCCESS;
443 }
444 
445 cairo_font_options_t *
_cairo_xlib_screen_get_font_options(cairo_xlib_screen_t * info)446 _cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info)
447 {
448     if (! info->has_font_options) {
449 	_cairo_font_options_init_default (&info->font_options);
450 	_cairo_font_options_set_round_glyph_positions (&info->font_options, CAIRO_ROUND_GLYPH_POS_ON);
451 
452 	if (info->screen != NULL) {
453             cairo_xlib_display_t *display;
454 
455             if (! _cairo_xlib_display_acquire (info->device, &display)) {
456                 _cairo_xlib_init_screen_font_options (display->display,
457                                                       info);
458                 cairo_device_release (&display->base);
459             }
460 	}
461 
462 	info->has_font_options = TRUE;
463     }
464 
465     return &info->font_options;
466 }
467