Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 16c3d4b236 | |||
| df93875c0c | |||
| 5a156e6b5f | |||
| 0e488b3dfe | |||
| 8495e0e412 | |||
| c9b0ca0de7 | |||
| 2a950dec71 | |||
| d6c80d4f35 | |||
| e41cc99474 | |||
| 61fe5ac684 | |||
| 1cf3f492b8 | |||
| 54fed96f8c | |||
| e2d659d5c2 | |||
| 8ad1189b0b | |||
| b5b7e1f2b4 | |||
| e56a539363 | |||
| 97aff588d5 | |||
| e4040adbe7 | |||
| 6506a3bc9c | |||
| 1883035d34 |
@@ -70,6 +70,7 @@ style-check-diff:
|
||||
- "${CI_PROJECT_DIR}/_build/testsuite/tools/output/*/*"
|
||||
- "${CI_PROJECT_DIR}/_build/testsuite/gsk/compare/*/*/*.png"
|
||||
- "${CI_PROJECT_DIR}/_build/testsuite/css/output/*/*.syscap"
|
||||
- "${CI_PROJECT_DIR}/_build/testsuite/headless/*/*.log"
|
||||
- "${CI_PROJECT_DIR}/_build_hello/meson-logs"
|
||||
cache:
|
||||
key: "$CI_JOB_NAME"
|
||||
|
||||
@@ -338,6 +338,7 @@ gdk_gl_texture_new_from_builder (GdkGLTextureBuilder *builder,
|
||||
gpointer data)
|
||||
{
|
||||
GdkGLTexture *self;
|
||||
GdkTexture *update_texture;
|
||||
|
||||
self = g_object_new (GDK_TYPE_GL_TEXTURE,
|
||||
"width", gdk_gl_texture_builder_get_width (builder),
|
||||
@@ -353,6 +354,22 @@ gdk_gl_texture_new_from_builder (GdkGLTextureBuilder *builder,
|
||||
self->destroy = destroy;
|
||||
self->data = data;
|
||||
|
||||
update_texture = gdk_gl_texture_builder_get_update_texture (builder);
|
||||
if (update_texture)
|
||||
{
|
||||
cairo_region_t *update_region = gdk_gl_texture_builder_get_update_region (builder);
|
||||
if (update_region)
|
||||
{
|
||||
update_region = cairo_region_copy (update_region);
|
||||
cairo_region_intersect_rectangle (update_region,
|
||||
&(cairo_rectangle_int_t) {
|
||||
0, 0,
|
||||
update_texture->width, update_texture->height
|
||||
});
|
||||
gdk_texture_set_diff (GDK_TEXTURE (self), update_texture, update_region);
|
||||
}
|
||||
}
|
||||
|
||||
return GDK_TEXTURE (self);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include "gdkglcontext.h"
|
||||
#include "gdkgltextureprivate.h"
|
||||
|
||||
#include <cairo-gobject.h>
|
||||
|
||||
struct _GdkGLTextureBuilder
|
||||
{
|
||||
GObject parent_instance;
|
||||
@@ -36,6 +38,9 @@ struct _GdkGLTextureBuilder
|
||||
GdkMemoryFormat format;
|
||||
gboolean has_mipmap;
|
||||
gpointer sync;
|
||||
|
||||
GdkTexture *update_texture;
|
||||
cairo_region_t *update_region;
|
||||
};
|
||||
|
||||
struct _GdkGLTextureBuilderClass
|
||||
@@ -70,6 +75,8 @@ enum
|
||||
PROP_HEIGHT,
|
||||
PROP_ID,
|
||||
PROP_SYNC,
|
||||
PROP_UPDATE_REGION,
|
||||
PROP_UPDATE_TEXTURE,
|
||||
PROP_WIDTH,
|
||||
|
||||
N_PROPS
|
||||
@@ -86,6 +93,9 @@ gdk_gl_texture_builder_dispose (GObject *object)
|
||||
|
||||
g_clear_object (&self->context);
|
||||
|
||||
g_clear_object (&self->update_texture);
|
||||
g_clear_pointer (&self->update_region, cairo_region_destroy);
|
||||
|
||||
G_OBJECT_CLASS (gdk_gl_texture_builder_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
@@ -123,6 +133,14 @@ gdk_gl_texture_builder_get_property (GObject *object,
|
||||
g_value_set_pointer (value, self->sync);
|
||||
break;
|
||||
|
||||
case PROP_UPDATE_REGION:
|
||||
g_value_set_boxed (value, self->update_region);
|
||||
break;
|
||||
|
||||
case PROP_UPDATE_TEXTURE:
|
||||
g_value_set_object (value, self->update_texture);
|
||||
break;
|
||||
|
||||
case PROP_WIDTH:
|
||||
g_value_set_int (value, self->width);
|
||||
break;
|
||||
@@ -167,6 +185,14 @@ gdk_gl_texture_builder_set_property (GObject *object,
|
||||
gdk_gl_texture_builder_set_sync (self, g_value_get_pointer (value));
|
||||
break;
|
||||
|
||||
case PROP_UPDATE_REGION:
|
||||
gdk_gl_texture_builder_set_update_region (self, g_value_get_boxed (value));
|
||||
break;
|
||||
|
||||
case PROP_UPDATE_TEXTURE:
|
||||
gdk_gl_texture_builder_set_update_texture (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_WIDTH:
|
||||
gdk_gl_texture_builder_set_width (self, g_value_get_int (value));
|
||||
break;
|
||||
@@ -260,6 +286,30 @@ gdk_gl_texture_builder_class_init (GdkGLTextureBuilderClass *klass)
|
||||
g_param_spec_pointer ("sync", NULL, NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GdkGLTextureBuilder:update-region: (attributes org.gdk.Property.get=gdk_gl_texture_builder_get_update_region org.gdk.Property.set=gdk_gl_texture_builder_set_update_region)
|
||||
*
|
||||
* The update region for [property@Gdk.GLTextureBuilder:update-texture].
|
||||
*
|
||||
* Since: 4.12
|
||||
*/
|
||||
properties[PROP_UPDATE_REGION] =
|
||||
g_param_spec_boxed ("update-region", NULL, NULL,
|
||||
CAIRO_GOBJECT_TYPE_REGION,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GdkGLTextureBuilder:update-texture: (attributes org.gdk.Property.get=gdk_gl_texture_builder_get_update_texture org.gdk.Property.set=gdk_gl_texture_builder_set_update_texture)
|
||||
*
|
||||
* The texture [property@Gdk.GLTextureBuilder:update-region] is an update for.
|
||||
*
|
||||
* Since: 4.12
|
||||
*/
|
||||
properties[PROP_UPDATE_TEXTURE] =
|
||||
g_param_spec_object ("update-texture", NULL, NULL,
|
||||
GDK_TYPE_TEXTURE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GdkGLTextureBuilder:width: (attributes org.gdk.Property.get=gdk_gl_texture_builder_get_width org.gdk.Property.set=gdk_gl_texture_builder_set_width)
|
||||
*
|
||||
@@ -271,6 +321,7 @@ gdk_gl_texture_builder_class_init (GdkGLTextureBuilderClass *klass)
|
||||
g_param_spec_int ("width", NULL, NULL,
|
||||
G_MININT, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
@@ -613,6 +664,102 @@ gdk_gl_texture_builder_set_format (GdkGLTextureBuilder *self,
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FORMAT]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_gl_texture_builder_get_update_texture: (attributes org.gdk.Method.get_property=update_texture)
|
||||
* @self: a `GdkGLTextureBuilder`
|
||||
*
|
||||
* Gets the texture previously set via gdk_gl_texture_builder_set_update_texture() or
|
||||
* %NULL if none was set.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): The texture
|
||||
*
|
||||
* Since: 4.12
|
||||
*/
|
||||
GdkTexture *
|
||||
gdk_gl_texture_builder_get_update_texture (GdkGLTextureBuilder *self)
|
||||
{
|
||||
g_return_val_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self), NULL);
|
||||
|
||||
return self->update_texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_gl_texture_builder_set_update_texture: (attributes org.gdk.Method.set_property=update_texture)
|
||||
* @self: a `GdkGLTextureBuilder`
|
||||
* @texture: (nullable): the texture to update
|
||||
*
|
||||
* Sets the texture to be updated by this texture. See
|
||||
* [method@Gdk.GLTextureBuilder.set_update_region] for an explanation.
|
||||
*
|
||||
* Since: 4.12
|
||||
*/
|
||||
void
|
||||
gdk_gl_texture_builder_set_update_texture (GdkGLTextureBuilder *self,
|
||||
GdkTexture *texture)
|
||||
{
|
||||
g_return_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self));
|
||||
g_return_if_fail (texture == NULL || GDK_IS_TEXTURE (texture));
|
||||
|
||||
if (!g_set_object (&self->update_texture, texture))
|
||||
return;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_UPDATE_TEXTURE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_gl_texture_builder_get_update_region: (attributes org.gdk.Method.get_property=update_region)
|
||||
* @self: a `GdkGLTextureBuilder`
|
||||
*
|
||||
* Gets the region previously set via gdk_gl_texture_builder_set_update_region() or
|
||||
* %NULL if none was set.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): The region
|
||||
*
|
||||
* Since: 4.12
|
||||
*/
|
||||
cairo_region_t *
|
||||
gdk_gl_texture_builder_get_update_region (GdkGLTextureBuilder *self)
|
||||
{
|
||||
g_return_val_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self), NULL);
|
||||
|
||||
return self->update_region;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_gl_texture_builder_set_update_region: (attributes org.gdk.Method.set_property=update_region)
|
||||
* @self: a `GdkGLTextureBuilder`
|
||||
* @region: (nullable): the region to update
|
||||
*
|
||||
* Sets the region to be updated by this texture. Together with
|
||||
* [property@Gdk.GLTextureBuilder:update-texture] this describes an
|
||||
* update of a previous texture.
|
||||
*
|
||||
* When rendering animations of large textures, it is possible that
|
||||
* consecutive textures are only updating contents in parts of the texture.
|
||||
* It is then possible to describe this update via these two properties,
|
||||
* so that GTK can avoid rerendering parts that did not change.
|
||||
*
|
||||
* An example would be a screen recording where only the mouse pointer moves.
|
||||
*
|
||||
* Since: 4.12
|
||||
*/
|
||||
void
|
||||
gdk_gl_texture_builder_set_update_region (GdkGLTextureBuilder *self,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
g_return_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self));
|
||||
|
||||
if (self->update_region == region)
|
||||
return;
|
||||
|
||||
g_clear_pointer (&self->update_region, cairo_region_destroy);
|
||||
|
||||
if (region)
|
||||
self->update_region = cairo_region_reference (region);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_UPDATE_REGION]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_gl_texture_builder_build:
|
||||
* @self: a `GdkGLTextureBuilder`
|
||||
|
||||
@@ -78,6 +78,18 @@ GDK_AVAILABLE_IN_4_12
|
||||
void gdk_gl_texture_builder_set_sync (GdkGLTextureBuilder *self,
|
||||
gpointer sync);
|
||||
|
||||
GDK_AVAILABLE_IN_4_12
|
||||
GdkTexture * gdk_gl_texture_builder_get_update_texture (GdkGLTextureBuilder *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_4_12
|
||||
void gdk_gl_texture_builder_set_update_texture (GdkGLTextureBuilder *self,
|
||||
GdkTexture *texture);
|
||||
|
||||
GDK_AVAILABLE_IN_4_12
|
||||
cairo_region_t * gdk_gl_texture_builder_get_update_region (GdkGLTextureBuilder *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_4_12
|
||||
void gdk_gl_texture_builder_set_update_region (GdkGLTextureBuilder *self,
|
||||
cairo_region_t *region);
|
||||
|
||||
GDK_AVAILABLE_IN_4_12
|
||||
GdkTexture * gdk_gl_texture_builder_build (GdkGLTextureBuilder *self,
|
||||
GDestroyNotify destroy,
|
||||
|
||||
+43
-1
@@ -282,6 +282,8 @@ gdk_texture_dispose (GObject *object)
|
||||
{
|
||||
GdkTexture *self = GDK_TEXTURE (object);
|
||||
|
||||
g_clear_pointer (&self->diff_to_previous, cairo_region_destroy);
|
||||
|
||||
gdk_texture_clear_render_data (self);
|
||||
|
||||
G_OBJECT_CLASS (gdk_texture_parent_class)->dispose (object);
|
||||
@@ -671,7 +673,47 @@ gdk_texture_do_download (GdkTexture *texture,
|
||||
guchar *data,
|
||||
gsize stride)
|
||||
{
|
||||
GDK_TEXTURE_GET_CLASS (texture)->download (texture, format, data,stride);
|
||||
GDK_TEXTURE_GET_CLASS (texture)->download (texture, format, data, stride);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_texture_diff (GdkTexture *self,
|
||||
GdkTexture *other,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
if (self == other)
|
||||
return;
|
||||
|
||||
if (self->previous_texture == other &&
|
||||
g_atomic_pointer_get (&other->next_texture) == self)
|
||||
{
|
||||
cairo_region_union (region, self->diff_to_previous);
|
||||
}
|
||||
else if (other->previous_texture == self &&
|
||||
g_atomic_pointer_get (&self->next_texture) == other)
|
||||
{
|
||||
cairo_region_union (region, other->diff_to_previous);
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_region_union_rectangle (region,
|
||||
&(cairo_rectangle_int_t) {
|
||||
0,
|
||||
0,
|
||||
MAX (self->width, other->width),
|
||||
MAX (self->height, other->height)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gdk_texture_set_diff (GdkTexture *self,
|
||||
GdkTexture *previous,
|
||||
cairo_region_t *diff)
|
||||
{
|
||||
self->previous_texture = previous;
|
||||
self->diff_to_previous = diff;
|
||||
g_atomic_pointer_set (&previous->next_texture, self);
|
||||
}
|
||||
|
||||
cairo_surface_t *
|
||||
|
||||
@@ -21,6 +21,12 @@ struct _GdkTexture
|
||||
gpointer render_key;
|
||||
gpointer render_data;
|
||||
GDestroyNotify render_notify;
|
||||
|
||||
/* for diffing swapchain-like textures.
|
||||
* Links are only valid if both textures agree on them */
|
||||
gpointer next_texture; /* atomic, no reference, may be invalid pointer */
|
||||
gpointer previous_texture; /* no reference, may be invalid pointer */
|
||||
cairo_region_t *diff_to_previous;
|
||||
};
|
||||
|
||||
struct _GdkTextureClass {
|
||||
@@ -42,6 +48,14 @@ void gdk_texture_do_download (GdkTexture
|
||||
GdkMemoryFormat format,
|
||||
guchar *data,
|
||||
gsize stride);
|
||||
void gdk_texture_diff (GdkTexture *self,
|
||||
GdkTexture *other,
|
||||
cairo_region_t *region);
|
||||
|
||||
void gdk_texture_set_diff (GdkTexture *self,
|
||||
GdkTexture *previous,
|
||||
cairo_region_t *diff);
|
||||
|
||||
gboolean gdk_texture_set_render_data (GdkTexture *self,
|
||||
gpointer key,
|
||||
gpointer data,
|
||||
|
||||
+39
-7
@@ -1650,12 +1650,28 @@ gsk_texture_node_diff (GskRenderNode *node1,
|
||||
{
|
||||
GskTextureNode *self1 = (GskTextureNode *) node1;
|
||||
GskTextureNode *self2 = (GskTextureNode *) node2;
|
||||
cairo_region_t *sub;
|
||||
|
||||
if (graphene_rect_equal (&node1->bounds, &node2->bounds) &&
|
||||
self1->texture == self2->texture)
|
||||
if (!graphene_rect_equal (&node1->bounds, &node2->bounds) ||
|
||||
gdk_texture_get_width (self1->texture) != gdk_texture_get_width (self2->texture) ||
|
||||
gdk_texture_get_height (self1->texture) != gdk_texture_get_height (self2->texture))
|
||||
{
|
||||
gsk_render_node_diff_impossible (node1, node2, region);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self1->texture == self2->texture)
|
||||
return;
|
||||
|
||||
gsk_render_node_diff_impossible (node1, node2, region);
|
||||
sub = cairo_region_create ();
|
||||
gdk_texture_diff (self1->texture, self2->texture, sub);
|
||||
region_union_region_affine (region,
|
||||
sub,
|
||||
node1->bounds.size.width / gdk_texture_get_width (self1->texture),
|
||||
node1->bounds.size.height / gdk_texture_get_height (self1->texture),
|
||||
node1->bounds.origin.x,
|
||||
node1->bounds.origin.y);
|
||||
cairo_region_destroy (sub);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1828,13 +1844,29 @@ gsk_texture_scale_node_diff (GskRenderNode *node1,
|
||||
{
|
||||
GskTextureScaleNode *self1 = (GskTextureScaleNode *) node1;
|
||||
GskTextureScaleNode *self2 = (GskTextureScaleNode *) node2;
|
||||
cairo_region_t *sub;
|
||||
|
||||
if (graphene_rect_equal (&node1->bounds, &node2->bounds) &&
|
||||
self1->texture == self2->texture &&
|
||||
self1->filter == self2->filter)
|
||||
if (!graphene_rect_equal (&node1->bounds, &node2->bounds) ||
|
||||
self1->filter != self2->filter ||
|
||||
gdk_texture_get_width (self1->texture) != gdk_texture_get_width (self2->texture) ||
|
||||
gdk_texture_get_height (self1->texture) != gdk_texture_get_height (self2->texture))
|
||||
{
|
||||
gsk_render_node_diff_impossible (node1, node2, region);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self1->texture == self2->texture)
|
||||
return;
|
||||
|
||||
gsk_render_node_diff_impossible (node1, node2, region);
|
||||
sub = cairo_region_create ();
|
||||
gdk_texture_diff (self1->texture, self2->texture, sub);
|
||||
region_union_region_affine (region,
|
||||
sub,
|
||||
node1->bounds.size.width / gdk_texture_get_width (self1->texture),
|
||||
node1->bounds.size.height / gdk_texture_get_height (self1->texture),
|
||||
node1->bounds.origin.x,
|
||||
node1->bounds.origin.y);
|
||||
cairo_region_destroy (sub);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -1371,7 +1371,10 @@ parse_shader (GtkCssParser *parser,
|
||||
GskGLShader *shader;
|
||||
|
||||
if (!parse_string (parser, context, &sourcecode))
|
||||
return FALSE;
|
||||
{
|
||||
gtk_css_parser_error_value (parser, "Not a string");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bytes = g_bytes_new_take (sourcecode, strlen (sourcecode));
|
||||
shader = gsk_gl_shader_new_from_bytes (bytes);
|
||||
@@ -1500,6 +1503,29 @@ parse_shader_args (GtkCssParser *parser,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static const char default_glsl[] =
|
||||
"void\n"
|
||||
"mainImage(out vec4 fragColor,\n"
|
||||
" in vec2 fragCoord,\n"
|
||||
" in vec2 resolution,\n"
|
||||
" in vec2 uv)\n"
|
||||
"{\n"
|
||||
" fragColor = vec4(1.0, 105.0/255.0, 180.0/255.0, 1.0);\n"
|
||||
"}";
|
||||
|
||||
static GskGLShader *
|
||||
get_default_glshader (void)
|
||||
{
|
||||
GBytes *bytes;
|
||||
GskGLShader *shader;
|
||||
|
||||
bytes = g_bytes_new (default_glsl, strlen (default_glsl) + 1);
|
||||
shader = gsk_gl_shader_new_from_bytes (bytes);
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
parse_glshader_node (GtkCssParser *parser,
|
||||
Context *context)
|
||||
@@ -1520,6 +1546,7 @@ parse_glshader_node (GtkCssParser *parser,
|
||||
{ "child4", parse_node, clear_node, &child[3] },
|
||||
};
|
||||
GskGLShader *shader;
|
||||
GskShaderArgsBuilder *builder;
|
||||
GskRenderNode *node;
|
||||
GBytes *args = NULL;
|
||||
int len, i;
|
||||
@@ -1532,8 +1559,17 @@ parse_glshader_node (GtkCssParser *parser,
|
||||
break;
|
||||
}
|
||||
|
||||
shader = shader_info.shader;
|
||||
args = gsk_shader_args_builder_free_to_args (shader_info.args);
|
||||
if (shader_info.shader)
|
||||
shader = shader_info.shader;
|
||||
else
|
||||
shader = get_default_glshader ();
|
||||
|
||||
if (shader_info.args)
|
||||
builder = shader_info.args;
|
||||
else
|
||||
builder = gsk_shader_args_builder_new (shader, NULL);
|
||||
|
||||
args = gsk_shader_args_builder_free_to_args (builder);
|
||||
|
||||
node = gsk_gl_shader_node_new (shader, &bounds, args, child, len);
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ gtk_column_view_column_dispose (GObject *object)
|
||||
g_clear_object (&self->sorter);
|
||||
g_clear_pointer (&self->title, g_free);
|
||||
g_clear_object (&self->menu);
|
||||
g_clear_pointer (&self->id, g_free);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_column_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
@@ -770,6 +770,20 @@ gtk_gl_area_snapshot (GtkWidget *widget,
|
||||
|
||||
texture = priv->texture;
|
||||
priv->texture = NULL;
|
||||
|
||||
if (priv->textures)
|
||||
gdk_gl_texture_builder_set_update_texture (texture->builder, ((Texture *) priv->textures->data)->holder);
|
||||
{
|
||||
cairo_region_t *region = cairo_region_create ();
|
||||
cairo_region_union_rectangle (region, &(cairo_rectangle_int_t) { 0, 0, 300, 300 });
|
||||
cairo_region_union_rectangle (region, &(cairo_rectangle_int_t) { 0, 600, 300, 300 });
|
||||
cairo_region_union_rectangle (region, &(cairo_rectangle_int_t) { 300, 300, 300, 300 });
|
||||
cairo_region_union_rectangle (region, &(cairo_rectangle_int_t) { 600, 0, 300, 300 });
|
||||
cairo_region_union_rectangle (region, &(cairo_rectangle_int_t) { 600, 600, 300, 300 });
|
||||
gdk_gl_texture_builder_set_update_region (texture->builder, region);
|
||||
cairo_region_destroy (region);
|
||||
}
|
||||
|
||||
priv->textures = g_list_prepend (priv->textures, texture);
|
||||
|
||||
if (gdk_gl_context_has_sync (priv->context))
|
||||
@@ -781,6 +795,7 @@ gtk_gl_area_snapshot (GtkWidget *widget,
|
||||
release_texture,
|
||||
texture);
|
||||
|
||||
gdk_gl_texture_builder_set_update_texture (texture->builder, NULL);
|
||||
/* Our texture is rendered by OpenGL, so it is upside down,
|
||||
* compared to what GSK expects, so flip it back.
|
||||
*/
|
||||
|
||||
@@ -4466,6 +4466,11 @@ update_window_style_classes (GtkWindow *window)
|
||||
|
||||
if (!priv->edge_constraints)
|
||||
{
|
||||
gtk_widget_remove_css_class (widget, "tiled-top");
|
||||
gtk_widget_remove_css_class (widget, "tiled-right");
|
||||
gtk_widget_remove_css_class (widget, "tiled-bottom");
|
||||
gtk_widget_remove_css_class (widget, "tiled-left");
|
||||
|
||||
if (priv->tiled)
|
||||
gtk_widget_add_css_class (widget, "tiled");
|
||||
else
|
||||
@@ -4473,6 +4478,8 @@ update_window_style_classes (GtkWindow *window)
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_remove_css_class (widget, "tiled");
|
||||
|
||||
if (edge_constraints & GDK_TOPLEVEL_STATE_TOP_TILED)
|
||||
gtk_widget_add_css_class (widget, "tiled-top");
|
||||
else
|
||||
|
||||
@@ -8,6 +8,7 @@ static char *arg_output_dir = NULL;
|
||||
static gboolean flip = FALSE;
|
||||
static gboolean rotate = FALSE;
|
||||
static gboolean repeat = FALSE;
|
||||
static gboolean mask = FALSE;
|
||||
|
||||
static const char *
|
||||
get_output_dir (void)
|
||||
@@ -156,7 +157,8 @@ static const GOptionEntry options[] = {
|
||||
{ "output", 0, 0, G_OPTION_ARG_FILENAME, &arg_output_dir, "Directory to save image files to", "DIR" },
|
||||
{ "flip", 0, 0, G_OPTION_ARG_NONE, &flip, "Do flipped test", NULL },
|
||||
{ "rotate", 0, 0, G_OPTION_ARG_NONE, &rotate, "Do rotated test", NULL },
|
||||
{ "repeat", 0, 0, G_OPTION_ARG_NONE, &repeat, "Do repeat test", NULL },
|
||||
{ "repeat", 0, 0, G_OPTION_ARG_NONE, &repeat, "Do repeated test", NULL },
|
||||
{ "mask", 0, 0, G_OPTION_ARG_NONE, &mask, "Do masked test", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@@ -186,6 +188,28 @@ load_node_file (const char *node_file)
|
||||
return node;
|
||||
}
|
||||
|
||||
static GdkPixbuf *
|
||||
apply_mask_to_pixbuf (GdkPixbuf *pixbuf)
|
||||
{
|
||||
GdkPixbuf *copy;
|
||||
|
||||
copy = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
|
||||
for (unsigned int j = 0; j < gdk_pixbuf_get_height (copy); j++)
|
||||
{
|
||||
guint8 *row = gdk_pixbuf_get_pixels (copy) + j * gdk_pixbuf_get_rowstride (copy);
|
||||
for (unsigned int i = 0; i < gdk_pixbuf_get_width (copy); i++)
|
||||
{
|
||||
guint8 *p = row + i * 4;
|
||||
if ((i < 25 && j >= 25) || (i >= 25 && j < 25))
|
||||
{
|
||||
p[0] = p[1] = p[2] = p[3] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-option arguments:
|
||||
* 1) .node file to compare
|
||||
@@ -408,6 +432,52 @@ main (int argc, char **argv)
|
||||
gsk_render_node_unref (node2);
|
||||
}
|
||||
|
||||
if (mask)
|
||||
{
|
||||
GskRenderNode *node2;
|
||||
GdkPixbuf *pixbuf, *pixbuf2;
|
||||
graphene_rect_t bounds;
|
||||
GskRenderNode *mask_node;
|
||||
GskRenderNode *nodes[2];
|
||||
|
||||
gsk_render_node_get_bounds (node, &bounds);
|
||||
nodes[0] = gsk_color_node_new (&(GdkRGBA){ 0, 0, 0, 1},
|
||||
&GRAPHENE_RECT_INIT (bounds.origin.x, bounds.origin.y, 25, 25));
|
||||
nodes[1] = gsk_color_node_new (&(GdkRGBA){ 0, 0, 0, 1},
|
||||
&GRAPHENE_RECT_INIT (bounds.origin.x + 25, bounds.origin.y + 25, bounds.size.width - 25, bounds.size.height - 25));
|
||||
|
||||
mask_node = gsk_container_node_new (nodes, G_N_ELEMENTS (nodes));
|
||||
node2 = gsk_mask_node_new (node, mask_node, GSK_MASK_MODE_ALPHA);
|
||||
gsk_render_node_unref (mask_node);
|
||||
gsk_render_node_unref (nodes[0]);
|
||||
gsk_render_node_unref (nodes[1]);
|
||||
rendered_texture = gsk_renderer_render_texture (renderer, node2, NULL);
|
||||
|
||||
save_image (rendered_texture, node_file, "-masked.out.png");
|
||||
|
||||
pixbuf = gdk_pixbuf_new_from_file (png_file, &error);
|
||||
pixbuf2 = apply_mask_to_pixbuf (pixbuf);
|
||||
reference_texture = gdk_texture_new_for_pixbuf (pixbuf2);
|
||||
g_object_unref (pixbuf2);
|
||||
g_object_unref (pixbuf);
|
||||
|
||||
save_image (reference_texture, node_file, "-masked.ref.png");
|
||||
|
||||
diff_texture = reftest_compare_textures (rendered_texture, reference_texture);
|
||||
|
||||
if (diff_texture)
|
||||
{
|
||||
save_node (node2, node_file, "-masked.node");
|
||||
save_image (diff_texture, node_file, "-masked.diff.png");
|
||||
g_object_unref (diff_texture);
|
||||
success = FALSE;
|
||||
}
|
||||
|
||||
g_clear_object (&rendered_texture);
|
||||
g_clear_object (&reference_texture);
|
||||
gsk_render_node_unref (node2);
|
||||
}
|
||||
|
||||
gsk_render_node_unref (node);
|
||||
|
||||
return success ? 0 : 1;
|
||||
|
||||
@@ -101,6 +101,7 @@ informative_render_tests = [
|
||||
|
||||
renderers = [
|
||||
{ 'name': 'gl' },
|
||||
{ 'name': 'vulkan' },
|
||||
{ 'name': 'broadway', 'exclude_term': '-3d' },
|
||||
{ 'name': 'cairo', 'exclude_term': '-3d' },
|
||||
]
|
||||
@@ -131,7 +132,7 @@ foreach renderer : renderers
|
||||
'gsk-compare-' + renderer_name,
|
||||
]
|
||||
|
||||
if compare_xfails.contains(testname)
|
||||
if compare_xfails.contains(testname) or renderer_name == 'vulkan'
|
||||
suites += 'failing'
|
||||
endif
|
||||
|
||||
@@ -140,7 +141,8 @@ foreach renderer : renderers
|
||||
endif
|
||||
|
||||
if ((exclude_term == '' or not testname.contains(exclude_term)) and
|
||||
(renderer_name != 'broadway' or broadway_enabled))
|
||||
(renderer_name != 'broadway' or broadway_enabled) and
|
||||
(renderer_name != 'vulkan' or have_vulkan))
|
||||
test(renderer_name + ' ' + testname, compare_render,
|
||||
args: [
|
||||
'--output', join_paths(meson.current_build_dir(), 'compare', renderer_name),
|
||||
@@ -200,6 +202,21 @@ foreach renderer : renderers
|
||||
],
|
||||
suite: suites + [ 'gsk-compare-rotated-' + renderer_name ],
|
||||
)
|
||||
test(renderer_name + ' ' + testname + ' masked', compare_render,
|
||||
args: [
|
||||
'--mask',
|
||||
'--output', join_paths(meson.current_build_dir(), 'compare', renderer_name),
|
||||
join_paths(meson.current_source_dir(), 'compare', testname + '.node'),
|
||||
join_paths(meson.current_source_dir(), 'compare', testname + '.png'),
|
||||
],
|
||||
env: [
|
||||
'GSK_RENDERER=' + renderer_name,
|
||||
'GTK_A11Y=test',
|
||||
'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
|
||||
'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir())
|
||||
],
|
||||
suite: suites + [ 'gsk-compare-masked-' + renderer_name ],
|
||||
)
|
||||
endif
|
||||
endforeach
|
||||
endforeach
|
||||
|
||||
@@ -24,12 +24,17 @@ loop = None
|
||||
|
||||
def quit_cb(loop):
|
||||
loop.quit()
|
||||
print('timed out while waiting')
|
||||
|
||||
def wait(millis):
|
||||
global loop
|
||||
before = GLib.get_monotonic_time()
|
||||
loop = GLib.MainLoop()
|
||||
GLib.timeout_add(millis, quit_cb, loop)
|
||||
loop.run()
|
||||
if verbose:
|
||||
time = (GLib.get_monotonic_time() - before) / 1000
|
||||
print(f'waited for {time} milliseconds')
|
||||
|
||||
display = None
|
||||
window = None
|
||||
@@ -42,10 +47,10 @@ def key_pressed_cb (controller, keyval, keycode, state):
|
||||
|
||||
if verbose:
|
||||
print(f'got key press: {keyval}, state {state}')
|
||||
assert expected_change != None, "Unexpected key press"
|
||||
assert expected_change['type'] == 'press', "Key press event expected"
|
||||
assert keyval == expected_change['keyval'], "Unexpected keyval in key press event"
|
||||
assert state == expected_change['state'], "Unexpected state in key press event"
|
||||
assert expected_change != None, 'Unexpected key press'
|
||||
assert expected_change['type'] == 'press', 'Key press event expected'
|
||||
assert keyval == expected_change['keyval'], 'Unexpected keyval in key press event'
|
||||
assert state == expected_change['state'], 'Unexpected state in key press event'
|
||||
|
||||
expected_change = None
|
||||
loop.quit()
|
||||
@@ -56,10 +61,10 @@ def key_released_cb (controller, keyval, keycode, state):
|
||||
|
||||
if verbose:
|
||||
print(f'got key release: {keyval}, state {state}')
|
||||
assert expected_change != None, "Unexpected key release"
|
||||
assert expected_change['type'] == 'release', "Key release event expected"
|
||||
assert keyval == expected_change['keyval'], "Unexpected keyval in key release event"
|
||||
assert state == expected_change['state'], "Unexpected state in key release event"
|
||||
assert expected_change != None, 'Unexpected key release'
|
||||
assert expected_change['type'] == 'release', 'Key release event expected'
|
||||
assert keyval == expected_change['keyval'], 'Unexpected keyval in key release event'
|
||||
assert state == expected_change['state'], 'Unexpected state in key release event'
|
||||
|
||||
expected_change = None
|
||||
loop.quit()
|
||||
@@ -71,9 +76,9 @@ def motion_cb (controller, x, y):
|
||||
if verbose:
|
||||
print(f'got motion: {x}, {y}')
|
||||
if expected_change != None:
|
||||
assert expected_change['type'] == 'motion', "Motion event expected"
|
||||
assert x == expected_change['x'], "Unexpected x coord in motion event"
|
||||
assert y == expected_change['y'], "Unexpected y coord in motion event"
|
||||
assert expected_change['type'] == 'motion', 'Motion event expected'
|
||||
assert x == expected_change['x'], 'Unexpected x coord in motion event'
|
||||
assert y == expected_change['y'], 'Unexpected y coord in motion event'
|
||||
expected_change = None
|
||||
loop.quit()
|
||||
|
||||
@@ -83,10 +88,10 @@ def enter_cb (controller, x, y):
|
||||
|
||||
if verbose:
|
||||
print(f'got enter: {x}, {y}')
|
||||
assert expected_change != None, "Unexpected enter"
|
||||
assert expected_change['type'] == 'enter', "Enter event expected"
|
||||
assert x == expected_change['x'], "Unexpected x coord in enter event"
|
||||
assert y == expected_change['y'], "Unexpected y coord in enter event"
|
||||
assert expected_change != None, 'Unexpected enter'
|
||||
assert expected_change['type'] == 'enter', 'Enter event expected'
|
||||
assert x == expected_change['x'], 'Unexpected x coord in enter event'
|
||||
assert y == expected_change['y'], 'Unexpected y coord in enter event'
|
||||
|
||||
expected_change = None
|
||||
loop.quit()
|
||||
@@ -97,11 +102,11 @@ def pressed_cb(controller, n, x, y):
|
||||
|
||||
if verbose:
|
||||
print(f'got pressed')
|
||||
assert expected_change != None, "Unexpected event"
|
||||
assert expected_change['type'] == 'press', "Button press expected"
|
||||
assert expected_change['button'] == controller.get_current_button(), "Unexpected button pressed"
|
||||
assert x == expected_change['x'], "Unexpected x coord in motion event"
|
||||
assert y == expected_change['y'], "Unexpected y coord in motion event"
|
||||
assert expected_change != None, 'Unexpected event'
|
||||
assert expected_change['type'] == 'press', 'Button press expected'
|
||||
assert expected_change['button'] == controller.get_current_button(), 'Unexpected button pressed'
|
||||
assert x == expected_change['x'], 'Unexpected x coord in motion event'
|
||||
assert y == expected_change['y'], 'Unexpected y coord in motion event'
|
||||
|
||||
expected_change = None
|
||||
loop.quit()
|
||||
@@ -112,8 +117,8 @@ def released_cb(controller, n, x, y):
|
||||
|
||||
if verbose:
|
||||
print(f'got released')
|
||||
assert expected_change != None, "Unexpected event"
|
||||
assert expected_change['type'] == 'release', "Button release expected"
|
||||
assert expected_change != None, 'Unexpected event'
|
||||
assert expected_change['type'] == 'release', 'Button release expected'
|
||||
|
||||
expected_change = None
|
||||
loop.quit()
|
||||
@@ -126,7 +131,7 @@ def expect_key_press(keyval, state, timeout):
|
||||
'state' : state
|
||||
}
|
||||
wait(timeout)
|
||||
assert expected_change == None, "Expected event did not happen"
|
||||
assert expected_change == None, 'Expected event did not happen'
|
||||
|
||||
def expect_key_release(keyval, state, timeout):
|
||||
global expected_change
|
||||
@@ -136,7 +141,7 @@ def expect_key_release(keyval, state, timeout):
|
||||
'state' : state
|
||||
}
|
||||
wait(timeout)
|
||||
assert expected_change == None, "Expected event did not happen"
|
||||
assert expected_change == None, 'Expected event did not happen'
|
||||
|
||||
def expect_motion(x, y, timeout):
|
||||
global expected_change
|
||||
@@ -146,7 +151,7 @@ def expect_motion(x, y, timeout):
|
||||
'y' : y
|
||||
}
|
||||
wait(timeout)
|
||||
assert expected_change == None, "Expected event did not happen"
|
||||
assert expected_change == None, 'Expected event did not happen'
|
||||
|
||||
def expect_enter(x, y, timeout):
|
||||
global expected_change
|
||||
@@ -156,7 +161,7 @@ def expect_enter(x, y, timeout):
|
||||
'y' : y
|
||||
}
|
||||
wait(timeout)
|
||||
assert expected_change == None, "Expected event did not happen"
|
||||
assert expected_change == None, 'Expected event did not happen'
|
||||
|
||||
def expect_button_press(button, x, y, timeout):
|
||||
global expected_change
|
||||
@@ -167,7 +172,7 @@ def expect_button_press(button, x, y, timeout):
|
||||
'y' : y
|
||||
}
|
||||
wait(timeout)
|
||||
assert expected_change == None, "Button press did not arrive"
|
||||
assert expected_change == None, 'Button press did not arrive'
|
||||
|
||||
def expect_button_release(button, x, y, timeout):
|
||||
global expected_change
|
||||
@@ -178,7 +183,7 @@ def expect_button_release(button, x, y, timeout):
|
||||
'y' : y
|
||||
}
|
||||
wait(timeout)
|
||||
assert expected_change == None, "Button release did not arrive"
|
||||
assert expected_change == None, 'Button release did not arrive'
|
||||
|
||||
def got_active(object, pspec):
|
||||
global loop
|
||||
@@ -219,11 +224,11 @@ def launch_observer():
|
||||
window.maximize()
|
||||
window.present()
|
||||
|
||||
wait(500)
|
||||
wait(2000)
|
||||
|
||||
assert window.is_active(), "Observer not active"
|
||||
assert window.get_width() == 1024, "Window not maximized"
|
||||
assert window.get_height() == 768, "Window not maximized"
|
||||
assert window.is_active(), 'Observer not active'
|
||||
assert window.get_width() == 1024, 'Window not maximized'
|
||||
assert window.get_height() == 768, 'Window not maximized'
|
||||
|
||||
# we need to wait out the map animation, or pointer coords will be off
|
||||
wait(1000)
|
||||
@@ -264,7 +269,7 @@ def stop_observer():
|
||||
window = None
|
||||
|
||||
def expect_entry_text(text):
|
||||
assert text == entry.get_text(), "Unexpected entry text: " + entry.get_text()
|
||||
assert text == entry.get_text(), 'Unexpected entry text: ' + entry.get_text()
|
||||
|
||||
def key_press(keyval):
|
||||
if verbose:
|
||||
@@ -299,6 +304,9 @@ def pointer_move(x, y):
|
||||
|
||||
def basic_keyboard_tests():
|
||||
try:
|
||||
if verbose:
|
||||
print('Starting basic keyboard tests')
|
||||
|
||||
launch_observer()
|
||||
|
||||
key_press(Gdk.KEY_a)
|
||||
@@ -321,7 +329,7 @@ def basic_keyboard_tests():
|
||||
|
||||
stop_observer()
|
||||
except AssertionError as e:
|
||||
print("Error in basic_keyboard_tests: {0}".format(e))
|
||||
print(f'Error in basic_keyboard_tests: {e}')
|
||||
terminate()
|
||||
|
||||
def quick_typing_test():
|
||||
@@ -338,15 +346,18 @@ def quick_typing_test():
|
||||
key_release(Gdk.KEY_t)
|
||||
|
||||
wait(100)
|
||||
expect_entry_text("Test")
|
||||
expect_entry_text('Test')
|
||||
|
||||
stop_observer()
|
||||
except AssertionError as e:
|
||||
print("Error in quick_typing_test: {0}".format(e))
|
||||
print(f'Error in quick_typing_test: {e}')
|
||||
terminate()
|
||||
|
||||
def basic_pointer_tests():
|
||||
try:
|
||||
if verbose:
|
||||
print('Starting basic pointer tests')
|
||||
|
||||
pointer_move(-100.0, -100.0)
|
||||
launch_observer()
|
||||
|
||||
@@ -368,7 +379,7 @@ def basic_pointer_tests():
|
||||
|
||||
stop_observer()
|
||||
except AssertionError as e:
|
||||
print("Error in basic_pointer_tests: {0}".format(e))
|
||||
print(f'Error in basic_pointer_tests: {e}')
|
||||
terminate()
|
||||
|
||||
ds_window = None
|
||||
@@ -379,9 +390,9 @@ def drag_begin(controller, drag):
|
||||
global loop
|
||||
|
||||
if verbose:
|
||||
print(f'got drag begin')
|
||||
assert expected_change != None, "Unexpected drag begin"
|
||||
assert expected_change['type'] == 'drag', "Drag begin expected"
|
||||
print('got drag begin')
|
||||
assert expected_change != None, 'Unexpected drag begin'
|
||||
assert expected_change['type'] == 'drag', 'Drag begin expected'
|
||||
|
||||
expected_change = None
|
||||
loop.quit()
|
||||
@@ -415,11 +426,11 @@ def launch_drag_source(value):
|
||||
ds_window.maximize()
|
||||
ds_window.present()
|
||||
|
||||
wait(500)
|
||||
wait(2000)
|
||||
|
||||
assert ds_window.is_active(), "drag source not active"
|
||||
assert ds_window.get_width() == 1024, "Window not maximized"
|
||||
assert ds_window.get_height() == 768, "Window not maximized"
|
||||
assert ds_window.is_active(), 'drag source not active'
|
||||
assert ds_window.get_width() == 1024, 'Window not maximized'
|
||||
assert ds_window.get_height() == 768, 'Window not maximized'
|
||||
|
||||
# we need to wait out the map animation, or pointer coords will be off
|
||||
wait(1000)
|
||||
@@ -437,9 +448,9 @@ def do_drop(controller, value, x, y):
|
||||
|
||||
if verbose:
|
||||
print(f'got drop {value}')
|
||||
assert expected_change != None, "Unexpected drop begin"
|
||||
assert expected_change['type'] == 'drop', "Drop expected"
|
||||
assert expected_change['value'] == value, "Unexpected value dropped"
|
||||
assert expected_change != None, 'Unexpected drop begin'
|
||||
assert expected_change['type'] == 'drop', 'Drop expected'
|
||||
assert expected_change['value'] == value, 'Unexpected value dropped'
|
||||
|
||||
expected_change = None
|
||||
loop.quit()
|
||||
@@ -465,11 +476,11 @@ def launch_drop_target():
|
||||
dt_window.maximize()
|
||||
dt_window.present()
|
||||
|
||||
wait(500)
|
||||
wait(2000)
|
||||
|
||||
assert dt_window.is_active(), "drop target not active"
|
||||
assert dt_window.get_width() == 1024, "Window not maximized"
|
||||
assert dt_window.get_height() == 768, "Window not maximized"
|
||||
assert dt_window.is_active(), 'drop target not active'
|
||||
assert dt_window.get_width() == 1024, 'Window not maximized'
|
||||
assert dt_window.get_height() == 768, 'Window not maximized'
|
||||
|
||||
# we need to wait out the map animation, or pointer coords will be off
|
||||
wait(1000)
|
||||
@@ -485,7 +496,7 @@ def expect_drag(timeout):
|
||||
'type' : 'drag',
|
||||
}
|
||||
wait(timeout)
|
||||
assert expected_change == None, "DND operation not started"
|
||||
assert expected_change == None, 'DND operation not started'
|
||||
|
||||
def expect_drop(value, timeout):
|
||||
global expected_change
|
||||
@@ -494,10 +505,13 @@ def expect_drop(value, timeout):
|
||||
'value' : value
|
||||
}
|
||||
wait(timeout)
|
||||
assert expected_change == None, "Drop has not happened"
|
||||
assert expected_change == None, 'Drop has not happened'
|
||||
|
||||
def dnd_tests():
|
||||
try:
|
||||
if verbose:
|
||||
print('Starting dnd tests')
|
||||
|
||||
pointer_move(-100, -100)
|
||||
|
||||
launch_drag_source('abc')
|
||||
@@ -511,7 +525,7 @@ def dnd_tests():
|
||||
wait(150)
|
||||
|
||||
pointer_move(120, 150)
|
||||
expect_drag(timeout=1000)
|
||||
expect_drag(timeout=2000)
|
||||
|
||||
launch_drop_target()
|
||||
wait(100);
|
||||
@@ -521,12 +535,18 @@ def dnd_tests():
|
||||
stop_drop_target()
|
||||
stop_drag_source()
|
||||
except AssertionError as e:
|
||||
print("Error in dnd_tests: {0}".format(e))
|
||||
print(f'Error in dnd_tests: {e}')
|
||||
terminate()
|
||||
|
||||
def session_closed_cb():
|
||||
print('Session closed')
|
||||
|
||||
def run_commands():
|
||||
basic_keyboard_tests()
|
||||
basic_pointer_tests()
|
||||
dnd_tests()
|
||||
quick_typing_test()
|
||||
|
||||
def mutter_appeared(name):
|
||||
global remote_desktop
|
||||
global session
|
||||
@@ -534,13 +554,13 @@ def mutter_appeared(name):
|
||||
global done
|
||||
|
||||
if verbose:
|
||||
print("mutter appeared on the bus")
|
||||
print('mutter appeared on the bus')
|
||||
|
||||
remote_desktop = bus.get('org.gnome.Mutter.RemoteDesktop',
|
||||
'/org/gnome/Mutter/RemoteDesktop')
|
||||
device_types = remote_desktop.Get('org.gnome.Mutter.RemoteDesktop', 'SupportedDeviceTypes')
|
||||
assert device_types & 1 == 1, "No keyboard"
|
||||
assert device_types & 2 == 2, "No pointer"
|
||||
assert device_types & 1 == 1, 'No keyboard'
|
||||
assert device_types & 2 == 2, 'No pointer'
|
||||
|
||||
screen_cast = bus.get('org.gnome.Mutter.ScreenCast',
|
||||
'/org/gnome/Mutter/ScreenCast')
|
||||
@@ -560,20 +580,19 @@ def mutter_appeared(name):
|
||||
key_release(Gdk.KEY_Control_L)
|
||||
pointer_move(-100, -100)
|
||||
|
||||
basic_keyboard_tests()
|
||||
basic_pointer_tests()
|
||||
dnd_tests()
|
||||
quick_typing_test()
|
||||
run_commands()
|
||||
|
||||
session.Stop()
|
||||
|
||||
if verbose:
|
||||
print('Done running commands, exiting...')
|
||||
done = True
|
||||
|
||||
def mutter_vanished():
|
||||
global done
|
||||
if remote_desktop != None:
|
||||
if verbose:
|
||||
print("mutter left the bus")
|
||||
print('mutter left the bus')
|
||||
done = True
|
||||
|
||||
bus = SessionBus()
|
||||
|
||||
@@ -26,6 +26,9 @@ def terminate():
|
||||
|
||||
def stream_added_closure(name):
|
||||
def stream_added(node_id):
|
||||
if verbose:
|
||||
print('pipewire stream added')
|
||||
|
||||
monitor = monitors[name];
|
||||
|
||||
freq = monitor['freq'];
|
||||
@@ -36,7 +39,7 @@ def stream_added_closure(name):
|
||||
# Use gstreamer out-of-process, since the gst gl support gets
|
||||
# itself into a twist with its wayland connection when monitors
|
||||
# disappear
|
||||
pipeline_desc = f'gst-launch-1.0 --verbose pipewiresrc path={node_id} ! video/x-raw,max-framerate={freq}/1,width={width},height={height} ! videoconvert ! glimagesink'
|
||||
pipeline_desc = f'gst-launch-1.0 pipewiresrc path={node_id} ! video/x-raw,max-framerate={freq}/1,width={width},height={height} ! videoconvert ! glimagesink' # >& gstreamer-monitor.log'
|
||||
if verbose:
|
||||
print(f'launching {pipeline_desc}')
|
||||
monitor['pipeline'] = subprocess.Popen([pipeline_desc], shell=True)
|
||||
@@ -49,11 +52,11 @@ def add_monitor(name, width, height, scale, freq):
|
||||
session_path = screen_cast.CreateSession({})
|
||||
session = bus.get('org.gnome.Mutter.ScreenCast', session_path)
|
||||
monitors[name] = {
|
||||
"session": session,
|
||||
"width": width,
|
||||
"height": height,
|
||||
"scale": scale,
|
||||
"freq": freq
|
||||
'session': session,
|
||||
'width': width,
|
||||
'height': height,
|
||||
'scale': scale,
|
||||
'freq': freq
|
||||
}
|
||||
stream_path = session.RecordVirtual({})
|
||||
stream = bus.get('org.gnome.Mutter.ScreenCast', stream_path)
|
||||
@@ -70,7 +73,7 @@ def remove_monitor(name):
|
||||
session = monitor['session']
|
||||
session.Stop()
|
||||
except KeyError:
|
||||
print("failed to remove monitor")
|
||||
print('failed to remove monitor')
|
||||
monitors[name] = None
|
||||
|
||||
expected_change = None
|
||||
@@ -78,23 +81,28 @@ loop = None
|
||||
|
||||
def quit_cb(loop):
|
||||
loop.quit()
|
||||
print('timed out while waiting')
|
||||
|
||||
def wait(millis):
|
||||
global loop
|
||||
before = GLib.get_monotonic_time()
|
||||
loop = GLib.MainLoop()
|
||||
GLib.timeout_add(millis, quit_cb, loop)
|
||||
loop.run()
|
||||
if verbose:
|
||||
time = (GLib.get_monotonic_time() - before) / 1000
|
||||
print(f'waited for {time} milliseconds')
|
||||
|
||||
def monitors_changed(monitors, position, removed, added):
|
||||
global expected_change
|
||||
|
||||
assert expected_change != None, "No change expected"
|
||||
assert position == expected_change['position'], "Unexpected position in monitors-changed"
|
||||
assert removed == expected_change['removed'], "Unexpected removed in monitors-changed"
|
||||
assert added == expected_change['added'], "Unexpected added in monitors-changed"
|
||||
assert expected_change != None, 'No change expected'
|
||||
assert position == expected_change['position'], 'Unexpected position in monitors-changed'
|
||||
assert removed == expected_change['removed'], 'Unexpected removed in monitors-changed'
|
||||
assert added == expected_change['added'], 'Unexpected added in monitors-changed'
|
||||
|
||||
if verbose:
|
||||
print('got expected change')
|
||||
print('got expected monitors-changed signal')
|
||||
|
||||
expected_change = None
|
||||
loop.quit()
|
||||
@@ -110,7 +118,7 @@ def launch_observer():
|
||||
print('launch observer')
|
||||
|
||||
monitor_model = display.get_monitors()
|
||||
assert monitor_model.get_n_items() == 0, "Unexpected initial monitors"
|
||||
assert monitor_model.get_n_items() == 0, 'Unexpected initial monitors'
|
||||
monitor_model.connect('items-changed', monitors_changed)
|
||||
|
||||
def expect_monitors_changed(position, removed, added, timeout):
|
||||
@@ -121,7 +129,7 @@ def expect_monitors_changed(position, removed, added, timeout):
|
||||
'added' : added
|
||||
}
|
||||
wait(timeout)
|
||||
assert expected_change == None, "Expected change did not happen"
|
||||
assert expected_change == None, 'Expected change did not happen'
|
||||
|
||||
def got_connector(monitor, pspec):
|
||||
loop.quit()
|
||||
@@ -130,15 +138,18 @@ def expect_monitor(position, width, height, scale, freq):
|
||||
assert monitor_model.get_n_items() > position, f'Monitor {position} not present'
|
||||
monitor = monitor_model.get_item(position)
|
||||
if monitor.get_connector() == None:
|
||||
if verbose:
|
||||
print('waiting for connector')
|
||||
handler = monitor.connect('notify::connector', got_connector)
|
||||
wait(500)
|
||||
monitor.disconnect(handler)
|
||||
assert monitor.is_valid(), "Monitor is not valid"
|
||||
assert monitor.get_connector() != None, 'Monitor has no connector'
|
||||
assert monitor.is_valid(), 'Monitor is not valid'
|
||||
geometry = monitor.get_geometry()
|
||||
assert geometry.width == width, "Unexpected monitor width"
|
||||
assert geometry.height == height, "Unexpected monitor height"
|
||||
assert monitor.get_scale_factor() == scale, "Unexpected scale factor"
|
||||
assert monitor.get_refresh_rate() == freq, "Unexpected monitor frequency"
|
||||
assert geometry.width == width, 'Unexpected monitor width'
|
||||
assert geometry.height == height, 'Unexpected monitor height'
|
||||
assert monitor.get_scale_factor() == scale, 'Unexpected scale factor'
|
||||
assert monitor.get_refresh_rate() == freq, 'Unexpected monitor frequency'
|
||||
if verbose:
|
||||
print(f'monitor {position}: {geometry.width}x{geometry.height} frequency {monitor.get_refresh_rate()} scale {monitor.get_scale_factor()} model \'{monitor.get_model()}\' connector \'{monitor.get_connector()}\'')
|
||||
|
||||
@@ -146,41 +157,41 @@ def run_commands():
|
||||
try:
|
||||
launch_observer()
|
||||
|
||||
add_monitor("0", width=100, height=100, scale=1, freq=60)
|
||||
expect_monitors_changed(0, 0, 1, 5000)
|
||||
add_monitor('0', width=100, height=100, scale=1, freq=60)
|
||||
expect_monitors_changed(0, 0, 1, 10000)
|
||||
expect_monitor (position=0, width=100, height=100, scale=1, freq=60000)
|
||||
|
||||
add_monitor("1", width=1024, height=768, scale=1, freq=144)
|
||||
expect_monitors_changed(1, 0, 1, 5000)
|
||||
add_monitor('1', width=1024, height=768, scale=1, freq=144)
|
||||
expect_monitors_changed(1, 0, 1, 10000)
|
||||
expect_monitor (position=1, width=1024, height=768, scale=1, freq=144000)
|
||||
|
||||
remove_monitor("0")
|
||||
remove_monitor('0')
|
||||
expect_monitors_changed(0, 1, 0, 11000) # mutter takes 10 seconds to remove it
|
||||
|
||||
remove_monitor("1")
|
||||
remove_monitor('1')
|
||||
expect_monitors_changed(0, 1, 0, 11000)
|
||||
except AssertionError as e:
|
||||
print("Error: {0}".format(e))
|
||||
print(f'Error: {e}')
|
||||
terminate()
|
||||
|
||||
def mutter_appeared(name):
|
||||
global screen_cast
|
||||
global done
|
||||
if verbose:
|
||||
print("mutter appeared on the bus")
|
||||
print('mutter appeared on the bus')
|
||||
screen_cast = bus.get('org.gnome.Mutter.ScreenCast',
|
||||
'/org/gnome/Mutter/ScreenCast')
|
||||
run_commands()
|
||||
|
||||
if verbose:
|
||||
print ("Done running commands, exiting...")
|
||||
print ('Done running commands, exiting...')
|
||||
done = True
|
||||
|
||||
def mutter_vanished():
|
||||
global done
|
||||
if screen_cast != None:
|
||||
if verbose:
|
||||
print("mutter left the bus")
|
||||
print('mutter left the bus')
|
||||
done = True
|
||||
|
||||
bus = SessionBus()
|
||||
|
||||
@@ -5,6 +5,7 @@ env.prepend('GI_TYPELIB_PATH',
|
||||
)
|
||||
env.prepend('LD_PRELOAD', project_build_root / 'gtk' / 'libgtk-4.so')
|
||||
env.prepend('MESON_CURRENT_SOURCE_DIR', meson.current_source_dir())
|
||||
env.prepend('MESON_CURRENT_BUILD_DIR', meson.current_build_dir())
|
||||
|
||||
test('monitor',
|
||||
find_program('run-headless-monitor-tests.sh', dirs: meson.current_source_dir()),
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
#! /bin/sh
|
||||
|
||||
srcdir=${MESON_CURRENT_SOURCE_DIR:-./testsuite/headless}
|
||||
builddir=${MESON_CURRENT_BUILD_DIR:-.}
|
||||
outputdir=${builddir}/input
|
||||
|
||||
dbus-run-session sh <<EOF
|
||||
mkdir -p ${outputdir}
|
||||
|
||||
export XDG_RUNTIME_DIR="$(mktemp -p $(pwd) -d xdg-runtime-XXXXXX)"
|
||||
|
||||
pipewire &
|
||||
pipewire_pid=\$!
|
||||
wireplumber &
|
||||
wireplumber_pid=\$!
|
||||
sleep 1
|
||||
|
||||
#echo DBUS_SESSION_BUS_ADDRESS=\$DBUS_SESSION_BUS_ADDRESS
|
||||
#echo WAYLAND_DISPLAY=gtk-test
|
||||
|
||||
export GTK_A11Y=none
|
||||
export GIO_USE_VFS=local
|
||||
|
||||
mutter --headless --virtual-monitor 1024x768 --no-x11 --wayland-display gtk-test2 >&mutter2.log &
|
||||
dbus-run-session sh 2>${outputdir}/dbus-stderr.log <<EOF
|
||||
|
||||
export XDG_RUNTIME_DIR="$(mktemp -p $(pwd) -d xdg-runtime-XXXXXX)"
|
||||
|
||||
pipewire >&${outputdir}/pipewire.log &
|
||||
pipewire_pid=\$!
|
||||
sleep 2
|
||||
|
||||
wireplumber >&${outputdir}/wireplumber.log &
|
||||
wireplumber_pid=\$!
|
||||
sleep 2
|
||||
|
||||
#echo DBUS_SESSION_BUS_ADDRESS=\$DBUS_SESSION_BUS_ADDRESS
|
||||
#echo WAYLAND_DISPLAY=gtk-test
|
||||
|
||||
mutter --headless --virtual-monitor 1024x768 --no-x11 --wayland-display gtk-test2 >&${outputdir}/mutter.log &
|
||||
mutter_pid=\$!
|
||||
|
||||
sleep 2
|
||||
|
||||
export WAYLAND_DISPLAY=gtk-test2
|
||||
export GDK_BACKEND=wayland
|
||||
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
#! /bin/sh
|
||||
|
||||
srcdir=${MESON_CURRENT_SOURCE_DIR:-./testsuite/headless}
|
||||
builddir=${MESON_CURRENT_BUILD_DIR:-.}
|
||||
outputdir=${builddir}/monitor
|
||||
|
||||
mkdir -p ${outputdir}
|
||||
|
||||
|
||||
export GTK_A11Y=none
|
||||
export GIO_USE_VFS=local
|
||||
|
||||
dbus-run-session sh <<EOF
|
||||
dbus-run-session sh 2>${outputdir}/dbus-stderr.log <<EOF
|
||||
|
||||
export XDG_RUNTIME_DIR="$(mktemp -p $(pwd) -d xdg-runtime-XXXXXX)"
|
||||
|
||||
pipewire &
|
||||
pipewire >&${outputdir}/pipewire.log &
|
||||
pipewire_pid=\$!
|
||||
sleep 2
|
||||
|
||||
wireplumber &
|
||||
wireplumber >&${outputdir}/wireplumber.log &
|
||||
wireplumber_pid=\$!
|
||||
sleep 2
|
||||
|
||||
@@ -22,7 +27,7 @@ sleep 2
|
||||
|
||||
export MUTTER_DEBUG=screen-cast
|
||||
|
||||
mutter --headless --no-x11 --wayland-display gtk-test &
|
||||
mutter --headless --no-x11 --wayland-display gtk-test >&${outputdir}/mutter.log &
|
||||
mutter_pid=\$!
|
||||
|
||||
sleep 2
|
||||
|
||||
Reference in New Issue
Block a user