/* 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 :
*
* Bypasses gsk rendering by passing the content of its child 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,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);
/**
* 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,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);
/**
* 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 ,
G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_EXPLICIT_NOTIFY);
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;
}
Messung V0.5 in Prozent C=98 H=96 G=96
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland