diff --git a/gdk/broadway/broadway-output.c b/gdk/broadway/broadway-output.c index 6ee3229933..b2b15f8362 100644 --- a/gdk/broadway/broadway-output.c +++ b/gdk/broadway/broadway-output.c @@ -8,6 +8,7 @@ #include "broadway-output.h" //#define DEBUG_NODE_SENDING +//#define DEBUG_NODE_SENDING_REMOVE /************************************************************************ * Basic I/O primitives * @@ -113,7 +114,7 @@ broadway_output_set_next_serial (BroadwayOutput *output, ************************************************************************/ static void -append_char (BroadwayOutput *output, char c) +append_uint8 (BroadwayOutput *output, guint8 c) { g_string_append_c (output->buf, c); } @@ -172,7 +173,7 @@ patch_uint32 (BroadwayOutput *output, guint32 v, gsize offset) static void write_header(BroadwayOutput *output, char op) { - append_char (output, op); + append_uint8 (output, op); append_uint32 (output, output->serial++); } @@ -317,15 +318,25 @@ static void append_type (BroadwayOutput *output, guint32 type, BroadwayNode *node) { #ifdef DEBUG_NODE_SENDING - g_print ("%*s%s", append_node_depth*2, "", broadway_node_type_names[type]); + g_print ("%*s%s(%d/%d)", append_node_depth*2, "", broadway_node_type_names[type], node->id, node->output_id); if (type == BROADWAY_NODE_TEXTURE) - g_print (" %u", node->data[4]); + g_print (" tx=%u", node->data[4]); g_print ("\n"); #endif append_uint32 (output, type); } +static BroadwayNode * +lookup_old_node (GHashTable *old_node_lookup, + guint32 id) +{ + if (old_node_lookup) + return g_hash_table_lookup (old_node_lookup, GINT_TO_POINTER (id)); + + return NULL; +} + /*********************************** * This outputs the tree to the client, while at the same time diffing @@ -347,59 +358,230 @@ append_type (BroadwayOutput *output, guint32 type, BroadwayNode *node) static void append_node (BroadwayOutput *output, BroadwayNode *node, - BroadwayNode *old_node, - gboolean all_parents_are_kept) + GHashTable *old_node_lookup) { guint32 i; + BroadwayNode *reused_node; append_node_depth++; - if (old_node != NULL && broadway_node_equal (node, old_node)) + reused_node = lookup_old_node (old_node_lookup, node->id); + if (reused_node) { - if (broadway_node_deep_equal (node, old_node)) - { - append_type (output, BROADWAY_NODE_KEEP_ALL, node); - goto out; - } - - if (all_parents_are_kept) - { - append_type (output, BROADWAY_NODE_KEEP_THIS, node); - append_uint32 (output, node->n_children); - for (i = 0; i < node->n_children; i++) - append_node (output, node->children[i], - i < old_node->n_children ? old_node->children[i] : NULL, - TRUE); - - goto out; - } + broadway_node_mark_deep_consumed (reused_node, TRUE); + append_type (output, BROADWAY_NODE_REUSE, node); + append_uint32 (output, node->output_id); + } + else + { + append_type (output, node->type, node); + append_uint32 (output, node->output_id); + for (i = 0; i < node->n_data; i++) + append_uint32 (output, node->data[i]); + for (i = 0; i < node->n_children; i++) + append_node (output, + node->children[i], + old_node_lookup); } - append_type (output, node->type, node); - for (i = 0; i < node->n_data; i++) - append_uint32 (output, node->data[i]); - for (i = 0; i < node->n_children; i++) - append_node (output, - node->children[i], - (old_node != NULL && i < old_node->n_children) ? old_node->children[i] : NULL, - FALSE); - - out: append_node_depth--; } +static gboolean +should_reuse_node (BroadwayOutput *output, + BroadwayNode *node, + BroadwayNode *old_node) +{ + int i; + guint32 new_texture; + + if (old_node->reused) + return FALSE; + + if (node->type != old_node->type) + return FALSE; + + if (broadway_node_equal (node, old_node)) + return TRUE; + + switch (node->type) { + case BROADWAY_NODE_TRANSFORM: +#ifdef DEBUG_NODE_SENDING + g_print ("Patching transform node %d/%d\n", + old_node->id, old_node->output_id); +#endif + append_uint32 (output, BROADWAY_NODE_OP_PATCH_TRANSFORM); + append_uint32 (output, old_node->output_id); + for (i = 0; i < node->n_data; i++) + append_uint32 (output, node->data[i]); + return TRUE; + + case BROADWAY_NODE_TEXTURE: + /* Check that the size, etc is the same */ + for (i = 0; i < 4; i++) + if (node->data[i] != old_node->data[i]) + return FALSE; + + new_texture = node->data[4]; + +#ifdef DEBUG_NODE_SENDING + g_print ("Patching texture node %d/%d to tx=%d\n", + old_node->id, old_node->output_id, + new_texture); +#endif + append_uint32 (output, BROADWAY_NODE_OP_PATCH_TEXTURE); + append_uint32 (output, old_node->output_id); + append_uint32 (output, new_texture); + return TRUE; + break; + default: + return FALSE; + } +} + + +static BroadwayNode * +append_node_ops (BroadwayOutput *output, + BroadwayNode *node, + BroadwayNode *parent, + BroadwayNode *previous_sibling, + BroadwayNode *old_node, + GHashTable *old_node_lookup) +{ + BroadwayNode *reused_node; + guint32 i; + + /* Maybe can be reused from the last tree. */ + reused_node = lookup_old_node (old_node_lookup, node->id); + if (reused_node) + { + g_assert (node == reused_node); + g_assert (reused_node->reused); + g_assert (!reused_node->consumed); /* Should only be once in the tree, and not consumed otherwise */ + + broadway_node_mark_deep_consumed (reused_node, TRUE); + + if (node == old_node) + { + /* The node in the old tree at the current position is the same, so + we need to do nothing, just don't delete it (which we won't since + its marked used) */ + } + else + { + /* We can reuse it, bu it comes from a different place or + order, if so we need to move it in place */ +#ifdef DEBUG_NODE_SENDING + g_print ("Move old node %d/%d to parent %d/%d after %d/%d\n", + reused_node->id, reused_node->output_id, + parent ? parent->id : 0, + parent ? parent->output_id : 0, + previous_sibling ? previous_sibling->id : 0, + previous_sibling ? previous_sibling->output_id : 0); +#endif + append_uint32 (output, BROADWAY_NODE_OP_MOVE_AFTER_CHILD); + append_uint32 (output, parent ? parent->output_id : 0); + append_uint32 (output, previous_sibling ? previous_sibling->output_id : 0); + append_uint32 (output, reused_node->output_id); + } + + return reused_node; + } + + /* If the next node in place is shallowly equal (but not necessarily + * deep equal) we reuse it and tweak its children as needed. + * Except we avoid this for reused node as those make more sense to reuse deeply. + */ + + if (old_node && should_reuse_node (output, node, old_node)) + { + int old_i = 0; + BroadwayNode *last_child = NULL; + + old_node->consumed = TRUE; // Don't reuse again + + // We rewrite this new node as it now represents the old node in the browser + node->output_id = old_node->output_id; + + /* However, we might need to rewrite then children of old_node */ + for (i = 0; i < node->n_children; i++) + { + BroadwayNode *child = node->children[i]; + + /* Find the next (or first) non-consumed old child, if any */ + while (old_i < old_node->n_children && + old_node->children[old_i]->consumed) + old_i++; + + last_child = + append_node_ops (output, + child, + node, /* parent */ + last_child, + (old_i < old_node->n_children) ? old_node->children[old_i] : NULL, + old_node_lookup); + } + + /* Remaining old nodes are either reused elsewhere, or end up marked not consumed so are deleted at the end */ + return old_node; + } + + /* Fallback to create a new tree */ +#ifdef DEBUG_NODE_SENDING + g_print ("Insert nodes in parent %d/%d, after sibling %d/%d\n", + parent ? parent->id : 0, + parent ? parent->output_id : 0, + previous_sibling ? previous_sibling->id : 0, + previous_sibling ? previous_sibling->output_id : 0); +#endif + append_uint32 (output, BROADWAY_NODE_OP_INSERT_NODE); + append_uint32 (output, parent ? parent->output_id : 0); + append_uint32 (output, previous_sibling ? previous_sibling->output_id : 0); + + append_node(output, node, old_node_lookup); + + return node; +} + +/* Remove non-consumed nodes */ +static void +append_node_removes (BroadwayOutput *output, + BroadwayNode *node) +{ + // TODO: Use an array of nodes instead + if (!node->consumed) + { +#ifdef DEBUG_NODE_SENDING_REMOVE + g_print ("Remove old node non-consumed node %d/%d\n", + node->id, node->output_id); +#endif + append_uint32 (output, BROADWAY_NODE_OP_REMOVE_NODE); + append_uint32 (output, node->output_id); + } + + for (int i = 0; i < node->n_children; i++) + append_node_removes (output, node->children[i]); +} + + + void broadway_output_surface_set_nodes (BroadwayOutput *output, int id, BroadwayNode *root, - BroadwayNode *old_root) + BroadwayNode *old_root, + GHashTable *old_node_lookup) { gsize size_pos, start, end; - /* Early return if nothing changed */ - if (old_root != NULL && - broadway_node_deep_equal (root, old_root)) - return; + + if (old_root) + { + broadway_node_mark_deep_consumed (old_root, FALSE); + broadway_node_mark_deep_reused (old_root, FALSE); + /* This will modify children of old_root if any are shared */ + broadway_node_mark_deep_reused (root, TRUE); + } write_header (output, BROADWAY_OP_SET_NODES); @@ -410,9 +592,11 @@ broadway_output_surface_set_nodes (BroadwayOutput *output, start = output->buf->len; #ifdef DEBUG_NODE_SENDING - g_print ("====== node tree for %d =======\n", id); + g_print ("====== node ops for surface %d =======\n", id); #endif - append_node (output, root, old_root, TRUE); + append_node_ops (output, root, NULL, NULL, old_root, old_node_lookup); + if (old_root) + append_node_removes (output, old_root); end = output->buf->len; patch_uint32 (output, (end - start) / 4, size_pos); } diff --git a/gdk/broadway/broadway-output.h b/gdk/broadway/broadway-output.h index 7270f188cc..036aa05c37 100644 --- a/gdk/broadway/broadway-output.h +++ b/gdk/broadway/broadway-output.h @@ -60,7 +60,8 @@ void broadway_output_set_transient_for (BroadwayOutput *output, void broadway_output_surface_set_nodes (BroadwayOutput *output, int id, BroadwayNode *root, - BroadwayNode *old_root); + BroadwayNode *old_root, + GHashTable *old_node_lookup); void broadway_output_upload_texture (BroadwayOutput *output, guint32 id, GBytes *texture); diff --git a/gdk/broadway/broadway-protocol.h b/gdk/broadway/broadway-protocol.h index 4f7b812e66..3f73f13771 100644 --- a/gdk/broadway/broadway-protocol.h +++ b/gdk/broadway/broadway-protocol.h @@ -20,13 +20,19 @@ typedef enum { /* Sync changes with broadway.js */ BROADWAY_NODE_SHADOW = 8, BROADWAY_NODE_OPACITY = 9, BROADWAY_NODE_CLIP = 10, - BROADWAY_NODE_KEEP_ALL = 11, - BROADWAY_NODE_KEEP_THIS = 12, - BROADWAY_NODE_TRANSFORM = 13, - BROADWAY_NODE_DEBUG = 14, - BROADWAY_NODE_REUSE = 15, + BROADWAY_NODE_TRANSFORM = 11, + BROADWAY_NODE_DEBUG = 12, + BROADWAY_NODE_REUSE = 13, } BroadwayNodeType; +typedef enum { /* Sync changes with broadway.js */ + BROADWAY_NODE_OP_INSERT_NODE = 0, + BROADWAY_NODE_OP_REMOVE_NODE = 1, + BROADWAY_NODE_OP_MOVE_AFTER_CHILD = 2, + BROADWAY_NODE_OP_PATCH_TEXTURE = 3, + BROADWAY_NODE_OP_PATCH_TRANSFORM = 4, +} BroadwayNodeOpType; + static const char *broadway_node_type_names[] G_GNUC_UNUSED = { "TEXTURE", "CONTAINER", @@ -39,51 +45,47 @@ static const char *broadway_node_type_names[] G_GNUC_UNUSED = { "SHADOW", "OPACITY", "CLIP", - "KEEP_ALL", - "KEEP_THIS", - "TRANSLATE", + "TRANSFORM", "DEBUG", + "REUSE", }; typedef enum { - BROADWAY_EVENT_ENTER = 'e', - BROADWAY_EVENT_LEAVE = 'l', - BROADWAY_EVENT_POINTER_MOVE = 'm', - BROADWAY_EVENT_BUTTON_PRESS = 'b', - BROADWAY_EVENT_BUTTON_RELEASE = 'B', - BROADWAY_EVENT_TOUCH = 't', - BROADWAY_EVENT_SCROLL = 's', - BROADWAY_EVENT_KEY_PRESS = 'k', - BROADWAY_EVENT_KEY_RELEASE = 'K', - BROADWAY_EVENT_GRAB_NOTIFY = 'g', - BROADWAY_EVENT_UNGRAB_NOTIFY = 'u', - BROADWAY_EVENT_CONFIGURE_NOTIFY = 'w', - BROADWAY_EVENT_SCREEN_SIZE_CHANGED = 'd', - BROADWAY_EVENT_FOCUS = 'f', - BROADWAY_EVENT_ROUNDTRIP_NOTIFY = 'F', + BROADWAY_EVENT_ENTER = 0, + BROADWAY_EVENT_LEAVE = 1, + BROADWAY_EVENT_POINTER_MOVE = 2, + BROADWAY_EVENT_BUTTON_PRESS = 3, + BROADWAY_EVENT_BUTTON_RELEASE = 4, + BROADWAY_EVENT_TOUCH = 5, + BROADWAY_EVENT_SCROLL = 6, + BROADWAY_EVENT_KEY_PRESS = 7, + BROADWAY_EVENT_KEY_RELEASE = 8, + BROADWAY_EVENT_GRAB_NOTIFY = 9, + BROADWAY_EVENT_UNGRAB_NOTIFY = 10, + BROADWAY_EVENT_CONFIGURE_NOTIFY = 11, + BROADWAY_EVENT_SCREEN_SIZE_CHANGED = 12, + BROADWAY_EVENT_FOCUS = 13, + BROADWAY_EVENT_ROUNDTRIP_NOTIFY = 14, } BroadwayEventType; typedef enum { - BROADWAY_OP_GRAB_POINTER = 'g', - BROADWAY_OP_UNGRAB_POINTER = 'u', - BROADWAY_OP_NEW_SURFACE = 's', - BROADWAY_OP_SHOW_SURFACE = 'S', - BROADWAY_OP_HIDE_SURFACE = 'H', - BROADWAY_OP_RAISE_SURFACE = 'r', - BROADWAY_OP_LOWER_SURFACE = 'R', - BROADWAY_OP_DESTROY_SURFACE = 'd', - BROADWAY_OP_MOVE_RESIZE = 'm', - BROADWAY_OP_SET_TRANSIENT_FOR = 'p', - BROADWAY_OP_PUT_RGB = 'i', - BROADWAY_OP_REQUEST_AUTH = 'l', - BROADWAY_OP_AUTH_OK = 'L', - BROADWAY_OP_DISCONNECTED = 'D', - BROADWAY_OP_SURFACE_UPDATE = 'b', - BROADWAY_OP_SET_SHOW_KEYBOARD = 'k', - BROADWAY_OP_UPLOAD_TEXTURE = 't', - BROADWAY_OP_RELEASE_TEXTURE = 'T', - BROADWAY_OP_SET_NODES = 'n', - BROADWAY_OP_ROUNDTRIP = 'F', + BROADWAY_OP_GRAB_POINTER = 0, + BROADWAY_OP_UNGRAB_POINTER = 1, + BROADWAY_OP_NEW_SURFACE = 2, + BROADWAY_OP_SHOW_SURFACE = 3, + BROADWAY_OP_HIDE_SURFACE = 4, + BROADWAY_OP_RAISE_SURFACE = 5, + BROADWAY_OP_LOWER_SURFACE = 6, + BROADWAY_OP_DESTROY_SURFACE = 7, + BROADWAY_OP_MOVE_RESIZE = 8, + BROADWAY_OP_SET_TRANSIENT_FOR = 9, + BROADWAY_OP_DISCONNECTED = 10, + BROADWAY_OP_SURFACE_UPDATE = 11, + BROADWAY_OP_SET_SHOW_KEYBOARD = 12, + BROADWAY_OP_UPLOAD_TEXTURE = 13, + BROADWAY_OP_RELEASE_TEXTURE = 14, + BROADWAY_OP_SET_NODES = 15, + BROADWAY_OP_ROUNDTRIP = 16, } BroadwayOpType; typedef struct { diff --git a/gdk/broadway/broadway-server.c b/gdk/broadway/broadway-server.c index 70a236ef46..24fd7bf51c 100644 --- a/gdk/broadway/broadway-server.c +++ b/gdk/broadway/broadway-server.c @@ -115,6 +115,7 @@ struct BroadwayInput { }; struct BroadwaySurface { + guint32 owner; gint32 id; gint32 x; gint32 y; @@ -224,6 +225,33 @@ broadway_node_deep_equal (BroadwayNode *a, } +void +broadway_node_mark_deep_reused (BroadwayNode *node, + gboolean reused) +{ + node->reused = reused; + for (int i = 0; i < node->n_children; i++) + broadway_node_mark_deep_reused (node->children[i], reused); +} + +void +broadway_node_mark_deep_consumed (BroadwayNode *node, + gboolean consumed) +{ + node->consumed = consumed; + for (int i = 0; i < node->n_children; i++) + broadway_node_mark_deep_consumed (node->children[i], consumed); +} + +void +broadway_node_add_to_lookup (BroadwayNode *node, + GHashTable *node_lookup) +{ + g_hash_table_insert (node_lookup, GINT_TO_POINTER(node->id), node); + for (int i = 0; i < node->n_children; i++) + broadway_node_add_to_lookup (node->children[i], node_lookup); +} + static void broadway_server_init (BroadwayServer *server) { @@ -444,9 +472,46 @@ process_input_message (BroadwayServer *server, BroadwayInputMsg *message) { gint32 client; + BroadwaySurface *surface; update_event_state (server, message); - client = -1; + + + switch (message->base.type) { + case BROADWAY_EVENT_ENTER: + case BROADWAY_EVENT_LEAVE: + case BROADWAY_EVENT_POINTER_MOVE: + case BROADWAY_EVENT_BUTTON_PRESS: + case BROADWAY_EVENT_BUTTON_RELEASE: + case BROADWAY_EVENT_SCROLL: + case BROADWAY_EVENT_GRAB_NOTIFY: + case BROADWAY_EVENT_UNGRAB_NOTIFY: + surface = broadway_server_lookup_surface (server, message->pointer.event_surface_id); + break; + case BROADWAY_EVENT_TOUCH: + surface = broadway_server_lookup_surface (server, message->touch.event_surface_id); + break; + case BROADWAY_EVENT_CONFIGURE_NOTIFY: + surface = broadway_server_lookup_surface (server, message->configure_notify.id); + break; + case BROADWAY_EVENT_ROUNDTRIP_NOTIFY: + surface = broadway_server_lookup_surface (server, message->roundtrip_notify.id); + break; + case BROADWAY_EVENT_KEY_PRESS: + case BROADWAY_EVENT_KEY_RELEASE: + /* TODO: Send to keys focused clients only... */ + case BROADWAY_EVENT_FOCUS: + case BROADWAY_EVENT_SCREEN_SIZE_CHANGED: + default: + surface = NULL; + break; + } + + if (surface) + client = surface->owner; + else + client = -1; + if (is_pointer_event (message) && server->pointer_grab_surface_id != -1) client = server->pointer_grab_client_id; @@ -557,6 +622,7 @@ parse_input_message (BroadwayInput *input, const unsigned char *message) msg.base.type = ntohl (*p++); msg.base.serial = ntohl (*p++); + time_ = ntohl (*p++); if (time_ == 0) { @@ -638,12 +704,19 @@ parse_input_message (BroadwayInput *input, const unsigned char *message) if (rt->id == msg.roundtrip_notify.id && rt->tag == msg.roundtrip_notify.tag) - { - server->outstanding_roundtrips = g_list_delete_link (server->outstanding_roundtrips, l); - g_free (rt); - break; - } + break; } + + if (l == NULL) + g_warning ("Got unexpected rountrip reply for id %d, tag %d\n", msg.roundtrip_notify.id, msg.roundtrip_notify.tag); + else + { + BroadwayOutstandingRoundtrip *rt = l->data; + + server->outstanding_roundtrips = g_list_delete_link (server->outstanding_roundtrips, l); + g_free (rt); + } + break; case BROADWAY_EVENT_SCREEN_SIZE_CHANGED: @@ -1753,6 +1826,7 @@ decode_nodes (BroadwayServer *server, g_ref_count_init (&node->refcount); node->type = type; node->id = id; + node->output_id = id; node->texture_id = 0; node->n_children = n_children; node->children = (BroadwayNode **)((char *)node + sizeof(BroadwayNode) + (size - 1) * sizeof(guint32)); @@ -1784,17 +1858,6 @@ decode_nodes (BroadwayServer *server, return node; } -static void -init_node_lookup (BroadwaySurface *surface, - BroadwayNode *node) -{ - int i; - - g_hash_table_insert (surface->node_lookup, GINT_TO_POINTER(node->id), node); - for (i = 0; i < node->n_children; i++) - init_node_lookup (surface, node->children[i]); -} - /* passes ownership of nodes */ void broadway_server_surface_update_nodes (BroadwayServer *server, @@ -1816,7 +1879,8 @@ broadway_server_surface_update_nodes (BroadwayServer *server, if (server->output != NULL) broadway_output_surface_set_nodes (server->output, surface->id, root, - surface->nodes); + surface->nodes, + surface->node_lookup); if (surface->nodes) broadway_node_unref (server, surface->nodes); @@ -1824,8 +1888,7 @@ broadway_server_surface_update_nodes (BroadwayServer *server, surface->nodes = root; g_hash_table_remove_all (surface->node_lookup); - - init_node_lookup (surface, surface->nodes); + broadway_node_add_to_lookup (root, surface->node_lookup); } guint32 @@ -2003,6 +2066,7 @@ broadway_server_ungrab_pointer (BroadwayServer *server, guint32 broadway_server_new_surface (BroadwayServer *server, + guint32 client, int x, int y, int width, @@ -2012,6 +2076,7 @@ broadway_server_new_surface (BroadwayServer *server, BroadwaySurface *surface; surface = g_new0 (BroadwaySurface, 1); + surface->owner = client; surface->id = server->id_counter++; surface->x = x; surface->y = y; @@ -2097,7 +2162,8 @@ broadway_server_resync_surfaces (BroadwayServer *server) if (surface->nodes) broadway_output_surface_set_nodes (server->output, surface->id, - surface->nodes, NULL); + surface->nodes, + NULL, NULL); if (surface->visible) broadway_output_show_surface (server->output, surface->id); diff --git a/gdk/broadway/broadway-server.h b/gdk/broadway/broadway-server.h index 1e125bd065..f11e9e872d 100644 --- a/gdk/broadway/broadway-server.h +++ b/gdk/broadway/broadway-server.h @@ -25,10 +25,16 @@ struct _BroadwayNode { grefcount refcount; guint32 type; guint32 id; + guint32 output_id; guint32 hash; /* deep hash */ guint32 n_children; BroadwayNode **children; guint32 texture_id; + + /* Scratch stuff used during diff */ + gboolean reused; + gboolean consumed; + guint32 n_data; guint32 data[1]; }; @@ -37,6 +43,12 @@ gboolean broadway_node_equal (BroadwayNode * BroadwayNode *b); gboolean broadway_node_deep_equal (BroadwayNode *a, BroadwayNode *b); +void broadway_node_mark_deep_reused (BroadwayNode *node, + gboolean reused); +void broadway_node_mark_deep_consumed (BroadwayNode *node, + gboolean consumed); +void broadway_node_add_to_lookup (BroadwayNode *node, + GHashTable *node_lookup); BroadwayServer *broadway_server_new (char *address, int port, const char *ssl_cert, @@ -74,6 +86,7 @@ gint32 broadway_server_get_mouse_surface (BroadwayServer * void broadway_server_set_show_keyboard (BroadwayServer *server, gboolean show); guint32 broadway_server_new_surface (BroadwayServer *server, + guint32 client, int x, int y, int width, diff --git a/gdk/broadway/broadway.js b/gdk/broadway/broadway.js index ae3d912ea6..ba2fdcd727 100644 --- a/gdk/broadway/broadway.js +++ b/gdk/broadway/broadway.js @@ -11,59 +11,63 @@ const BROADWAY_NODE_LINEAR_GRADIENT = 7; const BROADWAY_NODE_SHADOW = 8; const BROADWAY_NODE_OPACITY = 9; const BROADWAY_NODE_CLIP = 10; -const BROADWAY_NODE_KEEP_ALL = 11; -const BROADWAY_NODE_KEEP_THIS = 12; -const BROADWAY_NODE_TRANSFORM = 13; -const BROADWAY_NODE_DEBUG = 14; -const BROADWAY_NODE_REUSE = 15; +const BROADWAY_NODE_TRANSFORM = 11; +const BROADWAY_NODE_DEBUG = 12; +const BROADWAY_NODE_REUSE = 13; -const BROADWAY_OP_GRAB_POINTER = 'g'; -const BROADWAY_OP_UNGRAB_POINTER = 'u'; -const BROADWAY_OP_NEW_SURFACE = 's'; -const BROADWAY_OP_SHOW_SURFACE = 'S'; -const BROADWAY_OP_HIDE_SURFACE = 'H'; -const BROADWAY_OP_RAISE_SURFACE = 'r'; -const BROADWAY_OP_LOWER_SURFACE = 'R'; -const BROADWAY_OP_DESTROY_SURFACE = 'd'; -const BROADWAY_OP_MOVE_RESIZE = 'm'; -const BROADWAY_OP_SET_TRANSIENT_FOR = 'p'; -const BROADWAY_OP_PUT_RGB = 'i'; -const BROADWAY_OP_REQUEST_AUTH = 'l'; -const BROADWAY_OP_AUTH_OK = 'L'; -const BROADWAY_OP_DISCONNECTED = 'D'; -const BROADWAY_OP_SURFACE_UPDATE = 'b'; -const BROADWAY_OP_SET_SHOW_KEYBOARD = 'k'; -const BROADWAY_OP_UPLOAD_TEXTURE = 't'; -const BROADWAY_OP_RELEASE_TEXTURE = 'T'; -const BROADWAY_OP_SET_NODES = 'n'; -const BROADWAY_OP_ROUNDTRIP = 'F'; +const BROADWAY_NODE_OP_INSERT_NODE = 0; +const BROADWAY_NODE_OP_REMOVE_NODE = 1; +const BROADWAY_NODE_OP_MOVE_AFTER_CHILD = 2; +const BROADWAY_NODE_OP_PATCH_TEXTURE = 3; +const BROADWAY_NODE_OP_PATCH_TRANSFORM = 4; -const BROADWAY_EVENT_ENTER = 'e'; -const BROADWAY_EVENT_LEAVE = 'l'; -const BROADWAY_EVENT_POINTER_MOVE = 'm'; -const BROADWAY_EVENT_BUTTON_PRESS = 'b'; -const BROADWAY_EVENT_BUTTON_RELEASE = 'B'; -const BROADWAY_EVENT_TOUCH = 't'; -const BROADWAY_EVENT_SCROLL = 's'; -const BROADWAY_EVENT_KEY_PRESS = 'k'; -const BROADWAY_EVENT_KEY_RELEASE = 'K'; -const BROADWAY_EVENT_GRAB_NOTIFY = 'g'; -const BROADWAY_EVENT_UNGRAB_NOTIFY = 'u'; -const BROADWAY_EVENT_CONFIGURE_NOTIFY = 'w'; -const BROADWAY_EVENT_SCREEN_SIZE_CHANGED = 'd'; -const BROADWAY_EVENT_FOCUS = 'f'; -const BROADWAY_EVENT_ROUNDTRIP_NOTIFY = 'F'; +const BROADWAY_OP_GRAB_POINTER = 0; +const BROADWAY_OP_UNGRAB_POINTER = 1; +const BROADWAY_OP_NEW_SURFACE = 2; +const BROADWAY_OP_SHOW_SURFACE = 3; +const BROADWAY_OP_HIDE_SURFACE = 4; +const BROADWAY_OP_RAISE_SURFACE = 5; +const BROADWAY_OP_LOWER_SURFACE = 6; +const BROADWAY_OP_DESTROY_SURFACE = 7; +const BROADWAY_OP_MOVE_RESIZE = 8; +const BROADWAY_OP_SET_TRANSIENT_FOR = 9; +const BROADWAY_OP_DISCONNECTED = 10; +const BROADWAY_OP_SURFACE_UPDATE = 11; +const BROADWAY_OP_SET_SHOW_KEYBOARD = 12; +const BROADWAY_OP_UPLOAD_TEXTURE = 13; +const BROADWAY_OP_RELEASE_TEXTURE = 14; +const BROADWAY_OP_SET_NODES = 15; +const BROADWAY_OP_ROUNDTRIP = 16; + +const BROADWAY_EVENT_ENTER = 0; +const BROADWAY_EVENT_LEAVE = 1; +const BROADWAY_EVENT_POINTER_MOVE = 2; +const BROADWAY_EVENT_BUTTON_PRESS = 3; +const BROADWAY_EVENT_BUTTON_RELEASE = 4; +const BROADWAY_EVENT_TOUCH = 5; +const BROADWAY_EVENT_SCROLL = 6; +const BROADWAY_EVENT_KEY_PRESS = 7; +const BROADWAY_EVENT_KEY_RELEASE = 8; +const BROADWAY_EVENT_GRAB_NOTIFY = 9; +const BROADWAY_EVENT_UNGRAB_NOTIFY = 10; +const BROADWAY_EVENT_CONFIGURE_NOTIFY = 11; +const BROADWAY_EVENT_SCREEN_SIZE_CHANGED = 12; +const BROADWAY_EVENT_FOCUS = 13; +const BROADWAY_EVENT_ROUNDTRIP_NOTIFY = 14; const DISPLAY_OP_REPLACE_CHILD = 0; const DISPLAY_OP_APPEND_CHILD = 1; -const DISPLAY_OP_APPEND_ROOT = 2; -const DISPLAY_OP_SHOW_SURFACE = 3; -const DISPLAY_OP_HIDE_SURFACE = 4; -const DISPLAY_OP_DELETE_NODE = 5; -const DISPLAY_OP_MOVE_NODE = 6; -const DISPLAY_OP_RESIZE_NODE = 7; -const DISPLAY_OP_RESTACK_SURFACES = 8; -const DISPLAY_OP_DELETE_SURFACE = 9; +const DISPLAY_OP_INSERT_AFTER_CHILD = 2; +const DISPLAY_OP_APPEND_ROOT = 3; +const DISPLAY_OP_SHOW_SURFACE = 4; +const DISPLAY_OP_HIDE_SURFACE = 5; +const DISPLAY_OP_DELETE_NODE = 6; +const DISPLAY_OP_MOVE_NODE = 7; +const DISPLAY_OP_RESIZE_NODE = 8; +const DISPLAY_OP_RESTACK_SURFACES = 9; +const DISPLAY_OP_DELETE_SURFACE = 10; +const DISPLAY_OP_CHANGE_TEXTURE = 11; +const DISPLAY_OP_CHANGE_TRANSFORM = 12; // GdkCrossingMode const GDK_CROSSING_NORMAL = 0; @@ -89,6 +93,63 @@ const GDK_HYPER_MASK = 1 << 27; const GDK_META_MASK = 1 << 28; const GDK_RELEASE_MASK = 1 << 30; + +var useDataUrls = window.location.search.includes("datauri"); + +/* This base64code is based on https://github.com/beatgammit/base64-js/blob/master/index.js which is MIT licensed */ + +var b64_lookup = []; +var base64_code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; +for (var i = 0, len = base64_code.length; i < len; ++i) { + b64_lookup[i] = base64_code[i]; +} + +function tripletToBase64 (num) { + return b64_lookup[num >> 18 & 0x3F] + + b64_lookup[num >> 12 & 0x3F] + + b64_lookup[num >> 6 & 0x3F] + + b64_lookup[num & 0x3F]; +} + +function encodeBase64Chunk (uint8, start, end) { + var tmp; + var output = []; + for (var i = start; i < end; i += 3) { + tmp = + ((uint8[i] << 16) & 0xFF0000) + + ((uint8[i + 1] << 8) & 0xFF00) + + (uint8[i + 2] & 0xFF); + output.push(tripletToBase64(tmp)); + } + return output.join(''); +} + +function bytesToDataUri(uint8) { + var tmp; + var len = uint8.length; + var extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes + var parts = []; + var maxChunkLength = 16383; // must be multiple of 3 + + parts.push("data:image/png;base64,"); + + // go through the array every three bytes, we'll deal with trailing stuff later + for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { + parts.push(encodeBase64Chunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))); + } + + // pad the end with zeros, but make sure to not forget the extra bytes + if (extraBytes === 1) { + tmp = uint8[len - 1]; + parts.push(b64_lookup[tmp >> 2] + b64_lookup[(tmp << 4) & 0x3F] + '=='); + } else if (extraBytes === 2) { + tmp = (uint8[len - 2] << 8) + uint8[len - 1]; + parts.push(b64_lookup[tmp >> 10] + b64_lookup[(tmp >> 4) & 0x3F] + b64_lookup[(tmp << 2) & 0x3F] + '='); + } + + return parts.join(''); +} + /* Helper functions for debugging */ var logDiv = null; function log(str) { @@ -198,8 +259,15 @@ function getButtonMask (button) { } function Texture(id, data) { - var blob = new Blob([data],{type: "image/png"}); - this.url = window.URL.createObjectURL(blob); + var url; + if (useDataUrls) { + url = bytesToDataUri(data); + } else { + var blob = new Blob([data],{type: "image/png"}); + url = window.URL.createObjectURL(blob); + } + + this.url = url; this.refcount = 1; this.id = id; @@ -217,7 +285,9 @@ Texture.prototype.ref = function() { Texture.prototype.unref = function() { this.refcount -= 1; if (this.refcount == 0) { - window.URL.revokeObjectURL(this.url); + if (this.url.startsWith("blob")) { + window.URL.revokeObjectURL(this.url); + } delete textures[this.id]; } } @@ -234,6 +304,7 @@ function cmdCreateSurface(id, x, y, width, height, isTemp) surface.transientParent = 0; surface.visible = false; surface.imageData = null; + surface.nodes = {}; var div = document.createElement('div'); div.surface = surface; @@ -296,12 +367,13 @@ function cmdLowerSurface(id) moveToHelper(surface, 0); } -function TransformNodes(node_data, div, display_commands) { +function TransformNodes(node_data, div, nodes, display_commands) { this.node_data = node_data; this.display_commands = display_commands; this.data_pos = 0; this.div = div; this.outstanding = 1; + this.nodes = nodes; } TransformNodes.prototype.decode_uint32 = function() { @@ -441,6 +513,28 @@ TransformNodes.prototype.decode_string = function() { return utf8_to_string (utf8); } +TransformNodes.prototype.decode_transform = function() { + var transform_type = this.decode_uint32(); + + if (transform_type == 0) { + var point = this.decode_point(); + return "translate(" + px(point.x) + "," + px(point.y) + ")"; + } else if (transform_type == 1) { + var m = new Array(); + for (var i = 0; i < 16; i++) { + m[i] = this.decode_float (); + } + + return "matrix3d(" + + m[0] + "," + m[1] + "," + m[2] + "," + m[3]+ "," + + m[4] + "," + m[5] + "," + m[6] + "," + m[7] + "," + + m[8] + "," + m[9] + "," + m[10] + "," + m[11] + "," + + m[12] + "," + m[13] + "," + m[14] + "," + m[15] + ")"; + } else { + alert("Unexpected transform type " + transform_type); + } +} + function args() { var argsLength = arguments.length; var strings = []; @@ -474,27 +568,44 @@ function set_rrect_style (div, rrect) { div.style["border-bottom-left-radius"] = args(px(rrect.sizes[3].width), px(rrect.sizes[3].height)); } -TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode) +TransformNodes.prototype.createDiv = function(id) +{ + var div = document.createElement('div'); + div.node_id = id; + this.nodes[id] = div; + return div; +} + +TransformNodes.prototype.createImage = function(id) +{ + var image = new Image(); + image.node_id = id; + this.nodes[id] = image; + return image; +} + +TransformNodes.prototype.insertNode = function(parent, previousSibling, is_toplevel) { var type = this.decode_uint32(); + var id = this.decode_uint32(); var newNode = null; - - // We need to dup this because as we reuse children the original order is lost - var oldChildren = []; - if (oldNode) { - for (var i = 0; i < oldNode.children.length; i++) - oldChildren[i] = oldNode.children[i]; - } + var oldNode = null; switch (type) { + /* Reuse divs from last frame */ + case BROADWAY_NODE_REUSE: + { + oldNode = this.nodes[id]; + } + break; /* Leaf nodes */ case BROADWAY_NODE_TEXTURE: { var rect = this.decode_rect(); var texture_id = this.decode_uint32(); - var image = new Image(); + var image = this.createImage(id); image.width = rect.width; image.height = rect.height; image.style["position"] = "absolute"; @@ -511,7 +622,7 @@ TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode) { var rect = this.decode_rect(); var c = this.decode_color (); - var div = document.createElement('div'); + var div = this.createDiv(id); div.style["position"] = "absolute"; set_rect_style(div, rect); div.style["background-color"] = c; @@ -529,7 +640,7 @@ TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode) for (var i = 0; i < 4; i++) border_colors[i] = this.decode_color(); - var div = document.createElement('div'); + var div = this.createDiv(id); div.style["position"] = "absolute"; rrect.bounds.width -= border_widths[1] + border_widths[3]; rrect.bounds.height -= border_widths[0] + border_widths[2]; @@ -556,7 +667,7 @@ TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode) var spread = this.decode_float(); var blur = this.decode_float(); - var div = document.createElement('div'); + var div = this.createDiv(id); div.style["position"] = "absolute"; set_rrect_style(div, rrect); div.style["box-shadow"] = args(px(dx), px(dy), px(blur), px(spread), color); @@ -573,7 +684,7 @@ TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode) var spread = this.decode_float(); var blur = this.decode_float(); - var div = document.createElement('div'); + var div = this.createDiv(id); div.style["position"] = "absolute"; set_rrect_style(div, rrect); div.style["box-shadow"] = args("inset", px(dx), px(dy), px(blur), px(spread), color); @@ -588,7 +699,7 @@ TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode) var start = this.decode_point (); var end = this.decode_point (); var stops = this.decode_color_stops (); - var div = document.createElement('div'); + var div = this.createDiv(id); div.style["position"] = "absolute"; set_rect_style(div, rect); @@ -632,31 +743,13 @@ TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode) case BROADWAY_NODE_TRANSFORM: { - var transform_type = this.decode_uint32(); - var transform_string; + var transform_string = this.decode_transform(); - if (transform_type == 0) { - var point = this.decode_point(); - transform_string = "translate(" + px(point.x) + "," + px(point.y) + ")"; - } else if (transform_type == 1) { - var m = new Array(); - for (var i = 0; i < 16; i++) { - m[i] = this.decode_float (); - } - - transform_string = - "matrix3d(" + - m[0] + "," + m[1] + "," + m[2] + "," + m[3]+ "," + - m[4] + "," + m[5] + "," + m[6] + "," + m[7] + "," + - m[8] + "," + m[9] + "," + m[10] + "," + m[11] + "," + - m[12] + "," + m[13] + "," + m[14] + "," + m[15] + ")"; - } - - var div = document.createElement('div'); + var div = this.createDiv(id); div.style["transform"] = transform_string; div.style["transform-origin"] = "0px 0px"; - this.insertNode(div, -1, oldChildren[0]); + this.insertNode(div, null, false); newNode = div; } break; @@ -664,11 +757,11 @@ TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode) case BROADWAY_NODE_CLIP: { var rect = this.decode_rect(); - var div = document.createElement('div'); + var div = this.createDiv(id); div.style["position"] = "absolute"; set_rect_style(div, rect); div.style["overflow"] = "hidden"; - this.insertNode(div, -1, oldChildren[0]); + this.insertNode(div, null, false); newNode = div; } break; @@ -676,11 +769,11 @@ TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode) case BROADWAY_NODE_ROUNDED_CLIP: { var rrect = this.decode_rounded_rect(); - var div = document.createElement('div'); + var div = this.createDiv(id); div.style["position"] = "absolute"; set_rrect_style(div, rrect); div.style["overflow"] = "hidden"; - this.insertNode(div, -1, oldChildren[0]); + this.insertNode(div, null, false); newNode = div; } break; @@ -688,13 +781,13 @@ TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode) case BROADWAY_NODE_OPACITY: { var opacity = this.decode_float(); - var div = document.createElement('div'); + var div = this.createDiv(id); div.style["position"] = "absolute"; div.style["left"] = px(0); div.style["top"] = px(0); div.style["opacity"] = opacity; - this.insertNode(div, -1, oldChildren[0]); + this.insertNode(div, null, false); newNode = div; } break; @@ -710,13 +803,13 @@ TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode) var blur = this.decode_float(); filters = filters + "drop-shadow(" + args (px(dx), px(dy), px(blur), color) + ")"; } - var div = document.createElement('div'); + var div = this.createDiv(id); div.style["position"] = "absolute"; div.style["left"] = px(0); div.style["top"] = px(0); div.style["filter"] = filters; - this.insertNode(div, -1, oldChildren[0]); + this.insertNode(div, null, false); newNode = div; } break; @@ -724,9 +817,9 @@ TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode) case 14: // DEBUG { var str = this.decode_string(); - var div = document.createElement('div'); + var div = this.createDiv(id); div.setAttribute('debug', str); - this.insertNode(div, -1, oldChildren[0]); + this.insertNode(div, null, false); newNode = div; } break; @@ -735,73 +828,98 @@ TransformNodes.prototype.insertNode = function(parent, posInParent, oldNode) case BROADWAY_NODE_CONTAINER: { - var div = document.createElement('div'); + var div = this.createDiv(id); var len = this.decode_uint32(); + var lastChild = null; for (var i = 0; i < len; i++) { - this.insertNode(div, -1, oldChildren[i]); + lastChild = this.insertNode(div, lastChild, false); } newNode = div; } break; - case BROADWAY_NODE_KEEP_ALL: - { - if (!oldNode) - alert("KEEP_ALL with no oldNode"); - - if (oldNode.parentNode != parent) - newNode = oldNode; - else - newNode = null; - } - break; - - case BROADWAY_NODE_KEEP_THIS: - { - if (!oldNode) - alert("KEEP_THIS with no oldNode "); - - /* We only get keep-this if all parents were kept, check this */ - if (oldNode.parentNode != parent) - alert("Got KEEP_THIS for non-kept parent"); - - var len = this.decode_uint32(); - var i; - - for (i = 0; i < len; i++) { - this.insertNode(oldNode, i, - oldChildren[i]); - } - - /* Remove children that are after the new length */ - for (i = oldChildren.length - 1; i > len - 1; i--) - this.display_commands.push([DISPLAY_OP_DELETE_NODE, oldChildren[i]]); - - /* NOTE: No need to modify the parent, we're keeping this node as is */ - newNode = null; - } - break; - default: alert("Unexpected node type " + type); } if (newNode) { - if (posInParent >= 0 && parent.children[posInParent]) - this.display_commands.push([DISPLAY_OP_REPLACE_CHILD, parent, newNode, parent.children[posInParent]]); - else - this.display_commands.push([DISPLAY_OP_APPEND_CHILD, parent, newNode]); + if (is_toplevel) + this.display_commands.push([DISPLAY_OP_INSERT_AFTER_CHILD, parent, previousSibling, newNode]); + else // It is safe to display directly because we have not added the toplevel to the document yet + parent.appendChild(newNode); + return newNode; + } else if (oldNode) { + // This must be delayed until display ops, because it will remove from the old parent + this.display_commands.push([DISPLAY_OP_INSERT_AFTER_CHILD, parent, previousSibling, oldNode]); + return oldNode; } } TransformNodes.prototype.execute = function(display_commands) { - var div = this.div; - this.insertNode(div, 0, div.firstChild, display_commands); - if (this.data_pos != this.node_data.byteLength) - alert ("Did not consume entire array (len " + this.node_data.byteLength + ")"); -} + var root = this.div; + while (this.data_pos < this.node_data.byteLength) { + var op = this.decode_uint32(); + var parentId, parent; + + switch (op) { + case BROADWAY_NODE_OP_INSERT_NODE: + parentId = this.decode_uint32(); + if (parentId == 0) + parent = root; + else { + parent = this.nodes[parentId]; + if (parent == null) + console.log("Wanted to insert into parent " + parentId + " but it is unknown"); + } + + var previousChildId = this.decode_uint32(); + var previousChild = null; + if (previousChildId != 0) + previousChild = this.nodes[previousChildId]; + this.insertNode(parent, previousChild, true); + break; + case BROADWAY_NODE_OP_REMOVE_NODE: + var removeId = this.decode_uint32(); + var remove = this.nodes[removeId]; + delete this.nodes[removeId]; + if (remove == null) + console.log("Wanted to delete node " + removeId + " but it is unknown"); + + this.display_commands.push([DISPLAY_OP_DELETE_NODE, remove]); + break; + case BROADWAY_NODE_OP_MOVE_AFTER_CHILD: + parentId = this.decode_uint32(); + if (parentId == 0) + parent = root; + else + parent = this.nodes[parentId]; + var previousChildId = this.decode_uint32(); + var previousChild = null; + if (previousChildId != 0) + previousChild = this.nodes[previousChildId]; + var toMoveId = this.decode_uint32(); + var toMove = this.nodes[toMoveId]; + this.display_commands.push([DISPLAY_OP_INSERT_AFTER_CHILD, parent, previousChild, toMove]); + break; + case BROADWAY_NODE_OP_PATCH_TEXTURE: + var textureNodeId = this.decode_uint32(); + var textureNode = this.nodes[textureNodeId]; + var textureId = this.decode_uint32(); + var texture = textures[textureId].ref(); + this.display_commands.push([DISPLAY_OP_CHANGE_TEXTURE, textureNode, texture]); + break; + case BROADWAY_NODE_OP_PATCH_TRANSFORM: + var transformNodeId = this.decode_uint32(); + var transformNode = this.nodes[transformNodeId]; + var transformString = this.decode_transform(); + this.display_commands.push([DISPLAY_OP_CHANGE_TRANSFORM, transformNode, transformString]); + break; + } + + } +} function cmdGrabPointer(id, ownerEvents) { @@ -818,7 +936,7 @@ function cmdUngrabPointer() function handleDisplayCommands(display_commands) { - var div; + var div, parent; var len = display_commands.length; for (var i = 0; i < len; i++) { var cmd = display_commands[i]; @@ -830,6 +948,15 @@ function handleDisplayCommands(display_commands) case DISPLAY_OP_APPEND_CHILD: cmd[1].appendChild(cmd[2]); break; + case DISPLAY_OP_INSERT_AFTER_CHILD: + parent = cmd[1]; + var afterThis = cmd[2]; + div = cmd[3]; + if (afterThis == null) // First + parent.insertBefore(div, parent.firstChild); + else + parent.insertBefore(div, afterThis.nextSibling); + break; case DISPLAY_OP_APPEND_ROOT: document.body.appendChild(cmd[1]); break; @@ -867,7 +994,21 @@ function handleDisplayCommands(display_commands) var id = cmd[1]; delete surfaces[id]; break; - + case DISPLAY_OP_CHANGE_TEXTURE: + var image = cmd[1]; + var texture = cmd[2]; + // We need a new closure here to have a separate copy of "template" for each iteration... + function a_block(t) { + image.src = t.url; + // Unref blob url when loaded + image.onload = function() { t.unref(); }; + }(texture); + break; + case DISPLAY_OP_CHANGE_TRANSFORM: + var div = cmd[1]; + var transform_string = cmd[2]; + div.style["transform"] = transform_string; + break; default: alert("Unknown display op " + command); } @@ -882,7 +1023,7 @@ function handleCommands(cmd, display_commands, new_textures, modified_trees) while (res && cmd.pos < cmd.length) { var id, x, y, w, h, q, surface; var saved_pos = cmd.pos; - var command = cmd.get_char(); + var command = cmd.get_uint8(); lastSerial = cmd.get_32(); switch (command) { case BROADWAY_OP_DISCONNECTED: @@ -976,6 +1117,7 @@ function handleCommands(cmd, display_commands, new_textures, modified_trees) display_commands.push([DISPLAY_OP_RESIZE_NODE, surface.div, surface.width, surface.height]); } + sendConfigureNotify(surface); break; case BROADWAY_OP_RAISE_SURFACE: @@ -1013,7 +1155,7 @@ function handleCommands(cmd, display_commands, new_textures, modified_trees) var node_data = cmd.get_nodes (); surface = surfaces[id]; - var transform_nodes = new TransformNodes (node_data, surface.div, display_commands); + var transform_nodes = new TransformNodes (node_data, surface.div, surface.nodes, display_commands); transform_nodes.execute(); } break; @@ -1117,8 +1259,8 @@ function BinCommands(message) { this.pos = 0; } -BinCommands.prototype.get_char = function() { - return String.fromCharCode(this.dataview.getUint8(this.pos++)); +BinCommands.prototype.get_uint8 = function() { + return this.dataview.getUint8(this.pos++); }; BinCommands.prototype.get_bool = function() { return this.dataview.getUint8(this.pos++) != 0; @@ -1185,7 +1327,7 @@ function sendInput(cmd, args) if (inputSocket == null) return; - var fullArgs = [cmd.charCodeAt(0), lastSerial, lastTimeStamp].concat(args); + var fullArgs = [cmd, lastSerial, lastTimeStamp].concat(args); var buffer = new ArrayBuffer(fullArgs.length * 4); var view = new DataView(buffer); fullArgs.forEach(function(arg, i) { diff --git a/gdk/broadway/broadwayd.c b/gdk/broadway/broadwayd.c index a3b402f1f6..54d97c5ec1 100644 --- a/gdk/broadway/broadwayd.c +++ b/gdk/broadway/broadwayd.c @@ -234,7 +234,7 @@ client_handle_request (BroadwayClient *client, { case BROADWAY_REQUEST_NEW_SURFACE: reply_new_surface.id = - broadway_server_new_surface (server, + broadway_server_new_surface (server, client->id, request->new_surface.x, request->new_surface.y, request->new_surface.width, diff --git a/gdk/broadway/gdkbroadway-server.c b/gdk/broadway/gdkbroadway-server.c index 548f180375..4b8fe4fd9b 100644 --- a/gdk/broadway/gdkbroadway-server.c +++ b/gdk/broadway/gdkbroadway-server.c @@ -38,6 +38,7 @@ typedef struct BroadwayInput BroadwayInput; struct _GdkBroadwayServer { GObject parent_instance; + GdkDisplay *display; guint32 next_serial; guint32 next_texture_id; @@ -96,7 +97,9 @@ _gdk_broadway_server_get_next_serial (GdkBroadwayServer *server) } GdkBroadwayServer * -_gdk_broadway_server_new (const char *display, GError **error) +_gdk_broadway_server_new (GdkDisplay *display, + const char *display_name, + GError **error) { GdkBroadwayServer *server; GSocketClient *client; @@ -108,14 +111,14 @@ _gdk_broadway_server_new (const char *display, GError **error) char *local_socket_type = NULL; int port; - if (display == NULL) - display = ":0"; + if (display_name == NULL) + display_name = ":0"; - if (display[0] == ':' && g_ascii_isdigit(display[1])) + if (display_name[0] == ':' && g_ascii_isdigit(display_name[1])) { char *path, *basename; - port = strtol (display + strlen (":"), NULL, 10); + port = strtol (display_name + strlen (":"), NULL, 10); basename = g_strdup_printf ("broadway%d.socket", port + 1); path = g_build_filename (g_get_user_runtime_dir (), basename, NULL); g_free (basename); @@ -127,7 +130,7 @@ _gdk_broadway_server_new (const char *display, GError **error) else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - _("Broadway display type not supported: %s"), display); + _("Broadway display type not supported: %s"), display_name); return NULL; } @@ -145,6 +148,7 @@ _gdk_broadway_server_new (const char *display, GError **error) server = g_object_new (GDK_TYPE_BROADWAY_SERVER, NULL); server->connection = connection; + server->display = display; in = g_io_stream_get_input_stream (G_IO_STREAM (server->connection)); pollable = G_POLLABLE_INPUT_STREAM (in); @@ -346,7 +350,7 @@ process_input_messages (GdkBroadwayServer *server) server->incomming); if (reply->base.type == BROADWAY_REPLY_EVENT) - _gdk_broadway_events_got_input (&reply->event.msg); + _gdk_broadway_events_got_input (server->display, &reply->event.msg); else g_warning ("Unhandled reply type %d", reply->base.type); g_free (reply); diff --git a/gdk/broadway/gdkbroadway-server.h b/gdk/broadway/gdkbroadway-server.h index 537c8a199a..b30e38a5a6 100644 --- a/gdk/broadway/gdkbroadway-server.h +++ b/gdk/broadway/gdkbroadway-server.h @@ -14,7 +14,8 @@ typedef struct _GdkBroadwayServerClass GdkBroadwayServerClass; #define GDK_IS_BROADWAY_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_BROADWAY_SERVER)) #define GDK_BROADWAY_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_BROADWAY_SERVER, GdkBroadwayServerClass)) -GdkBroadwayServer *_gdk_broadway_server_new (const char *display, +GdkBroadwayServer *_gdk_broadway_server_new (GdkDisplay *display, + const char *display_name, GError **error); void _gdk_broadway_server_flush (GdkBroadwayServer *server); void _gdk_broadway_server_sync (GdkBroadwayServer *server); diff --git a/gdk/broadway/gdkdisplay-broadway.c b/gdk/broadway/gdkdisplay-broadway.c index 9a80ec8d55..e3ba560f88 100644 --- a/gdk/broadway/gdkdisplay-broadway.c +++ b/gdk/broadway/gdkdisplay-broadway.c @@ -174,7 +174,7 @@ _gdk_broadway_display_open (const gchar *display_name) if (display_name == NULL) display_name = g_getenv ("BROADWAY_DISPLAY"); - broadway_display->server = _gdk_broadway_server_new (display_name, &error); + broadway_display->server = _gdk_broadway_server_new (display, display_name, &error); if (broadway_display->server == NULL) { g_printerr ("Unable to init Broadway server: %s\n", error->message); @@ -386,6 +386,31 @@ gdk_broadway_display_ensure_texture (GdkDisplay *display, return data->id; } +static gboolean +flush_idle (gpointer data) +{ + GdkDisplay *display = data; + GdkBroadwayDisplay *broadway_display = GDK_BROADWAY_DISPLAY (display); + + broadway_display->idle_flush_id = 0; + gdk_display_flush (display); + + return FALSE; +} + +void +gdk_broadway_display_flush_in_idle (GdkDisplay *display) +{ + GdkBroadwayDisplay *broadway_display = GDK_BROADWAY_DISPLAY (display); + + if (broadway_display->idle_flush_id == 0) + { + broadway_display->idle_flush_id = g_idle_add (flush_idle, g_object_ref (display)); + g_source_set_name_by_id (broadway_display->idle_flush_id, "[gtk] flush_idle"); + } +} + + static void gdk_broadway_display_class_init (GdkBroadwayDisplayClass * class) { diff --git a/gdk/broadway/gdkdisplay-broadway.h b/gdk/broadway/gdkdisplay-broadway.h index e028381c54..a8d6819c82 100644 --- a/gdk/broadway/gdkdisplay-broadway.h +++ b/gdk/broadway/gdkdisplay-broadway.h @@ -56,6 +56,8 @@ struct _GdkBroadwayDisplay GdkMonitor *monitor; GHashTable *texture_cache; + + guint idle_flush_id; }; struct _GdkBroadwayDisplayClass diff --git a/gdk/broadway/gdkeventsource.c b/gdk/broadway/gdkeventsource.c index 9356ad975b..f0672bd8c4 100644 --- a/gdk/broadway/gdkeventsource.c +++ b/gdk/broadway/gdkeventsource.c @@ -83,30 +83,14 @@ gdk_event_source_check (GSource *source) } void -_gdk_broadway_events_got_input (BroadwayInputMsg *message) +_gdk_broadway_events_got_input (GdkDisplay *display, + BroadwayInputMsg *message) { - GdkDisplay *display; GdkBroadwayDisplay *display_broadway; GdkSeat *seat; GdkSurface *surface; GdkEvent *event = NULL; GList *node; - GSList *list, *d; - - display = NULL; - - list = gdk_display_manager_list_displays (gdk_display_manager_get ()); - for (d = list; d; d = d->next) - { - if (GDK_IS_BROADWAY_DISPLAY (d->data)) - { - display = d->data; - break; - } - } - g_slist_free (list); - - g_assert (display != NULL); display_broadway = GDK_BROADWAY_DISPLAY (display); seat = gdk_display_get_default_seat (display); @@ -176,14 +160,14 @@ _gdk_broadway_events_got_input (BroadwayInputMsg *message) break; case BROADWAY_EVENT_BUTTON_PRESS: case BROADWAY_EVENT_BUTTON_RELEASE: - if (message->base.type != 'b' && + if (message->base.type != BROADWAY_EVENT_BUTTON_PRESS && _gdk_broadway_moveresize_handle_event (display, message)) break; surface = g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.event_surface_id)); if (surface) { - event = gdk_event_new (message->base.type == 'b' ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE); + event = gdk_event_new (message->base.type == BROADWAY_EVENT_BUTTON_PRESS ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE); event->any.surface = g_object_ref (surface); event->button.time = message->base.time; event->button.x = message->pointer.win_x; @@ -278,7 +262,7 @@ _gdk_broadway_events_got_input (BroadwayInputMsg *message) GINT_TO_POINTER (message->key.surface_id)); if (surface) { - event = gdk_event_new (message->base.type == 'k' ? GDK_KEY_PRESS : GDK_KEY_RELEASE); + event = gdk_event_new (message->base.type == BROADWAY_EVENT_KEY_PRESS ? GDK_KEY_PRESS : GDK_KEY_RELEASE); event->any.surface = g_object_ref (surface); event->key.time = message->base.time; event->key.keyval = message->key.key; diff --git a/gdk/broadway/gdkprivate-broadway.h b/gdk/broadway/gdkprivate-broadway.h index 11a48a0291..6f9e126dff 100644 --- a/gdk/broadway/gdkprivate-broadway.h +++ b/gdk/broadway/gdkprivate-broadway.h @@ -41,6 +41,8 @@ guint32 gdk_broadway_display_ensure_texture (GdkDisplay *display, GdkTexture *texture); +void gdk_broadway_display_flush_in_idle (GdkDisplay *display); + void gdk_broadway_surface_set_nodes (GdkSurface *surface, GArray *nodes, GPtrArray *node_textures); @@ -78,7 +80,8 @@ gboolean _gdk_keymap_key_is_modifier (GdkKeymap *keymap, void _gdk_broadway_display_size_changed (GdkDisplay *display, BroadwayInputScreenResizeNotify *msg); -void _gdk_broadway_events_got_input (BroadwayInputMsg *message); +void _gdk_broadway_events_got_input (GdkDisplay *display, + BroadwayInputMsg *message); void _gdk_broadway_display_init_root_window (GdkDisplay *display); void _gdk_broadway_display_init_dnd (GdkDisplay *display); diff --git a/gdk/broadway/gdksurface-broadway.c b/gdk/broadway/gdksurface-broadway.c index bc2ca62eed..732d2a7fd6 100644 --- a/gdk/broadway/gdksurface-broadway.c +++ b/gdk/broadway/gdksurface-broadway.c @@ -73,39 +73,6 @@ G_DEFINE_TYPE (GdkSurfaceImplBroadway, gdk_surface_impl_broadway, GDK_TYPE_SURFACE_IMPL) -static GdkDisplay * -find_broadway_display (void) -{ - GdkDisplay *display; - GSList *list, *l; - - display = NULL; - - list = gdk_display_manager_list_displays (gdk_display_manager_get ()); - for (l = list; l; l = l->next) - { - if (GDK_IS_BROADWAY_DISPLAY (l->data)) - { - display = l->data; - break; - } - } - g_slist_free (list); - - return display; -} - -static guint flush_id = 0; - -static gboolean -flush_idle (gpointer data) -{ - flush_id = 0; - - gdk_display_flush (find_broadway_display ()); - - return FALSE; -} /* We need to flush in an idle rather than AFTER_PAINT, as the clock is frozen during e.g. surface resizes so the paint will not happen @@ -113,11 +80,7 @@ flush_idle (gpointer data) static void queue_flush (GdkSurface *surface) { - if (flush_id == 0) - { - flush_id = g_idle_add (flush_idle, NULL); - g_source_set_name_by_id (flush_id, "[gtk] flush_idle"); - } + gdk_broadway_display_flush_in_idle (gdk_surface_get_display (surface)); } static void @@ -165,13 +128,32 @@ _gdk_broadway_roundtrip_notify (GdkSurface *surface, guint32 tag, gboolean local_reply) { + GdkSurfaceImplBroadway *impl = GDK_SURFACE_IMPL_BROADWAY (surface->impl); GdkFrameClock *clock = gdk_surface_get_frame_clock (surface); + GdkFrameTimings *timings; + + timings = gdk_frame_clock_get_timings (clock, impl->pending_frame_counter); + impl->pending_frame_counter = 0; /* If there is no remove web client, rate limit update to once a second */ if (local_reply) g_timeout_add_seconds (1, (GSourceFunc)thaw_clock_cb, g_object_ref (clock)); else _gdk_frame_clock_thaw (clock); + + if (timings) + { + timings->refresh_interval = 33333; /* default to 1/30th of a second */ + // This isn't quite right, since we've done a rountrip back too, can we do better? + timings->presentation_time = g_get_monotonic_time (); + timings->complete = TRUE; + + +#ifdef G_ENABLE_DEBUG + if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0) + _gdk_frame_clock_debug_print_timings (clock, timings); +#endif + } } static void @@ -182,6 +164,7 @@ on_frame_clock_after_paint (GdkFrameClock *clock, GdkSurfaceImplBroadway *impl = GDK_SURFACE_IMPL_BROADWAY (surface->impl); GdkBroadwayDisplay *broadway_display; + impl->pending_frame_counter = gdk_frame_clock_get_frame_counter (clock); _gdk_frame_clock_freeze (gdk_surface_get_frame_clock (surface)); broadway_display = GDK_BROADWAY_DISPLAY (display); @@ -191,6 +174,30 @@ on_frame_clock_after_paint (GdkFrameClock *clock, gdk_display_flush (display); } +static void +on_frame_clock_before_paint (GdkFrameClock *clock, + GdkSurface *surface) +{ + GdkFrameTimings *timings = gdk_frame_clock_get_current_timings (clock); + gint64 presentation_time; + gint64 refresh_interval; + + if (surface->update_freeze_count > 0) + return; + + gdk_frame_clock_get_refresh_info (clock, + timings->frame_time, + &refresh_interval, &presentation_time); + if (presentation_time != 0) + { + timings->predicted_presentation_time = presentation_time + refresh_interval; + } + else + { + timings->predicted_presentation_time = timings->frame_time + refresh_interval / 2 + refresh_interval; + } +} + static void connect_frame_clock (GdkSurface *surface) { @@ -198,6 +205,8 @@ connect_frame_clock (GdkSurface *surface) { GdkFrameClock *frame_clock = gdk_surface_get_frame_clock (surface); + g_signal_connect (frame_clock, "before-paint", + G_CALLBACK (on_frame_clock_before_paint), surface); g_signal_connect (frame_clock, "after-paint", G_CALLBACK (on_frame_clock_after_paint), surface); } @@ -1208,8 +1217,8 @@ gdk_broadway_surface_begin_resize_drag (GdkSurface *surface, GdkSurfaceEdge edge, GdkDevice *device, gint button, - gint root_x, - gint root_y, + gint x, + gint y, guint32 timestamp) { MoveResizeData *mv_resize; @@ -1232,8 +1241,8 @@ gdk_broadway_surface_begin_resize_drag (GdkSurface *surface, mv_resize->is_resize = TRUE; mv_resize->moveresize_button = button; mv_resize->resize_edge = edge; - mv_resize->moveresize_x = root_x; - mv_resize->moveresize_y = root_y; + mv_resize->moveresize_x = x + impl->wrapper->x; + mv_resize->moveresize_y = y + impl->wrapper->y; mv_resize->moveresize_surface = g_object_ref (surface); mv_resize->moveresize_orig_width = gdk_surface_get_width (surface); @@ -1251,8 +1260,8 @@ static void gdk_broadway_surface_begin_move_drag (GdkSurface *surface, GdkDevice *device, gint button, - gint root_x, - gint root_y, + gint x, + gint y, guint32 timestamp) { MoveResizeData *mv_resize; @@ -1274,8 +1283,8 @@ gdk_broadway_surface_begin_move_drag (GdkSurface *surface, mv_resize->is_resize = FALSE; mv_resize->moveresize_button = button; - mv_resize->moveresize_x = root_x; - mv_resize->moveresize_y = root_y; + mv_resize->moveresize_x = x + impl->wrapper->x; + mv_resize->moveresize_y = y + impl->wrapper->y; mv_resize->moveresize_surface = g_object_ref (surface); mv_resize->moveresize_orig_width = gdk_surface_get_width (surface); diff --git a/gdk/broadway/gdksurface-broadway.h b/gdk/broadway/gdksurface-broadway.h index a89042e17a..9be2da92df 100644 --- a/gdk/broadway/gdksurface-broadway.h +++ b/gdk/broadway/gdksurface-broadway.h @@ -61,6 +61,8 @@ struct _GdkSurfaceImplBroadway int pre_maximize_width; int pre_maximize_height; + gint64 pending_frame_counter; + gboolean dirty; gboolean last_synced; diff --git a/gsk/gskbroadwayrenderer.c b/gsk/broadway/gskbroadwayrenderer.c similarity index 79% rename from gsk/gskbroadwayrenderer.c rename to gsk/broadway/gskbroadwayrenderer.c index e8cf1fe347..594c2cba8c 100644 --- a/gsk/gskbroadwayrenderer.c +++ b/gsk/broadway/gskbroadwayrenderer.c @@ -326,6 +326,145 @@ add_new_node (GskRenderer *renderer, return TRUE; } +typedef struct ColorizedTexture { + GdkTexture *texture; + graphene_matrix_t color_matrix; + graphene_vec4_t color_offset; +} ColorizedTexture; + +static void +colorized_texture_free (ColorizedTexture *colorized) +{ + g_object_unref (colorized->texture); + g_free (colorized); +} + +static ColorizedTexture * +colorized_texture_new (GdkTexture *texture, + const graphene_matrix_t *color_matrix, + const graphene_vec4_t *color_offset) +{ + ColorizedTexture *colorized = g_new0 (ColorizedTexture, 1); + colorized->texture = g_object_ref (texture); + colorized->color_matrix = *color_matrix; + colorized->color_offset = *color_offset; + return colorized; +} + +static void +colorized_texture_free_list (GList *list) +{ + g_list_free_full (list, (GDestroyNotify)colorized_texture_free); +} + + +static gboolean +matrix_equal (const graphene_matrix_t *a, + const graphene_matrix_t *b) +{ + for (int i = 0; i < 4; i ++) + { + graphene_vec4_t ra, rb; + graphene_matrix_get_row (a, i, &ra); + graphene_matrix_get_row (b, i, &rb); + if (!graphene_vec4_equal (&ra, &rb)) + return FALSE; + } + return TRUE; +} + +static GdkTexture * +get_colorized_texture (GdkTexture *texture, + const graphene_matrix_t *color_matrix, + const graphene_vec4_t *color_offset) +{ + cairo_surface_t *surface = gdk_texture_download_surface (texture); + cairo_surface_t *image_surface; + graphene_vec4_t pixel; + guint32* pixel_data; + guchar *data; + gsize x, y, width, height, stride; + float alpha; + GdkTexture *colorized_texture; + GList *colorized_list, *l; + ColorizedTexture *colorized; + + colorized_list = g_object_get_data (G_OBJECT (texture), "broadway-colorized"); + + for (l = colorized_list; l != NULL; l = l->next) + { + colorized = l->data; + + if (graphene_vec4_equal (&colorized->color_offset, color_offset) && + matrix_equal (&colorized->color_matrix, color_matrix)) + return g_object_ref (colorized->texture); + } + + image_surface = cairo_surface_map_to_image (surface, NULL); + data = cairo_image_surface_get_data (image_surface); + width = cairo_image_surface_get_width (image_surface); + height = cairo_image_surface_get_height (image_surface); + stride = cairo_image_surface_get_stride (image_surface); + + for (y = 0; y < height; y++) + { + pixel_data = (guint32 *) data; + for (x = 0; x < width; x++) + { + alpha = ((pixel_data[x] >> 24) & 0xFF) / 255.0; + + if (alpha == 0) + { + graphene_vec4_init (&pixel, 0.0, 0.0, 0.0, 0.0); + } + else + { + graphene_vec4_init (&pixel, + ((pixel_data[x] >> 16) & 0xFF) / (255.0 * alpha), + ((pixel_data[x] >> 8) & 0xFF) / (255.0 * alpha), + ( pixel_data[x] & 0xFF) / (255.0 * alpha), + alpha); + graphene_matrix_transform_vec4 (color_matrix, &pixel, &pixel); + } + + graphene_vec4_add (&pixel, color_offset, &pixel); + + alpha = graphene_vec4_get_w (&pixel); + if (alpha > 0.0) + { + alpha = MIN (alpha, 1.0); + pixel_data[x] = (((guint32) (alpha * 255)) << 24) | + (((guint32) (CLAMP (graphene_vec4_get_x (&pixel), 0, 1) * alpha * 255)) << 16) | + (((guint32) (CLAMP (graphene_vec4_get_y (&pixel), 0, 1) * alpha * 255)) << 8) | + ((guint32) (CLAMP (graphene_vec4_get_z (&pixel), 0, 1) * alpha * 255)); + } + else + { + pixel_data[x] = 0; + } + } + data += stride; + } + + cairo_surface_mark_dirty (image_surface); + cairo_surface_unmap_image (surface, image_surface); + + colorized_texture = gdk_texture_new_for_surface (surface); + + colorized = colorized_texture_new (colorized_texture, color_matrix, color_offset); + if (colorized_list) + colorized_list = g_list_append (colorized_list, colorized); + else + { + colorized_list = g_list_append (NULL, colorized); + g_object_set_data_full (G_OBJECT (texture), "broadway-colorized", + colorized_list, (GDestroyNotify)colorized_texture_free_list); + } + + return colorized_texture; +} + + /* Note: This tracks the offset so that we can convert the absolute coordinates of the GskRenderNodes to parent-relative which is what the dom uses, and @@ -571,6 +710,26 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, return; case GSK_COLOR_MATRIX_NODE: + { + GskRenderNode *child = gsk_color_matrix_node_get_child (node); + if (gsk_render_node_get_node_type (child) == GSK_TEXTURE_NODE) + { + const graphene_matrix_t *color_matrix = gsk_color_matrix_node_peek_color_matrix (node); + const graphene_vec4_t *color_offset = gsk_color_matrix_node_peek_color_offset (node); + GdkTexture *texture = gsk_texture_node_get_texture (child); + GdkTexture *colorized_texture = get_colorized_texture (texture, color_matrix, color_offset); + if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE)) + { + guint32 texture_id = gdk_broadway_display_ensure_texture (display, colorized_texture); + add_rect (nodes, &child->bounds, offset_x, offset_y); + add_uint32 (nodes, texture_id); + } + + return; + } + } + break; /* Fallback */ + case GSK_TEXT_NODE: case GSK_REPEATING_LINEAR_GRADIENT_NODE: case GSK_REPEAT_NODE: diff --git a/gsk/gskbroadwayrendererprivate.h b/gsk/broadway/gskbroadwayrendererprivate.h similarity index 100% rename from gsk/gskbroadwayrendererprivate.h rename to gsk/broadway/gskbroadwayrendererprivate.h diff --git a/gsk/gskrenderer.c b/gsk/gskrenderer.c index 95eaa0a1e5..0fdfbc63e7 100644 --- a/gsk/gskrenderer.c +++ b/gsk/gskrenderer.c @@ -55,7 +55,7 @@ #include #endif #ifdef GDK_WINDOWING_BROADWAY -#include "gskbroadwayrendererprivate.h" +#include "broadway/gskbroadwayrendererprivate.h" #endif #ifdef GDK_RENDERING_VULKAN #include "vulkan/gskvulkanrendererprivate.h" diff --git a/gsk/meson.build b/gsk/meson.build index e4b4ca46b7..912c5ed0f8 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -97,7 +97,7 @@ endif # have_vulkan if get_variable('broadway_enabled') gsk_private_sources += files([ - 'gskbroadwayrenderer.c', + 'broadway/gskbroadwayrenderer.c', ]) endif