|  | // Copyright 2013 The Flutter Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "flutter/shell/platform/linux/fl_gl_area.h" | 
|  |  | 
|  | #include <epoxy/gl.h> | 
|  |  | 
|  | struct _FlGLArea { | 
|  | GtkWidget parent_instance; | 
|  |  | 
|  | GdkGLContext* context; | 
|  |  | 
|  | GPtrArray* textures; | 
|  | }; | 
|  |  | 
|  | G_DEFINE_TYPE(FlGLArea, fl_gl_area, GTK_TYPE_WIDGET) | 
|  |  | 
|  | static void fl_gl_area_dispose(GObject* gobject) { | 
|  | FlGLArea* self = FL_GL_AREA(gobject); | 
|  |  | 
|  | g_clear_object(&self->context); | 
|  | g_clear_pointer(&self->textures, g_ptr_array_unref); | 
|  |  | 
|  | G_OBJECT_CLASS(fl_gl_area_parent_class)->dispose(gobject); | 
|  | } | 
|  |  | 
|  | // Implements GtkWidget::realize. | 
|  | static void fl_gl_area_realize(GtkWidget* widget) { | 
|  | GtkAllocation allocation; | 
|  | gtk_widget_get_allocation(widget, &allocation); | 
|  |  | 
|  | gtk_widget_set_realized(widget, TRUE); | 
|  |  | 
|  | GdkWindowAttr attributes; | 
|  | attributes.window_type = GDK_WINDOW_CHILD; | 
|  | attributes.x = allocation.x; | 
|  | attributes.y = allocation.y; | 
|  | attributes.width = allocation.width; | 
|  | attributes.height = allocation.height; | 
|  | attributes.wclass = GDK_INPUT_OUTPUT; | 
|  | attributes.visual = gtk_widget_get_visual(widget); | 
|  | attributes.event_mask = gtk_widget_get_events(widget); | 
|  | gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; | 
|  | GdkWindow* window = gdk_window_new(gtk_widget_get_parent_window(widget), | 
|  | &attributes, attributes_mask); | 
|  | gtk_widget_set_window(widget, window); | 
|  | gtk_widget_register_window(widget, window); | 
|  | } | 
|  |  | 
|  | // Implements GtkWidget::unrealize. | 
|  | static void fl_gl_area_unrealize(GtkWidget* widget) { | 
|  | FlGLArea* self = FL_GL_AREA(widget); | 
|  | gdk_gl_context_make_current(self->context); | 
|  | g_clear_pointer(&self->textures, g_ptr_array_unref); | 
|  |  | 
|  | /* Make sure to unset the context if current */ | 
|  | if (self->context == gdk_gl_context_get_current()) { | 
|  | gdk_gl_context_clear_current(); | 
|  | } | 
|  |  | 
|  | GTK_WIDGET_CLASS(fl_gl_area_parent_class)->unrealize(widget); | 
|  | } | 
|  |  | 
|  | // Implements GtkWidget::size_allocate. | 
|  | static void fl_gl_area_size_allocate(GtkWidget* widget, | 
|  | GtkAllocation* allocation) { | 
|  | gtk_widget_set_allocation(widget, allocation); | 
|  |  | 
|  | if (gtk_widget_get_has_window(widget)) { | 
|  | if (gtk_widget_get_realized(widget)) { | 
|  | gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, | 
|  | allocation->y, allocation->width, | 
|  | allocation->height); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Implements GtkWidget::draw. | 
|  | static gboolean fl_gl_area_draw(GtkWidget* widget, cairo_t* cr) { | 
|  | FlGLArea* self = FL_GL_AREA(widget); | 
|  |  | 
|  | gdk_gl_context_make_current(self->context); | 
|  |  | 
|  | gint scale = gtk_widget_get_scale_factor(widget); | 
|  |  | 
|  | if (self->textures == nullptr) { | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | for (guint i = 0; i < self->textures->len; i++) { | 
|  | FlBackingStoreProvider* texture = | 
|  | FL_BACKING_STORE_PROVIDER(g_ptr_array_index(self->textures, i)); | 
|  | uint32_t texture_id = fl_backing_store_provider_get_gl_texture_id(texture); | 
|  | GdkRectangle geometry = fl_backing_store_provider_get_geometry(texture); | 
|  | gdk_cairo_draw_from_gl(cr, gtk_widget_get_window(widget), texture_id, | 
|  | GL_TEXTURE, scale, geometry.x, geometry.y, | 
|  | geometry.width, geometry.height); | 
|  | } | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static void fl_gl_area_class_init(FlGLAreaClass* klass) { | 
|  | GObjectClass* gobject_class = G_OBJECT_CLASS(klass); | 
|  | gobject_class->dispose = fl_gl_area_dispose; | 
|  |  | 
|  | GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); | 
|  | widget_class->realize = fl_gl_area_realize; | 
|  | widget_class->unrealize = fl_gl_area_unrealize; | 
|  | widget_class->size_allocate = fl_gl_area_size_allocate; | 
|  | widget_class->draw = fl_gl_area_draw; | 
|  |  | 
|  | gtk_widget_class_set_accessible_role(widget_class, ATK_ROLE_DRAWING_AREA); | 
|  | } | 
|  |  | 
|  | static void fl_gl_area_init(FlGLArea* self) { | 
|  | gtk_widget_set_app_paintable(GTK_WIDGET(self), TRUE); | 
|  | } | 
|  |  | 
|  | GtkWidget* fl_gl_area_new(GdkGLContext* context) { | 
|  | g_return_val_if_fail(GDK_IS_GL_CONTEXT(context), nullptr); | 
|  | FlGLArea* area = | 
|  | reinterpret_cast<FlGLArea*>(g_object_new(fl_gl_area_get_type(), nullptr)); | 
|  | area->context = GDK_GL_CONTEXT(g_object_ref(context)); | 
|  | return GTK_WIDGET(area); | 
|  | } | 
|  |  | 
|  | void fl_gl_area_queue_render(FlGLArea* self, GPtrArray* textures) { | 
|  | g_return_if_fail(FL_IS_GL_AREA(self)); | 
|  |  | 
|  | g_clear_pointer(&self->textures, g_ptr_array_unref); | 
|  | self->textures = g_ptr_array_ref(textures); | 
|  |  | 
|  | gtk_widget_queue_draw(GTK_WIDGET(self)); | 
|  | } |