| /* GDK - The GIMP Drawing Kit |
| * |
| * Copyright © 2018 Benjamin Otte |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "config.h" |
| |
| #include "gdkcairocontext-wayland.h" |
| |
| #include "gdkprivate-wayland.h" |
| |
| #include "gdkinternals.h" |
| |
| static const cairo_user_data_key_t gdk_wayland_cairo_context_key; |
| static const cairo_user_data_key_t gdk_wayland_cairo_region_key; |
| |
| G_DEFINE_TYPE (GdkWaylandCairoContext, gdk_wayland_cairo_context, GDK_TYPE_CAIRO_CONTEXT) |
| |
| static void |
| gdk_wayland_cairo_context_surface_add_region (cairo_surface_t *surface, |
| const cairo_region_t *region) |
| { |
| cairo_region_t *surface_region; |
| |
| surface_region = cairo_surface_get_user_data (surface, &gdk_wayland_cairo_region_key); |
| if (surface_region == NULL) |
| { |
| surface_region = cairo_region_copy (region); |
| cairo_surface_set_user_data (surface, |
| &gdk_wayland_cairo_region_key, |
| surface_region, |
| (cairo_destroy_func_t) cairo_region_destroy); |
| } |
| else |
| { |
| cairo_region_union (surface_region, region); |
| } |
| } |
| |
| static void |
| gdk_wayland_cairo_context_surface_clear_region (cairo_surface_t *surface) |
| { |
| cairo_surface_set_user_data (surface, &gdk_wayland_cairo_region_key, NULL, NULL); |
| } |
| |
| static const cairo_region_t * |
| gdk_wayland_cairo_context_surface_get_region (cairo_surface_t *surface) |
| { |
| return cairo_surface_get_user_data (surface, &gdk_wayland_cairo_region_key); |
| } |
| |
| static GdkWaylandCairoContext * |
| gdk_wayland_cairo_context_get_from_surface (cairo_surface_t *surface) |
| { |
| return cairo_surface_get_user_data (surface, &gdk_wayland_cairo_context_key); |
| } |
| |
| static void |
| gdk_wayland_cairo_context_add_surface (GdkWaylandCairoContext *self, |
| cairo_surface_t *surface) |
| { |
| cairo_surface_reference (surface); |
| cairo_surface_set_user_data (surface, &gdk_wayland_cairo_context_key, self, NULL); |
| |
| self->surfaces = g_slist_prepend (self->surfaces, surface); |
| } |
| |
| static void |
| gdk_wayland_cairo_context_remove_surface (GdkWaylandCairoContext *self, |
| cairo_surface_t *surface) |
| { |
| self->surfaces = g_slist_remove (self->surfaces, surface); |
| |
| cairo_surface_set_user_data (surface, &gdk_wayland_cairo_context_key, NULL, NULL); |
| cairo_surface_destroy (surface); |
| } |
| |
| static void |
| gdk_wayland_cairo_context_buffer_release (void *_data, |
| struct wl_buffer *wl_buffer) |
| { |
| cairo_surface_t *cairo_surface = _data; |
| GdkWaylandCairoContext *self = gdk_wayland_cairo_context_get_from_surface (cairo_surface); |
| |
| /* context was destroyed before compositor released this buffer */ |
| if (self == NULL) |
| return; |
| |
| /* Cache one surface for reuse when drawing */ |
| if (self->cached_surface == NULL) |
| { |
| self->cached_surface = cairo_surface; |
| return; |
| } |
| |
| /* Get rid of all the extra ones */ |
| gdk_wayland_cairo_context_remove_surface (self, cairo_surface); |
| } |
| |
| static const struct wl_buffer_listener buffer_listener = { |
| gdk_wayland_cairo_context_buffer_release |
| }; |
| |
| static cairo_surface_t * |
| gdk_wayland_cairo_context_create_surface (GdkWaylandCairoContext *self) |
| { |
| GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (self))); |
| GdkSurface *surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self)); |
| cairo_surface_t *cairo_surface; |
| struct wl_buffer *buffer; |
| cairo_region_t *region; |
| int width, height; |
| |
| width = gdk_surface_get_width (surface); |
| height = gdk_surface_get_height (surface); |
| cairo_surface = _gdk_wayland_display_create_shm_surface (display_wayland, |
| width, height, |
| gdk_surface_get_scale_factor (surface)); |
| buffer = _gdk_wayland_shm_surface_get_wl_buffer (cairo_surface); |
| wl_buffer_add_listener (buffer, &buffer_listener, cairo_surface); |
| gdk_wayland_cairo_context_add_surface (self, cairo_surface); |
| |
| region = cairo_region_create_rectangle (&(cairo_rectangle_int_t) { 0, 0, width, height }); |
| gdk_wayland_cairo_context_surface_add_region (cairo_surface, region); |
| cairo_region_destroy (region); |
| |
| return cairo_surface; |
| } |
| |
| static void |
| gdk_wayland_cairo_context_begin_frame (GdkDrawContext *draw_context, |
| cairo_region_t *region) |
| { |
| GdkWaylandCairoContext *self = GDK_WAYLAND_CAIRO_CONTEXT (draw_context); |
| const cairo_region_t *surface_region; |
| GSList *l; |
| cairo_t *cr; |
| |
| if (self->cached_surface) |
| self->paint_surface = g_steal_pointer (&self->cached_surface); |
| else |
| self->paint_surface = gdk_wayland_cairo_context_create_surface (self); |
| |
| surface_region = gdk_wayland_cairo_context_surface_get_region (self->paint_surface); |
| if (surface_region) |
| cairo_region_union (region, surface_region); |
| |
| for (l = self->surfaces; l; l = l->next) |
| { |
| gdk_wayland_cairo_context_surface_add_region (l->data, region); |
| } |
| |
| /* clear the repaint area */ |
| cr = cairo_create (self->paint_surface); |
| cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); |
| gdk_cairo_region (cr, region); |
| cairo_fill (cr); |
| cairo_destroy (cr); |
| } |
| |
| static void |
| gdk_wayland_cairo_context_end_frame (GdkDrawContext *draw_context, |
| cairo_region_t *painted) |
| { |
| GdkWaylandCairoContext *self = GDK_WAYLAND_CAIRO_CONTEXT (draw_context); |
| GdkSurface *surface = gdk_draw_context_get_surface (draw_context); |
| |
| gdk_wayland_surface_attach_image (surface, self->paint_surface, painted); |
| gdk_wayland_surface_sync (surface); |
| |
| gdk_wayland_cairo_context_surface_clear_region (self->paint_surface); |
| self->paint_surface = NULL; |
| } |
| |
| static void |
| gdk_wayland_cairo_context_clear_all_cairo_surfaces (GdkWaylandCairoContext *self) |
| { |
| self->cached_surface = NULL; |
| while (self->surfaces) |
| gdk_wayland_cairo_context_remove_surface (self, self->surfaces->data); |
| } |
| |
| static void |
| gdk_wayland_cairo_context_surface_resized (GdkDrawContext *draw_context) |
| { |
| GdkWaylandCairoContext *self = GDK_WAYLAND_CAIRO_CONTEXT (draw_context); |
| |
| gdk_wayland_cairo_context_clear_all_cairo_surfaces (self); |
| } |
| |
| static cairo_t * |
| gdk_wayland_cairo_context_cairo_create (GdkCairoContext *context) |
| { |
| GdkWaylandCairoContext *self = GDK_WAYLAND_CAIRO_CONTEXT (context); |
| |
| return cairo_create (self->paint_surface); |
| } |
| |
| static void |
| gdk_wayland_cairo_context_dispose (GObject *object) |
| { |
| GdkWaylandCairoContext *self = GDK_WAYLAND_CAIRO_CONTEXT (object); |
| |
| gdk_wayland_cairo_context_clear_all_cairo_surfaces (self); |
| |
| G_OBJECT_CLASS (gdk_wayland_cairo_context_parent_class)->dispose (object); |
| } |
| |
| static void |
| gdk_wayland_cairo_context_class_init (GdkWaylandCairoContextClass *klass) |
| { |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
| GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass); |
| GdkCairoContextClass *cairo_context_class = GDK_CAIRO_CONTEXT_CLASS (klass); |
| |
| gobject_class->dispose = gdk_wayland_cairo_context_dispose; |
| |
| draw_context_class->begin_frame = gdk_wayland_cairo_context_begin_frame; |
| draw_context_class->end_frame = gdk_wayland_cairo_context_end_frame; |
| draw_context_class->surface_resized = gdk_wayland_cairo_context_surface_resized; |
| |
| cairo_context_class->cairo_create = gdk_wayland_cairo_context_cairo_create; |
| } |
| |
| static void |
| gdk_wayland_cairo_context_init (GdkWaylandCairoContext *self) |
| { |
| } |
| |