Exemplo n.º 1
0
/**
 * cairo_font_face_destroy:
 * @font_face: a #cairo_font_face_t
 *
 * Decreases the reference count on @font_face by one. If the result
 * is zero, then @font_face and all associated resources are freed.
 * See cairo_font_face_reference().
 **/
void
cairo_font_face_destroy (cairo_font_face_t *font_face)
{
    if (font_face == NULL || font_face->ref_count == CAIRO_REF_COUNT_INVALID)
	return;

    CAIRO_MUTEX_LOCK (_cairo_font_face_mutex);

    assert (font_face->ref_count > 0);

    if (--(font_face->ref_count) > 0) {
        CAIRO_MUTEX_UNLOCK (_cairo_font_face_mutex);
	return;
    }

    CAIRO_MUTEX_UNLOCK (_cairo_font_face_mutex);

    font_face->backend->destroy (font_face);

    /* We allow resurrection to deal with some memory management for the
     * FreeType backend where cairo_ft_font_face_t and cairo_ft_unscaled_font_t
     * need to effectively mutually reference each other
     */
    if (font_face->ref_count > 0)
	return;

    _cairo_user_data_array_fini (&font_face->user_data);

    free (font_face);
}
Exemplo n.º 2
0
cairo_status_t
_cairo_xlib_screen_get_visual_info (cairo_xlib_screen_info_t *info,
				    Visual *visual,
				    cairo_xlib_visual_info_t **out)
{
    Display *dpy = info->display->display;
    cairo_xlib_visual_info_t **visuals, *ret = NULL;
    cairo_status_t status;
    int i, n_visuals;

    CAIRO_MUTEX_LOCK (info->mutex);
    visuals = _cairo_array_index (&info->visuals, 0);
    n_visuals = _cairo_array_num_elements (&info->visuals);
    for (i = 0; i < n_visuals; i++) {
	if (visuals[i]->visualid == visual->visualid) {
	    ret = visuals[i];
	    break;
	}
    }
    CAIRO_MUTEX_UNLOCK (info->mutex);

    if (ret != NULL) {
	*out = ret;
	return CAIRO_STATUS_SUCCESS;
    }

    status = _cairo_xlib_visual_info_create (dpy,
					     XScreenNumberOfScreen (info->screen),
					     visual->visualid,
					     &ret);
    if (status)
	return status;

    CAIRO_MUTEX_LOCK (info->mutex);
    if (n_visuals != _cairo_array_num_elements (&info->visuals)) {
	/* check that another thread has not added our visual */
	int new_visuals = _cairo_array_num_elements (&info->visuals);
	visuals = _cairo_array_index (&info->visuals, 0);
	for (i = n_visuals; i < new_visuals; i++) {
	    if (visuals[i]->visualid == visual->visualid) {
		_cairo_xlib_visual_info_destroy (dpy, ret);
		ret = visuals[i];
		break;
	    }
	}
	if (i == new_visuals)
	    status = _cairo_array_append (&info->visuals, &ret);
    } else
	status = _cairo_array_append (&info->visuals, &ret);
    CAIRO_MUTEX_UNLOCK (info->mutex);

    if (status) {
	_cairo_xlib_visual_info_destroy (dpy, ret);
	return status;
    }

    *out = ret;
    return CAIRO_STATUS_SUCCESS;
}
Exemplo n.º 3
0
void
_cairo_xlib_display_notify (cairo_xlib_display_t *display)
{
    cairo_xlib_job_t *jobs, *job, *freelist;
    Display *dpy = display->display;

    CAIRO_MUTEX_LOCK (display->mutex);
    jobs = display->workqueue;
    while (jobs != NULL) {
	display->workqueue = NULL;
	CAIRO_MUTEX_UNLOCK (display->mutex);

	/* reverse the list to obtain FIFO order */
	job = NULL;
	do {
	    cairo_xlib_job_t *next = jobs->next;
	    jobs->next = job;
	    job = jobs;
	    jobs = next;
	} while (jobs != NULL);
	freelist = jobs = job;

	do {
	    job = jobs;
	    jobs = job->next;

	    switch (job->type){
	    case WORK:
		job->func.work.notify (dpy, job->func.work.data);
		if (job->func.work.destroy != NULL)
		    job->func.work.destroy (job->func.work.data);
		break;

	    case RESOURCE:
		job->func.resource.notify (dpy, job->func.resource.xid);
		break;
	    }
	} while (jobs != NULL);

	CAIRO_MUTEX_LOCK (display->mutex);
	do {
	    job = freelist;
	    freelist = job->next;
	    _cairo_freelist_free (&display->wq_freelist, job);
	} while (freelist != NULL);

	jobs = display->workqueue;
    }
    CAIRO_MUTEX_UNLOCK (display->mutex);
}
Exemplo n.º 4
0
static int
_cairo_xlib_close_display (Display *dpy, XExtCodes *codes)
{
    cairo_xlib_display_t *display, **prev, *next;
    cairo_xlib_error_func_t old_handler;

    CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
    for (display = _cairo_xlib_display_list; display; display = display->next)
	if (display->display == dpy)
	    break;
    CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
    if (display == NULL)
	return 0;

    /* protect the notifies from triggering XErrors */
    XSync (dpy, False);
    old_handler = XSetErrorHandler (_noop_error_handler);

    _cairo_xlib_display_notify (display);
    _cairo_xlib_call_close_display_hooks (display);
    _cairo_xlib_display_discard_screens (display);

    /* catch any that arrived before marking the display as closed */
    _cairo_xlib_display_notify (display);

    XSync (dpy, False);
    XSetErrorHandler (old_handler);

    /*
     * Unhook from the global list
     */
    CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
    prev = &_cairo_xlib_display_list;
    for (display = _cairo_xlib_display_list; display; display = next) {
	next = display->next;
	if (display->display == dpy) {
	    *prev = next;
	    break;
	} else
	    prev = &display->next;
    }
    CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);

    assert (display != NULL);
    _cairo_xlib_display_destroy (display);

    /* Return value in accordance with requirements of
     * XESetCloseDisplay */
    return 0;
}
Exemplo n.º 5
0
void
_cairo_scaled_font_map_destroy (void)
{
    int i;
    cairo_scaled_font_map_t *font_map = cairo_scaled_font_map;
    cairo_scaled_font_t *scaled_font;

    if (font_map == NULL)
	return;

    CAIRO_MUTEX_UNLOCK (cairo_scaled_font_map_mutex);

    for (i = 0; i < font_map->num_holdovers; i++) {
	scaled_font = font_map->holdovers[i];
	/* We should only get here through the reset_static_data path
	 * and there had better not be any active references at that
	 * point. */
	assert (scaled_font->ref_count == 0);
	_cairo_hash_table_remove (font_map->hash_table,
				  &scaled_font->hash_entry);
	_cairo_scaled_font_fini (scaled_font);
	free (scaled_font);
    }

    _cairo_hash_table_destroy (font_map->hash_table);

    free (cairo_scaled_font_map);
    cairo_scaled_font_map = NULL;
}
static cairo_int_status_t
_cairo_paginated_surface_show_glyphs (void			*abstract_surface,
				      cairo_operator_t		 op,
				      cairo_pattern_t		*source,
				      cairo_glyph_t		*glyphs,
				      int			 num_glyphs,
				      cairo_scaled_font_t	*scaled_font)
{
    cairo_paginated_surface_t *surface = abstract_surface;
    cairo_int_status_t status;

    /* Optimize away erasing of nothing. */
    if (surface->page_is_blank && op == CAIRO_OPERATOR_CLEAR)
	return CAIRO_STATUS_SUCCESS;

    surface->page_is_blank = FALSE;

    /* Since this is a "wrapping" surface, we're calling back into
     * _cairo_surface_show_glyphs from within a call to the same.
     * Since _cairo_surface_show_glyphs acquires a mutex, we release
     * and re-acquire the mutex around this nested call.
     *
     * Yes, this is ugly, but we consider it pragmatic as compared to
     * adding locking code to all 18 surface-backend-specific
     * show_glyphs functions, (which would get less testing and likely
     * lead to bugs).
     */
    CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
    status = _cairo_surface_show_glyphs (surface->meta, op, source,
					 glyphs, num_glyphs,
					 scaled_font);
    CAIRO_MUTEX_LOCK (scaled_font->mutex);

    return status;
}
Exemplo n.º 7
0
cairo_status_t
_cairo_xlib_display_queue_work (cairo_xlib_display_t *display,
	                        cairo_xlib_notify_func notify,
				void *data,
				void (*destroy) (void *))
{
    cairo_xlib_job_t *job;
    cairo_status_t status = CAIRO_STATUS_NO_MEMORY;

    CAIRO_MUTEX_LOCK (display->mutex);
    if (display->closed == FALSE) {
	job = _cairo_freelist_alloc (&display->wq_freelist);
	if (job != NULL) {
	    job->type = WORK;
	    job->func.work.data    = data;
	    job->func.work.notify  = notify;
	    job->func.work.destroy = destroy;

	    job->next = display->workqueue;
	    display->workqueue = job;

	    status = CAIRO_STATUS_SUCCESS;
	}
    }
    CAIRO_MUTEX_UNLOCK (display->mutex);

    return status;
}
Exemplo n.º 8
0
cairo_status_t
_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display,
	                            cairo_xlib_notify_resource_func notify,
				    XID xid)
{
    cairo_xlib_job_t *job;
    cairo_status_t status = CAIRO_STATUS_NO_MEMORY;

    CAIRO_MUTEX_LOCK (display->mutex);
    if (display->closed == FALSE) {
	job = _cairo_freelist_alloc (&display->wq_freelist);
	if (job != NULL) {
	    job->type = RESOURCE;
	    job->func.resource.xid = xid;
	    job->func.resource.notify = notify;

	    job->next = display->workqueue;
	    display->workqueue = job;

	    status = CAIRO_STATUS_SUCCESS;
	}
    }
    CAIRO_MUTEX_UNLOCK (display->mutex);

    return status;
}
Exemplo n.º 9
0
void
_cairo_xlib_remove_close_display_hooks (Display *dpy, const void *key)
{
    cairo_xlib_display_t *display;
    cairo_xlib_hook_t *hook, *next, **prev;

    display = _cairo_xlib_display_get (dpy);
    if (display == NULL)
	return;

    CAIRO_MUTEX_LOCK (display->mutex);
    prev = &display->close_display_hooks;
    for (hook = display->close_display_hooks; hook != NULL; hook = next) {
	next = hook->next;
	if (hook->key == key) {
	    *prev = hook->next;
	    _cairo_freelist_free (&display->hook_freelist, hook);
	} else
	    prev = &hook->next;
    }
    *prev = NULL;
    CAIRO_MUTEX_UNLOCK (display->mutex);

    _cairo_xlib_display_destroy (display);
}
Exemplo n.º 10
0
cairo_bool_t
_cairo_xlib_add_close_display_hook (Display *dpy, void (*func) (Display *, void *), void *data, const void *key)
{
    cairo_xlib_display_t *display;
    cairo_xlib_hook_t *hook;
    cairo_bool_t ret = FALSE;

    display = _cairo_xlib_display_get (dpy);
    if (display == NULL)
	return FALSE;

    CAIRO_MUTEX_LOCK (display->mutex);
    if (display->closed == FALSE) {
	hook = _cairo_freelist_alloc (&display->hook_freelist);
	if (hook != NULL) {
	    hook->func = func;
	    hook->data = data;
	    hook->key = key;

	    hook->next = display->close_display_hooks;
	    display->close_display_hooks = hook;
	    ret = TRUE;
	}
    }
    CAIRO_MUTEX_UNLOCK (display->mutex);

    _cairo_xlib_display_destroy (display);

    return ret;
}
Exemplo n.º 11
0
void
_cairo_xlib_screen_info_destroy (cairo_xlib_screen_info_t *info)
{
    cairo_xlib_screen_info_t **prev;
    cairo_xlib_screen_info_t *list;

    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&info->ref_count));

    if (! _cairo_reference_count_dec_and_test (&info->ref_count))
	return;

    CAIRO_MUTEX_LOCK (info->display->mutex);
    for (prev = &info->display->screens; (list = *prev); prev = &list->next) {
	if (list == info) {
	    *prev = info->next;
	    break;
	}
    }
    CAIRO_MUTEX_UNLOCK (info->display->mutex);

    _cairo_xlib_screen_info_close_display (info);

    _cairo_xlib_display_destroy (info->display);

    _cairo_array_fini (&info->visuals);

    CAIRO_MUTEX_FINI (info->mutex);

    free (info);
}
Exemplo n.º 12
0
cairo_status_t
_cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info, int depth, GC gc, cairo_bool_t reset_clip)
{
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
    GC oldgc;

    depth = depth_to_index (depth);

    CAIRO_MUTEX_LOCK (info->mutex);
    oldgc = info->gc[depth];
    info->gc[depth] = gc;
    if (reset_clip)
	info->gc_needs_clip_reset |= 1 << depth;
    else
	info->gc_needs_clip_reset &= ~(1 << depth);
    CAIRO_MUTEX_UNLOCK (info->mutex);

    if (oldgc != NULL) {
	status = _cairo_xlib_display_queue_work (info->display,
		                               (cairo_xlib_notify_func) XFreeGC,
					       oldgc,
					       NULL);
    }

    return status;
}
Exemplo n.º 13
0
void
_cairo_atomic_int_inc (int *x)
{
    CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
    *x += 1;
    CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);
}
Exemplo n.º 14
0
static cairo_scaled_font_map_t *
_cairo_scaled_font_map_lock (void)
{
    CAIRO_MUTEX_LOCK (cairo_scaled_font_map_mutex);

    if (cairo_scaled_font_map == NULL) {
	cairo_scaled_font_map = malloc (sizeof (cairo_scaled_font_map_t));
	if (cairo_scaled_font_map == NULL)
	    goto CLEANUP_MUTEX_LOCK;

	cairo_scaled_font_map->hash_table =
	    _cairo_hash_table_create (_cairo_scaled_font_keys_equal);

	if (cairo_scaled_font_map->hash_table == NULL)
	    goto CLEANUP_SCALED_FONT_MAP;

	cairo_scaled_font_map->num_holdovers = 0;
    }

    return cairo_scaled_font_map;

 CLEANUP_SCALED_FONT_MAP:
    free (cairo_scaled_font_map);
 CLEANUP_MUTEX_LOCK:
    CAIRO_MUTEX_UNLOCK (cairo_scaled_font_map_mutex);
    return NULL;
}
Exemplo n.º 15
0
void
_cairo_atomic_int_set (int *x, int value)
{
    CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
    *x = value;
    CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);
}
Exemplo n.º 16
0
void
_cairo_unlock_global_image_glyph_cache (void)
{
    if (_global_image_glyph_cache) {
	_cairo_cache_shrink_to (_global_image_glyph_cache, 
				CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT);
    }
    CAIRO_MUTEX_UNLOCK (_global_image_glyph_cache_mutex);
}
Exemplo n.º 17
0
void
_cairo_font_reset_static_data (void)
{
    _cairo_scaled_font_map_destroy ();

    CAIRO_MUTEX_LOCK (cairo_toy_font_face_hash_table_mutex);
    _cairo_hash_table_destroy (cairo_toy_font_face_hash_table);
    cairo_toy_font_face_hash_table = NULL;
    CAIRO_MUTEX_UNLOCK (cairo_toy_font_face_hash_table_mutex);
}
Exemplo n.º 18
0
int
_cairo_atomic_int_get (int *x)
{
    int ret;

    CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
    ret = *x;
    CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);

    return ret;
}
Exemplo n.º 19
0
cairo_bool_t
_cairo_atomic_int_dec_and_test (int *x)
{
    cairo_bool_t ret;

    CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
    ret = --*x == 0;
    CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);

    return ret;
}
Exemplo n.º 20
0
cairo_atomic_intptr_t
_cairo_atomic_int_get (cairo_atomic_intptr_t *x)
{
    cairo_atomic_intptr_t ret;

    CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
    ret = *x;
    CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);

    return ret;
}
Exemplo n.º 21
0
void
_cairo_toy_font_face_reset_static_data (void)
{
    /* We manually acquire the lock rather than calling
     * cairo_toy_font_face_hash_table_lock simply to avoid
     * creating the table only to destroy it again. */
    CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex);
    _cairo_hash_table_destroy (cairo_toy_font_face_hash_table);
    cairo_toy_font_face_hash_table = NULL;
    CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex);
}
Exemplo n.º 22
0
int
_cairo_atomic_int_cmpxchg (int *x, int oldv, int newv)
{
    int ret;

    CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
    ret = *x;
    if (ret == oldv)
	*x = newv;
    CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);

    return ret;
}
Exemplo n.º 23
0
void *
_cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv)
{
    void *ret;

    CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
    ret = *x;
    if (ret == oldv)
	*x = newv;
    CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);

    return ret;
}
Exemplo n.º 24
0
static void
_cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
{
    cairo_xlib_screen_info_t	    *screen;
    cairo_xlib_hook_t		    *hooks, *list;

    /* call all registered shutdown routines */
    CAIRO_MUTEX_LOCK (display->mutex);

    for (screen = display->screens; screen != NULL; screen = screen->next)
	_cairo_xlib_screen_info_close_display (screen);

    hooks = display->close_display_hooks;
    while (hooks != NULL) {
	display->close_display_hooks = NULL;
	CAIRO_MUTEX_UNLOCK (display->mutex);

	list = hooks;
	do {
	    cairo_xlib_hook_t *hook = list;
	    list = hook->next;

	    hook->func (display->display, hook->data);
	} while (list != NULL);

	CAIRO_MUTEX_LOCK (display->mutex);
	do {
	    cairo_xlib_hook_t *hook = hooks;
	    hooks = hook->next;

	    _cairo_freelist_free (&display->hook_freelist, hook);
	} while (hooks != NULL);

	hooks = display->close_display_hooks;
    }
    display->closed = TRUE;

    CAIRO_MUTEX_UNLOCK (display->mutex);
}
Exemplo n.º 25
0
cairo_atomic_intptr_t
_cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_intptr_t *x, cairo_atomic_intptr_t oldv, cairo_atomic_intptr_t newv)
{
    cairo_atomic_intptr_t ret;

    CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
    ret = *x;
    if (ret == oldv)
	*x = newv;
    CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);

    return ret;
}
Exemplo n.º 26
0
void
_cairo_drm_device_fini (cairo_drm_device_t *device)
{
    CAIRO_MUTEX_LOCK (_cairo_drm_device_mutex);
    if (device->prev != NULL)
	device->prev->next = device->next;
    else
	_cairo_drm_known_devices = device->next;
    if (device->next != NULL)
	device->next->prev = device->prev;
    CAIRO_MUTEX_UNLOCK (_cairo_drm_device_mutex);

    if (device->fd != -1)
	close (device->fd);
}
Exemplo n.º 27
0
void
_cairo_font_reset_static_data (void)
{
    _cairo_scaled_font_map_destroy ();

    _cairo_lock_global_image_glyph_cache();
    _cairo_cache_destroy (_global_image_glyph_cache);
    _global_image_glyph_cache = NULL;
    _cairo_unlock_global_image_glyph_cache();

    CAIRO_MUTEX_LOCK (cairo_toy_font_face_hash_table_mutex);
    _cairo_hash_table_destroy (cairo_toy_font_face_hash_table);
    cairo_toy_font_face_hash_table = NULL;
    CAIRO_MUTEX_UNLOCK (cairo_toy_font_face_hash_table_mutex);
}
Exemplo n.º 28
0
static void
_cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface)
{
    cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface;
    cairo_image_surface_t *image;
    cairo_surface_t *clone;
    void *extra;
    cairo_status_t status;

    TRACE ((stderr, "%s: target=%d\n",
	    __FUNCTION__, snapshot->target->unique_id));

    /* We need to make an image copy of the original surface since the
     * snapshot may exceed the lifetime of the original device, i.e.
     * when we later need to use the snapshot the data may have already
     * been lost.
     */

    CAIRO_MUTEX_LOCK (snapshot->mutex);

    if (snapshot->target->backend->snapshot != NULL) {
	clone = snapshot->target->backend->snapshot (snapshot->target);
	if (clone != NULL) {
	    assert (clone->status || ! _cairo_surface_is_snapshot (clone));
	    goto done;
	}
    }

    /* XXX copy to a similar surface, leave acquisition till later?
     * We should probably leave such decisions to the backend in case we
     * rely upon devices/connections like Xlib.
    */
    status = _cairo_surface_acquire_source_image (snapshot->target, &image, &extra);
    if (unlikely (status)) {
	snapshot->target = _cairo_surface_create_in_error (status);
	status = _cairo_surface_set_error (surface, status);
	goto unlock;
    }
    clone = image->base.backend->snapshot (&image->base);
    _cairo_surface_release_source_image (snapshot->target, image, extra);

done:
    status = _cairo_surface_set_error (surface, clone->status);
    snapshot->target = snapshot->clone = clone;
    snapshot->base.type = clone->type;
unlock:
    CAIRO_MUTEX_UNLOCK (snapshot->mutex);
}
Exemplo n.º 29
0
static void
_cairo_xlib_display_discard_screens (cairo_xlib_display_t *display)
{
    cairo_xlib_screen_info_t *screens;

    CAIRO_MUTEX_LOCK (display->mutex);
    screens = display->screens;
    display->screens = NULL;
    CAIRO_MUTEX_UNLOCK (display->mutex);

    while (screens != NULL) {
	cairo_xlib_screen_info_t *screen = screens;
	screens = screen->next;

	_cairo_xlib_screen_info_destroy (screen);
    }
}
Exemplo n.º 30
0
/**
 * cairo_font_face_reference:
 * @font_face: a #cairo_font_face_t, (may be %NULL in which case this
 * function does nothing).
 *
 * Increases the reference count on @font_face by one. This prevents
 * @font_face from being destroyed until a matching call to
 * cairo_font_face_destroy() is made.
 *
 * The number of references to a #cairo_font_face_t can be get using
 * cairo_font_face_get_reference_count().
 *
 * Return value: the referenced #cairo_font_face_t.
 **/
cairo_font_face_t *
cairo_font_face_reference (cairo_font_face_t *font_face)
{
    if (font_face == NULL || font_face->ref_count == CAIRO_REF_COUNT_INVALID)
	return font_face;

    CAIRO_MUTEX_LOCK (_cairo_font_face_mutex);

    /* We would normally assert (font_face->ref_count >0) here but we
     * can't get away with that due to the zombie case as documented
     * in _cairo_ft_font_face_destroy. */

    font_face->ref_count++;

    CAIRO_MUTEX_UNLOCK (_cairo_font_face_mutex);

    return font_face;
}