1 /* Cairo - a vector graphics library with display and print output
2  *
3  * Copyright © 2009 Intel Corporation
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 Intel Corporation.
31  *
32  * Contributors(s):
33  *	Chris Wilson <chris@chris-wilson.co.uk>
34  */
35 
36 #include "cairoint.h"
37 #include "cairo-device-private.h"
38 #include "cairo-error-private.h"
39 
40 /**
41  * SECTION:cairo-device
42  * @Title: cairo_device_t
43  * @Short_Description: interface to underlying rendering system
44  * @See_Also: #cairo_surface_t
45  *
46  * Devices are the abstraction Cairo employs for the rendering system
47  * used by a #cairo_surface_t. You can get the device of a surface using
48  * cairo_surface_get_device().
49  *
50  * Devices are created using custom functions specific to the rendering
51  * system you want to use. See the documentation for the surface types
52  * for those functions.
53  *
54  * An important function that devices fulfill is sharing access to the
55  * rendering system between Cairo and your application. If you want to
56  * access a device directly that you used to draw to with Cairo, you must
57  * first call cairo_device_flush() to ensure that Cairo finishes all
58  * operations on the device and resets it to a clean state.
59  *
60  * Cairo also provides the functions cairo_device_acquire() and
61  * cairo_device_release() to synchronize access to the rendering system
62  * in a multithreaded environment. This is done internally, but can also
63  * be used by applications.
64  *
65  * Putting this all together, a function that works with devices should
66  * look something like this:
67  * <informalexample><programlisting>
68  * void
69  * my_device_modifying_function (cairo_device_t *device)
70  * {
71  *   cairo_status_t status;
72  *
73  *   // Ensure the device is properly reset
74  *   cairo_device_flush (device);
75  *   // Try to acquire the device
76  *   status = cairo_device_acquire (device);
77  *   if (status != CAIRO_STATUS_SUCCESS) {
78  *     printf ("Failed to acquire the device: %s\n", cairo_status_to_string (status));
79  *     return;
80  *   }
81  *
82  *   // Do the custom operations on the device here.
83  *   // But do not call any Cairo functions that might acquire devices.
84  *
85  *   // Release the device when done.
86  *   cairo_device_release (device);
87  * }
88  * </programlisting></informalexample>
89  *
90  * <note><para>Please refer to the documentation of each backend for
91  * additional usage requirements, guarantees provided, and
92  * interactions with existing surface API of the device functions for
93  * surfaces of that type.
94  * </para></note>
95  */
96 
97 static const cairo_device_t _nil_device = {
98     CAIRO_REFERENCE_COUNT_INVALID,
99     CAIRO_STATUS_NO_MEMORY,
100 };
101 
102 static const cairo_device_t _mismatch_device = {
103     CAIRO_REFERENCE_COUNT_INVALID,
104     CAIRO_STATUS_DEVICE_TYPE_MISMATCH,
105 };
106 
107 static const cairo_device_t _invalid_device = {
108     CAIRO_REFERENCE_COUNT_INVALID,
109     CAIRO_STATUS_DEVICE_ERROR,
110 };
111 
112 cairo_device_t *
_cairo_device_create_in_error(cairo_status_t status)113 _cairo_device_create_in_error (cairo_status_t status)
114 {
115     switch (status) {
116     case CAIRO_STATUS_NO_MEMORY:
117 	return (cairo_device_t *) &_nil_device;
118     case CAIRO_STATUS_DEVICE_ERROR:
119 	return (cairo_device_t *) &_invalid_device;
120     case CAIRO_STATUS_DEVICE_TYPE_MISMATCH:
121 	return (cairo_device_t *) &_mismatch_device;
122 
123     case CAIRO_STATUS_SUCCESS:
124     case CAIRO_STATUS_LAST_STATUS:
125 	ASSERT_NOT_REACHED;
126 	/* fall-through */
127     case CAIRO_STATUS_SURFACE_TYPE_MISMATCH:
128     case CAIRO_STATUS_INVALID_STATUS:
129     case CAIRO_STATUS_INVALID_FORMAT:
130     case CAIRO_STATUS_INVALID_VISUAL:
131     case CAIRO_STATUS_READ_ERROR:
132     case CAIRO_STATUS_WRITE_ERROR:
133     case CAIRO_STATUS_FILE_NOT_FOUND:
134     case CAIRO_STATUS_TEMP_FILE_ERROR:
135     case CAIRO_STATUS_INVALID_STRIDE:
136     case CAIRO_STATUS_INVALID_SIZE:
137     case CAIRO_STATUS_INVALID_RESTORE:
138     case CAIRO_STATUS_INVALID_POP_GROUP:
139     case CAIRO_STATUS_NO_CURRENT_POINT:
140     case CAIRO_STATUS_INVALID_MATRIX:
141     case CAIRO_STATUS_NULL_POINTER:
142     case CAIRO_STATUS_INVALID_STRING:
143     case CAIRO_STATUS_INVALID_PATH_DATA:
144     case CAIRO_STATUS_SURFACE_FINISHED:
145     case CAIRO_STATUS_PATTERN_TYPE_MISMATCH:
146     case CAIRO_STATUS_INVALID_DASH:
147     case CAIRO_STATUS_INVALID_DSC_COMMENT:
148     case CAIRO_STATUS_INVALID_INDEX:
149     case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE:
150     case CAIRO_STATUS_FONT_TYPE_MISMATCH:
151     case CAIRO_STATUS_USER_FONT_IMMUTABLE:
152     case CAIRO_STATUS_USER_FONT_ERROR:
153     case CAIRO_STATUS_NEGATIVE_COUNT:
154     case CAIRO_STATUS_INVALID_CLUSTERS:
155     case CAIRO_STATUS_INVALID_SLANT:
156     case CAIRO_STATUS_INVALID_WEIGHT:
157     case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
158     case CAIRO_STATUS_INVALID_CONTENT:
159     default:
160 	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
161 	return (cairo_device_t *) &_nil_device;
162     }
163 }
164 
165 void
_cairo_device_init(cairo_device_t * device,const cairo_device_backend_t * backend)166 _cairo_device_init (cairo_device_t *device,
167 		    const cairo_device_backend_t *backend)
168 {
169     CAIRO_REFERENCE_COUNT_INIT (&device->ref_count, 1);
170     device->status = CAIRO_STATUS_SUCCESS;
171 
172     device->backend = backend;
173 
174     CAIRO_RECURSIVE_MUTEX_INIT (device->mutex);
175     device->mutex_depth = 0;
176 
177     device->finished = FALSE;
178 
179     _cairo_user_data_array_init (&device->user_data);
180 }
181 
182 /**
183  * cairo_device_reference:
184  * @device: a #cairo_device_t
185  *
186  * Increases the reference count on @device by one. This prevents
187  * @device from being destroyed until a matching call to
188  * cairo_device_destroy() is made.
189  *
190  * The number of references to a #cairo_device_t can be get using
191  * cairo_device_get_reference_count().
192  *
193  * Return value: the referenced #cairo_device_t.
194  *
195  * Since: 1.10
196  **/
197 cairo_device_t *
cairo_device_reference(cairo_device_t * device)198 cairo_device_reference (cairo_device_t *device)
199 {
200     if (device == NULL ||
201 	CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
202     {
203 	return device;
204     }
205 
206     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count));
207     _cairo_reference_count_inc (&device->ref_count);
208 
209     return device;
210 }
211 slim_hidden_def (cairo_device_reference);
212 
213 /**
214  * cairo_device_status:
215  * @device: a #cairo_device_t
216  *
217  * Checks whether an error has previously occurred for this
218  * device.
219  *
220  * Return value: %CAIRO_STATUS_SUCCESS on success or an error code if
221  *               the device is in an error state.
222  *
223  * Since: 1.10
224  **/
225 cairo_status_t
cairo_device_status(cairo_device_t * device)226 cairo_device_status (cairo_device_t *device)
227 {
228     if (device == NULL)
229 	return CAIRO_STATUS_NULL_POINTER;
230 
231     return device->status;
232 }
233 
234 /**
235  * cairo_device_flush:
236  * @device: a #cairo_device_t
237  *
238  * Finish any pending operations for the device and also restore any
239  * temporary modifications cairo has made to the device's state.
240  * This function must be called before switching from using the
241  * device with Cairo to operating on it directly with native APIs.
242  * If the device doesn't support direct access, then this function
243  * does nothing.
244  *
245  * This function may acquire devices.
246  *
247  * Since: 1.10
248  **/
249 void
cairo_device_flush(cairo_device_t * device)250 cairo_device_flush (cairo_device_t *device)
251 {
252     cairo_status_t status;
253 
254     if (device == NULL || device->status)
255 	return;
256 
257     if (device->backend->flush != NULL) {
258 	status = device->backend->flush (device);
259 	if (unlikely (status))
260 	    status = _cairo_device_set_error (device, status);
261     }
262 }
263 slim_hidden_def (cairo_device_flush);
264 
265 /**
266  * cairo_device_finish:
267  * @device: the #cairo_device_t to finish
268  *
269  * This function finishes the device and drops all references to
270  * external resources. All surfaces, fonts and other objects created
271  * for this @device will be finished, too.
272  * Further operations on the @device will not affect the @device but
273  * will instead trigger a %CAIRO_STATUS_DEVICE_FINISHED error.
274  *
275  * When the last call to cairo_device_destroy() decreases the
276  * reference count to zero, cairo will call cairo_device_finish() if
277  * it hasn't been called already, before freeing the resources
278  * associated with the device.
279  *
280  * This function may acquire devices.
281  *
282  * Since: 1.10
283  **/
284 void
cairo_device_finish(cairo_device_t * device)285 cairo_device_finish (cairo_device_t *device)
286 {
287     if (device == NULL ||
288 	CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
289     {
290 	return;
291     }
292 
293     if (device->finished)
294 	return;
295 
296     cairo_device_flush (device);
297 
298     device->finished = TRUE;
299 
300     if (device->backend->finish != NULL)
301 	device->backend->finish (device);
302 }
303 slim_hidden_def (cairo_device_finish);
304 
305 /**
306  * cairo_device_destroy:
307  * @device: a #cairo_device_t
308  *
309  * Decreases the reference count on @device by one. If the result is
310  * zero, then @device and all associated resources are freed.  See
311  * cairo_device_reference().
312  *
313  * This function may acquire devices if the last reference was dropped.
314  *
315  * Since: 1.10
316  **/
317 void
cairo_device_destroy(cairo_device_t * device)318 cairo_device_destroy (cairo_device_t *device)
319 {
320     cairo_user_data_array_t user_data;
321 
322     if (device == NULL ||
323 	CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
324     {
325 	return;
326     }
327 
328     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count));
329     if (! _cairo_reference_count_dec_and_test (&device->ref_count))
330 	return;
331 
332     cairo_device_finish (device);
333 
334     assert (device->mutex_depth == 0);
335     CAIRO_MUTEX_FINI (device->mutex);
336 
337     user_data = device->user_data;
338 
339     device->backend->destroy (device);
340 
341     _cairo_user_data_array_fini (&user_data);
342 
343 }
344 slim_hidden_def (cairo_device_destroy);
345 
346 /**
347  * cairo_device_get_type:
348  * @device: a #cairo_device_t
349  *
350  * This function returns the type of the device. See #cairo_device_type_t
351  * for available types.
352  *
353  * Return value: The type of @device.
354  *
355  * Since: 1.10
356  **/
357 cairo_device_type_t
cairo_device_get_type(cairo_device_t * device)358 cairo_device_get_type (cairo_device_t *device)
359 {
360     if (device == NULL ||
361 	CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
362     {
363 	return (cairo_device_type_t) -1;
364     }
365 
366     return device->backend->type;
367 }
368 
369 /**
370  * cairo_device_acquire:
371  * @device: a #cairo_device_t
372  *
373  * Acquires the @device for the current thread. This function will block
374  * until no other thread has acquired the device.
375  *
376  * If the return value is %CAIRO_STATUS_SUCCESS, you successfully acquired the
377  * device. From now on your thread owns the device and no other thread will be
378  * able to acquire it until a matching call to cairo_device_release(). It is
379  * allowed to recursively acquire the device multiple times from the same
380  * thread.
381  *
382  * <note><para>You must never acquire two different devices at the same time
383  * unless this is explicitly allowed. Otherwise the possibility of deadlocks
384  * exist.
385  *
386  * As various Cairo functions can acquire devices when called, these functions
387  * may also cause deadlocks when you call them with an acquired device. So you
388  * must not have a device acquired when calling them. These functions are
389  * marked in the documentation.
390  * </para></note>
391  *
392  * Return value: %CAIRO_STATUS_SUCCESS on success or an error code if
393  *               the device is in an error state and could not be
394  *               acquired. After a successful call to cairo_device_acquire(),
395  *               a matching call to cairo_device_release() is required.
396  *
397  * Since: 1.10
398  **/
399 cairo_status_t
cairo_device_acquire(cairo_device_t * device)400 cairo_device_acquire (cairo_device_t *device)
401 {
402     if (device == NULL)
403 	return CAIRO_STATUS_SUCCESS;
404 
405     if (unlikely (device->status))
406 	return device->status;
407 
408     if (unlikely (device->finished))
409 	return _cairo_device_set_error (device, CAIRO_STATUS_SURFACE_FINISHED); /* XXX */
410 
411     CAIRO_MUTEX_LOCK (device->mutex);
412     if (device->mutex_depth++ == 0) {
413 	if (device->backend->lock != NULL)
414 	    device->backend->lock (device);
415     }
416 
417     return CAIRO_STATUS_SUCCESS;
418 }
419 slim_hidden_def (cairo_device_acquire);
420 
421 /**
422  * cairo_device_release:
423  * @device: a #cairo_device_t
424  *
425  * Releases a @device previously acquired using cairo_device_acquire(). See
426  * that function for details.
427  *
428  * Since: 1.10
429  **/
430 void
cairo_device_release(cairo_device_t * device)431 cairo_device_release (cairo_device_t *device)
432 {
433     if (device == NULL)
434 	return;
435 
436     assert (device->mutex_depth > 0);
437 
438     if (--device->mutex_depth == 0) {
439 	if (device->backend->unlock != NULL)
440 	    device->backend->unlock (device);
441     }
442 
443     CAIRO_MUTEX_UNLOCK (device->mutex);
444 }
445 slim_hidden_def (cairo_device_release);
446 
447 cairo_status_t
_cairo_device_set_error(cairo_device_t * device,cairo_status_t status)448 _cairo_device_set_error (cairo_device_t *device,
449 			 cairo_status_t  status)
450 {
451     if (status == CAIRO_STATUS_SUCCESS || status >= CAIRO_INT_STATUS_UNSUPPORTED)
452 	return status;
453 
454     /* Don't overwrite an existing error. This preserves the first
455      * error, which is the most significant. */
456     _cairo_status_set_error (&device->status, status);
457 
458     return _cairo_error (status);
459 }
460 
461 /**
462  * cairo_device_get_reference_count:
463  * @device: a #cairo_device_t
464  *
465  * Returns the current reference count of @device.
466  *
467  * Return value: the current reference count of @device.  If the
468  * object is a nil object, 0 will be returned.
469  *
470  * Since: 1.10
471  **/
472 unsigned int
cairo_device_get_reference_count(cairo_device_t * device)473 cairo_device_get_reference_count (cairo_device_t *device)
474 {
475     if (device == NULL ||
476 	CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
477 	return 0;
478 
479     return CAIRO_REFERENCE_COUNT_GET_VALUE (&device->ref_count);
480 }
481 
482 /**
483  * cairo_device_get_user_data:
484  * @device: a #cairo_device_t
485  * @key: the address of the #cairo_user_data_key_t the user data was
486  * attached to
487  *
488  * Return user data previously attached to @device using the
489  * specified key.  If no user data has been attached with the given
490  * key this function returns %NULL.
491  *
492  * Return value: the user data previously attached or %NULL.
493  *
494  * Since: 1.10
495  **/
496 void *
cairo_device_get_user_data(cairo_device_t * device,const cairo_user_data_key_t * key)497 cairo_device_get_user_data (cairo_device_t		 *device,
498 			    const cairo_user_data_key_t *key)
499 {
500     return _cairo_user_data_array_get_data (&device->user_data,
501 					    key);
502 }
503 
504 /**
505  * cairo_device_set_user_data:
506  * @device: a #cairo_device_t
507  * @key: the address of a #cairo_user_data_key_t to attach the user data to
508  * @user_data: the user data to attach to the #cairo_device_t
509  * @destroy: a #cairo_destroy_func_t which will be called when the
510  * #cairo_t is destroyed or when new user data is attached using the
511  * same key.
512  *
513  * Attach user data to @device.  To remove user data from a surface,
514  * call this function with the key that was used to set it and %NULL
515  * for @data.
516  *
517  * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
518  * slot could not be allocated for the user data.
519  *
520  * Since: 1.10
521  **/
522 cairo_status_t
cairo_device_set_user_data(cairo_device_t * device,const cairo_user_data_key_t * key,void * user_data,cairo_destroy_func_t destroy)523 cairo_device_set_user_data (cairo_device_t		 *device,
524 			    const cairo_user_data_key_t *key,
525 			    void			 *user_data,
526 			    cairo_destroy_func_t	  destroy)
527 {
528     if (CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
529 	return device->status;
530 
531     return _cairo_user_data_array_set_data (&device->user_data,
532 					    key, user_data, destroy);
533 }
534