mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-08 17:50:10 +00:00
91d36dd410
Removed via regex and grep.
The following were intentionally not removed:
- GtkImage:file: (attributes org.gtk.Property.set=gtk_image_set_from_file)
- GtkImage:resource: (attributes org.gtk.Property.set=gtk_image_set_from_resource)
As they have no getter and (setter PROP) without a (getter PROP) crash
gobject-introspection. This is fixed by
ad3118eb51
.
458 lines
12 KiB
C
458 lines
12 KiB
C
/* GTK - The GIMP Toolkit
|
|
*
|
|
* Copyright (C) 2023 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Author:
|
|
* Matthias Clasen <mclasen@redhat.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gtkbinlayout.h"
|
|
#include "gtkgraphicsoffload.h"
|
|
#include "gtksnapshotprivate.h"
|
|
#include "gtkwidgetprivate.h"
|
|
#include "gtkprivate.h"
|
|
#include "gdk/gdksurfaceprivate.h"
|
|
#include "gdk/gdksubsurfaceprivate.h"
|
|
#include "gdk/gdkrgbaprivate.h"
|
|
#include "gtktypebuiltins.h"
|
|
|
|
/**
|
|
* GtkGraphicsOffload:
|
|
*
|
|
* A widget that allows to bypass gsk rendering for its child by passing the content
|
|
* directly to the compositor.
|
|
*
|
|
* Graphics offload is an optimization to reduce overhead and battery use that is
|
|
* most useful for video content. It only works on some platforms and in certain
|
|
* situations. GTK will automatically fall back to normal rendering if it doesn't.
|
|
*
|
|
* Graphics offload is most efficient if there are no controls drawn on top of the
|
|
* video content.
|
|
*
|
|
* You should consider using graphics offload for your main widget if it shows
|
|
* frequently changing content (such as a video, or a VM display) and you provide
|
|
* the content in the form of dmabuf textures (see [class@Gdk.DmabufTextureBuilder]),
|
|
* in particular if it may be fullscreen.
|
|
*
|
|
* Numerous factors can prohibit graphics offload:
|
|
*
|
|
* - Unsupported platforms. Currently, graphics offload only works on Linux with Wayland.
|
|
*
|
|
* - Clipping, such as rounded corners that cause the video content to not be rectangular
|
|
*
|
|
* - Unsupported dmabuf formats (see [method@Gdk.Display.get_dmabuf_formats])
|
|
*
|
|
* - Translucent video content (content with an alpha channel, even if it isn't used)
|
|
*
|
|
* - Transforms that are more complex than translations and scales
|
|
*
|
|
* - Filters such as opacity, grayscale or similar
|
|
*
|
|
* To investigate problems related graphics offload, GTK offers debug flags to print
|
|
* out information about graphics offload and dmabuf use:
|
|
*
|
|
* GDK_DEBUG=offload
|
|
* GDK_DEBUG=dmabuf
|
|
*
|
|
* The GTK inspector provides a visual debugging tool for graphics offload.
|
|
*
|
|
* Since: 4.14
|
|
*/
|
|
|
|
struct _GtkGraphicsOffload
|
|
{
|
|
GtkWidget parent_instance;
|
|
|
|
GtkWidget *child;
|
|
|
|
GdkSubsurface *subsurface;
|
|
|
|
GtkGraphicsOffloadEnabled enabled;
|
|
gboolean black_background;
|
|
};
|
|
|
|
struct _GtkGraphicsOffloadClass
|
|
{
|
|
GtkWidgetClass parent_class;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_CHILD,
|
|
PROP_ENABLED,
|
|
PROP_BLACK_BACKGROUND,
|
|
LAST_PROP,
|
|
};
|
|
|
|
static GParamSpec *properties[LAST_PROP] = { NULL, };
|
|
|
|
G_DEFINE_TYPE (GtkGraphicsOffload, gtk_graphics_offload, GTK_TYPE_WIDGET)
|
|
|
|
static void
|
|
gtk_graphics_offload_init (GtkGraphicsOffload *self)
|
|
{
|
|
self->enabled = GTK_GRAPHICS_OFFLOAD_ENABLED;
|
|
}
|
|
|
|
static void
|
|
gtk_graphics_offload_dispose (GObject *object)
|
|
{
|
|
GtkGraphicsOffload *self = GTK_GRAPHICS_OFFLOAD (object);
|
|
|
|
g_clear_pointer (&self->child, gtk_widget_unparent);
|
|
|
|
G_OBJECT_CLASS (gtk_graphics_offload_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gtk_graphics_offload_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkGraphicsOffload *self = GTK_GRAPHICS_OFFLOAD (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_CHILD:
|
|
gtk_graphics_offload_set_child (self, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_ENABLED:
|
|
gtk_graphics_offload_set_enabled (self, g_value_get_enum (value));
|
|
break;
|
|
|
|
case PROP_BLACK_BACKGROUND:
|
|
gtk_graphics_offload_set_black_background (self, g_value_get_boolean (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_graphics_offload_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkGraphicsOffload *self = GTK_GRAPHICS_OFFLOAD (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_CHILD:
|
|
g_value_set_object (value, gtk_graphics_offload_get_child (self));
|
|
break;
|
|
|
|
case PROP_ENABLED:
|
|
g_value_set_enum (value, gtk_graphics_offload_get_enabled (self));
|
|
break;
|
|
|
|
case PROP_BLACK_BACKGROUND:
|
|
g_value_set_boolean (value, gtk_graphics_offload_get_black_background (self));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sync_subsurface (GtkGraphicsOffload *self)
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (self);
|
|
|
|
if (gtk_widget_get_realized (widget) && self->enabled != GTK_GRAPHICS_OFFLOAD_DISABLED)
|
|
{
|
|
if (!self->subsurface)
|
|
self->subsurface = gdk_surface_create_subsurface (gtk_widget_get_surface (widget));
|
|
}
|
|
else
|
|
{
|
|
g_clear_object (&self->subsurface);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_graphics_offload_realize (GtkWidget *widget)
|
|
{
|
|
GtkGraphicsOffload *self = GTK_GRAPHICS_OFFLOAD (widget);
|
|
|
|
GTK_WIDGET_CLASS (gtk_graphics_offload_parent_class)->realize (widget);
|
|
|
|
sync_subsurface (self);
|
|
}
|
|
|
|
static void
|
|
gtk_graphics_offload_unrealize (GtkWidget *widget)
|
|
{
|
|
GtkGraphicsOffload *self = GTK_GRAPHICS_OFFLOAD (widget);
|
|
|
|
GTK_WIDGET_CLASS (gtk_graphics_offload_parent_class)->unrealize (widget);
|
|
|
|
sync_subsurface (self);
|
|
}
|
|
|
|
static void
|
|
gtk_graphics_offload_snapshot (GtkWidget *widget,
|
|
GtkSnapshot *snapshot)
|
|
{
|
|
GtkGraphicsOffload *self = GTK_GRAPHICS_OFFLOAD (widget);
|
|
|
|
if (self->subsurface)
|
|
gtk_snapshot_push_subsurface (snapshot, self->subsurface);
|
|
|
|
if (self->black_background)
|
|
gtk_snapshot_append_color (snapshot,
|
|
&GDK_RGBA_BLACK,
|
|
&GRAPHENE_RECT_INIT (0, 0,
|
|
gtk_widget_get_width (widget),
|
|
gtk_widget_get_height (widget)));
|
|
|
|
if (self->child)
|
|
gtk_widget_snapshot_child (widget, self->child, snapshot);
|
|
|
|
if (self->subsurface)
|
|
gtk_snapshot_pop (snapshot);
|
|
}
|
|
|
|
static void
|
|
gtk_graphics_offload_class_init (GtkGraphicsOffloadClass *class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
|
|
|
object_class->dispose = gtk_graphics_offload_dispose;
|
|
object_class->set_property = gtk_graphics_offload_set_property;
|
|
object_class->get_property = gtk_graphics_offload_get_property;
|
|
|
|
widget_class->realize = gtk_graphics_offload_realize;
|
|
widget_class->unrealize = gtk_graphics_offload_unrealize;
|
|
widget_class->snapshot = gtk_graphics_offload_snapshot;
|
|
|
|
/**
|
|
* GtkGraphicsOffload:child:
|
|
*
|
|
* The child widget.
|
|
*
|
|
* Since: 4.14
|
|
*/
|
|
properties[PROP_CHILD] = g_param_spec_object ("child", NULL, NULL,
|
|
GTK_TYPE_WIDGET,
|
|
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
|
|
|
/**
|
|
* GtkGraphicsOffload:enabled:
|
|
*
|
|
* Whether graphics offload is enabled.
|
|
*
|
|
* Since: 4.14
|
|
*/
|
|
properties[PROP_ENABLED] = g_param_spec_enum ("enabled", NULL, NULL,
|
|
GTK_TYPE_GRAPHICS_OFFLOAD_ENABLED,
|
|
GTK_GRAPHICS_OFFLOAD_ENABLED,
|
|
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
|
|
|
/**
|
|
* GtkGraphicsOffload:black-background:
|
|
*
|
|
* Whether to draw a black background.
|
|
*
|
|
* Since: 4.16
|
|
*/
|
|
properties[PROP_BLACK_BACKGROUND] = g_param_spec_boolean ("black-background", NULL, NULL,
|
|
FALSE,
|
|
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
|
|
|
g_object_class_install_properties (object_class, LAST_PROP, properties);
|
|
|
|
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
|
|
gtk_widget_class_set_css_name (widget_class, "graphicsoffload");
|
|
}
|
|
|
|
/**
|
|
* gtk_graphics_offload_new:
|
|
* @child: (nullable): the child widget
|
|
*
|
|
* Creates a new GtkGraphicsOffload widget.
|
|
*
|
|
* Returns: the new widget
|
|
*
|
|
* Since: 4.14
|
|
*/
|
|
GtkWidget *
|
|
gtk_graphics_offload_new (GtkWidget *child)
|
|
{
|
|
return g_object_new (GTK_TYPE_GRAPHICS_OFFLOAD,
|
|
"child", child,
|
|
NULL);
|
|
}
|
|
|
|
/**
|
|
* gtk_graphics_offload_set_child:
|
|
* @self: a `GtkGraphicsOffload`
|
|
* @child: (nullable): the child widget
|
|
*
|
|
* Sets the child of @self.
|
|
*
|
|
* Since: 4.14
|
|
*/
|
|
void
|
|
gtk_graphics_offload_set_child (GtkGraphicsOffload *self,
|
|
GtkWidget *child)
|
|
{
|
|
g_return_if_fail (GTK_IS_GRAPHICS_OFFLOAD (self));
|
|
g_return_if_fail (child == NULL || self->child == child || (GTK_IS_WIDGET (child) &>k_widget_get_parent (child) == NULL));
|
|
|
|
if (self->child == child)
|
|
return;
|
|
|
|
g_clear_pointer (&self->child, gtk_widget_unparent);
|
|
|
|
if (child)
|
|
{
|
|
self->child = child;
|
|
gtk_widget_set_parent (child, GTK_WIDGET (self));
|
|
}
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHILD]);
|
|
}
|
|
|
|
/**
|
|
* gtk_graphics_offload_get_child:
|
|
* @self: a `GtkGraphicsOffload`
|
|
*
|
|
* Gets the child of @self.
|
|
*
|
|
* Returns: (nullable) (transfer none): the child widget
|
|
*
|
|
* Since: 4.14
|
|
*/
|
|
GtkWidget *
|
|
gtk_graphics_offload_get_child (GtkGraphicsOffload *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_GRAPHICS_OFFLOAD (self), NULL);
|
|
|
|
return self->child;
|
|
}
|
|
|
|
/**
|
|
* gtk_graphics_offload_set_enabled:
|
|
* @self: a `GtkGraphicsOffload`
|
|
* @enabled: whether to enable offload
|
|
*
|
|
* Sets whether this GtkGraphicsOffload widget will attempt
|
|
* to offload the content of its child widget.
|
|
*
|
|
* Since: 4.14
|
|
*/
|
|
void
|
|
gtk_graphics_offload_set_enabled (GtkGraphicsOffload *self,
|
|
GtkGraphicsOffloadEnabled enabled)
|
|
{
|
|
g_return_if_fail (GTK_IS_GRAPHICS_OFFLOAD (self));
|
|
|
|
if (self->enabled == enabled)
|
|
return;
|
|
|
|
self->enabled = enabled;
|
|
|
|
sync_subsurface (self);
|
|
gtk_widget_queue_draw (GTK_WIDGET (self));
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ENABLED]);
|
|
}
|
|
|
|
/**
|
|
* gtk_graphics_offload_get_enabled:
|
|
* @self: a `GtkGraphicsOffload`
|
|
*
|
|
* Returns whether offload is enabled for @self.
|
|
*
|
|
* Returns: whether offload is enabled
|
|
*
|
|
* Since: 4.14
|
|
*/
|
|
GtkGraphicsOffloadEnabled
|
|
gtk_graphics_offload_get_enabled (GtkGraphicsOffload *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_GRAPHICS_OFFLOAD (self), TRUE);
|
|
|
|
return self->enabled;
|
|
}
|
|
|
|
/**
|
|
* gtk_graphics_offload_set_black_background:
|
|
* @self: a `GtkGraphicsOffload`
|
|
* @value: whether to draw a black background behind the content
|
|
*
|
|
* Sets whether this GtkGraphicsOffload widget will draw a black
|
|
* background.
|
|
*
|
|
* A main use case for this is **_letterboxing_** where black bars are
|
|
* visible next to the content if the aspect ratio of the content does
|
|
* not match the dimensions of the monitor.
|
|
*
|
|
* Using this property for letterboxing instead of CSS allows compositors
|
|
* to show content with maximum efficiency, using direct scanout to avoid
|
|
* extra copies in the compositor.
|
|
*
|
|
* On Wayland, this is implemented using the
|
|
* [single-pixel buffer](https://wayland.app/protocols/single-pixel-buffer-v1)
|
|
* protocol.
|
|
*
|
|
* Since: 4.16
|
|
*/
|
|
void
|
|
gtk_graphics_offload_set_black_background (GtkGraphicsOffload *self,
|
|
gboolean value)
|
|
{
|
|
g_return_if_fail (GTK_IS_GRAPHICS_OFFLOAD (self));
|
|
|
|
if (self->black_background == value)
|
|
return;
|
|
|
|
self->black_background = value;
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (self));
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BLACK_BACKGROUND]);
|
|
}
|
|
|
|
/**
|
|
* gtk_graphics_offload_get_black_background:
|
|
* @self: a `GtkGraphicsOffload`
|
|
*
|
|
* Returns whether the widget draws a black background.
|
|
*
|
|
* See [method@Gtk.GraphicsOffload.set_black_background].
|
|
*
|
|
* Returns: `TRUE` if black background is drawn
|
|
*
|
|
* Since: 4.16
|
|
*/
|
|
gboolean
|
|
gtk_graphics_offload_get_black_background (GtkGraphicsOffload *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_GRAPHICS_OFFLOAD (self), FALSE);
|
|
|
|
return self->black_background;
|
|
}
|