[gtk+/wip/alexl/broadway4] broadway: Initial version of using actual render nodes



commit 7ba466d6958a81693ef5ff7e0176803ecf35d96e
Author: Alexander Larsson <alexl redhat com>
Date:   Tue Nov 21 19:33:12 2017 +0100

    broadway: Initial version of using actual render nodes

 gdk/broadway/broadway-output.c     |   15 +
 gdk/broadway/broadway-output.h     |    4 +
 gdk/broadway/broadway-protocol.h   |   25 +-
 gdk/broadway/broadway-server.c     |   48 ++-
 gdk/broadway/broadway-server.h     |    4 +
 gdk/broadway/broadway.js           |  916 +++++++++++++++---------------------
 gdk/broadway/broadwayd.c           |   13 +-
 gdk/broadway/gdkbroadway-server.c  |   17 +
 gdk/broadway/gdkbroadway-server.h  |    3 +
 gdk/broadway/gdkdisplay-broadway.c |   39 ++
 gdk/broadway/gdkprivate-broadway.h |    7 +
 gdk/broadway/gdkwindow-broadway.c  |  516 +++++++++++----------
 gdk/broadway/gdkwindow-broadway.h  |    3 +
 gsk/gskbroadwayrenderer.c          |  120 ++++-
 14 files changed, 921 insertions(+), 809 deletions(-)
---
diff --git a/gdk/broadway/broadway-output.c b/gdk/broadway/broadway-output.c
index 770fa43..89326c5 100644
--- a/gdk/broadway/broadway-output.c
+++ b/gdk/broadway/broadway-output.c
@@ -298,6 +298,21 @@ broadway_output_window_update (BroadwayOutput *output,
 }
 
 void
+broadway_output_window_set_nodes (BroadwayOutput *output,
+                                  int             id,
+                                  guint32        *data,
+                                  guint32         data_len)
+{
+  write_header (output, BROADWAY_OP_SET_NODES);
+  guint32 i;
+
+  append_uint16 (output, id);
+  append_uint32 (output, data_len);
+  for (i = 0; i < data_len; i++)
+    append_uint32 (output, data[i]);
+}
+
+void
 broadway_output_upload_texture (BroadwayOutput *output,
                                guint32 id,
                                GBytes *texture)
diff --git a/gdk/broadway/broadway-output.h b/gdk/broadway/broadway-output.h
index 7c69e3b..eb29c17 100644
--- a/gdk/broadway/broadway-output.h
+++ b/gdk/broadway/broadway-output.h
@@ -56,6 +56,10 @@ void            broadway_output_set_transient_for (BroadwayOutput *output,
 void            broadway_output_window_update   (BroadwayOutput *output,
                                                 int             id,
                                                 guint32         texture);
+void            broadway_output_window_set_nodes (BroadwayOutput *output,
+                                                  int             id,
+                                                  guint32        *data,
+                                                  guint32         data_len);
 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 3bf963f..7f7a5e9 100644
--- a/gdk/broadway/broadway-protocol.h
+++ b/gdk/broadway/broadway-protocol.h
@@ -8,6 +8,11 @@ typedef struct  {
     gint32 width, height;
 } BroadwayRect;
 
+typedef enum { /* Sync changes with broadway.js */
+  BROADWAY_NODE_TEXTURE,
+  BROADWAY_NODE_CONTAINER,
+} BroadwayNodeType;
+
 typedef enum {
   BROADWAY_EVENT_ENTER = 'e',
   BROADWAY_EVENT_LEAVE = 'l',
@@ -45,6 +50,7 @@ typedef enum {
   BROADWAY_OP_SET_SHOW_KEYBOARD = 'k',
   BROADWAY_OP_UPLOAD_TEXTURE = 't',
   BROADWAY_OP_RELEASE_TEXTURE = 'T',
+  BROADWAY_OP_SET_NODES = 'n',
 } BroadwayOpType;
 
 typedef struct {
@@ -162,6 +168,7 @@ typedef enum {
   BROADWAY_REQUEST_SET_SHOW_KEYBOARD,
   BROADWAY_REQUEST_UPLOAD_TEXTURE,
   BROADWAY_REQUEST_RELEASE_TEXTURE,
+  BROADWAY_REQUEST_SET_NODES,
 } BroadwayRequestType;
 
 typedef struct {
@@ -184,15 +191,6 @@ typedef struct {
 typedef struct {
   BroadwayRequestBase base;
   guint32 id;
-  gint32 dx;
-  gint32 dy;
-  guint32 n_rects;
-  BroadwayRect rects[1];
-} BroadwayRequestTranslate;
-
-typedef struct {
-  BroadwayRequestBase base;
-  guint32 id;
   guint32 texture;
 } BroadwayRequestUpdate;
 
@@ -211,6 +209,13 @@ typedef struct {
 typedef struct {
   BroadwayRequestBase base;
   guint32 id;
+  guint32 data[1];
+} BroadwayRequestSetNodes;
+
+
+typedef struct {
+  BroadwayRequestBase base;
+  guint32 id;
   guint32 owner_events;
   guint32 event_mask;
   guint32 time_;
@@ -259,11 +264,11 @@ typedef union {
   BroadwayRequestMoveResize move_resize;
   BroadwayRequestGrabPointer grab_pointer;
   BroadwayRequestUngrabPointer ungrab_pointer;
-  BroadwayRequestTranslate translate;
   BroadwayRequestFocusWindow focus_window;
   BroadwayRequestSetShowKeyboard set_show_keyboard;
   BroadwayRequestUploadTexture upload_texture;
   BroadwayRequestReleaseTexture release_texture;
+  BroadwayRequestSetNodes set_nodes;
 } BroadwayRequest;
 
 typedef enum {
diff --git a/gdk/broadway/broadway-server.c b/gdk/broadway/broadway-server.c
index 8d86a9e..4aa1b26 100644
--- a/gdk/broadway/broadway-server.c
+++ b/gdk/broadway/broadway-server.c
@@ -117,6 +117,8 @@ struct BroadwayWindow {
   gboolean visible;
   gint32 transient_for;
   guint32 texture;
+  guint32 *nodes;
+  gint nodes_len;
 };
 
 static void broadway_server_resync_windows (BroadwayServer *server);
@@ -173,6 +175,13 @@ broadway_server_class_init (BroadwayServerClass * class)
   object_class->finalize = broadway_server_finalize;
 }
 
+static void
+broadway_window_free (BroadwayWindow *window)
+{
+  g_free (window->nodes);
+  g_free (window);
+}
+
 static void start (BroadwayInput *input);
 
 static void
@@ -1375,9 +1384,8 @@ broadway_server_destroy_window (BroadwayServer *server,
     {
       server->toplevels = g_list_remove (server->toplevels, window);
       g_hash_table_remove (server->id_ht,
-                          GINT_TO_POINTER (id));
-
-      g_free (window);
+                           GINT_TO_POINTER (id));
+      broadway_window_free (window);
     }
 }
 
@@ -1512,13 +1520,12 @@ broadway_server_has_client (BroadwayServer *server)
 
 void
 broadway_server_window_update (BroadwayServer   *server,
-                              gint              id,
-                              guint32           texture)
+                               gint              id,
+                               guint32           texture)
 {
   BroadwayWindow *window;
 
-  window = g_hash_table_lookup (server->id_ht,
-                               GINT_TO_POINTER (id));
+  window = g_hash_table_lookup (server->id_ht, GINT_TO_POINTER (id));
   if (window == NULL)
     return;
 
@@ -1526,7 +1533,28 @@ broadway_server_window_update (BroadwayServer   *server,
 
   if (server->output != NULL)
     broadway_output_window_update (server->output, window->id,
-                                  window->texture);
+                                   window->texture);
+}
+
+void
+broadway_server_window_set_nodes (BroadwayServer   *server,
+                                  gint              id,
+                                  gint              n_data,
+                                  guint32          *data)
+{
+  BroadwayWindow *window;
+
+  window = g_hash_table_lookup (server->id_ht, GINT_TO_POINTER (id));
+  if (window == NULL)
+    return;
+
+  g_free (window->nodes);
+  window->nodes = g_memdup (data, sizeof (guint32)*n_data);
+  window->nodes_len = n_data;
+
+  if (server->output != NULL)
+    broadway_output_window_set_nodes (server->output, window->id,
+                                      window->nodes, window->nodes_len);
 }
 
 guint32
@@ -1771,6 +1799,10 @@ broadway_server_resync_windows (BroadwayServer *server)
        broadway_output_set_transient_for (server->output, window->id,
                                           window->transient_for);
 
+      if (window->nodes)
+        broadway_output_window_set_nodes (server->output, window->id,
+                                          window->nodes, window->nodes_len);
+
       broadway_output_window_update (server->output, window->id,
                                     window->texture);
 
diff --git a/gdk/broadway/broadway-server.h b/gdk/broadway/broadway-server.h
index 422488c..1432a0a 100644
--- a/gdk/broadway/broadway-server.h
+++ b/gdk/broadway/broadway-server.h
@@ -85,6 +85,10 @@ cairo_surface_t   * broadway_server_create_surface           (int
 void                broadway_server_window_update            (BroadwayServer   *server,
                                                              gint              id,
                                                              guint32           texture);
+void                broadway_server_window_set_nodes         (BroadwayServer   *server,
+                                                             gint              id,
+                                                              gint              n_data,
+                                                              guint32          *data);
 gboolean            broadway_server_window_move_resize       (BroadwayServer   *server,
                                                              gint              id,
                                                              gboolean          with_move,
diff --git a/gdk/broadway/broadway.js b/gdk/broadway/broadway.js
index b268e0d..6192977 100644
--- a/gdk/broadway/broadway.js
+++ b/gdk/broadway/broadway.js
@@ -2,10 +2,10 @@
 var logDiv = null;
 function log(str) {
     if (!logDiv) {
-       logDiv = document.createElement('div');
-       document.body.appendChild(logDiv);
-       logDiv.style["position"] = "absolute";
-       logDiv.style["right"] = "0px";
+        logDiv = document.createElement('div');
+        document.body.appendChild(logDiv);
+        logDiv.style["position"] = "absolute";
+        logDiv.style["right"] = "0px";
     }
     logDiv.appendChild(document.createTextNode(str));
     logDiv.appendChild(document.createElement('br'));
@@ -16,44 +16,44 @@ function getStackTrace()
     var callstack = [];
     var isCallstackPopulated = false;
     try {
-       i.dont.exist+=0;
+        i.dont.exist+=0;
     } catch(e) {
-       if (e.stack) { // Firefox
-           var lines = e.stack.split("\n");
-           for (var i=0, len=lines.length; i<len; i++) {
-               if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
-                   callstack.push(lines[i]);
-               }
-           }
-           // Remove call to getStackTrace()
-           callstack.shift();
-           isCallstackPopulated = true;
-       } else if (window.opera && e.message) { // Opera
-           var lines = e.message.split("\n");
-           for (var i=0, len=lines.length; i<len; i++) {
-               if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
-                   var entry = lines[i];
-                   // Append next line also since it has the file info
-                   if (lines[i+1]) {
-                       entry += " at " + lines[i+1];
-                       i++;
-                   }
-                   callstack.push(entry);
-               }
-           }
-           // Remove call to getStackTrace()
-           callstack.shift();
-           isCallstackPopulated = true;
-       }
+        if (e.stack) { // Firefox
+            var lines = e.stack.split("\n");
+            for (var i=0, len=lines.length; i<len; i++) {
+                if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
+                    callstack.push(lines[i]);
+                }
+            }
+            // Remove call to getStackTrace()
+            callstack.shift();
+            isCallstackPopulated = true;
+        } else if (window.opera && e.message) { // Opera
+            var lines = e.message.split("\n");
+            for (var i=0, len=lines.length; i<len; i++) {
+                if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
+                    var entry = lines[i];
+                    // Append next line also since it has the file info
+                    if (lines[i+1]) {
+                        entry += " at " + lines[i+1];
+                        i++;
+                    }
+                    callstack.push(entry);
+                }
+            }
+            // Remove call to getStackTrace()
+            callstack.shift();
+            isCallstackPopulated = true;
+        }
     }
     if (!isCallstackPopulated) { //IE and Safari
-       var currentFunction = arguments.callee.caller;
-       while (currentFunction) {
-           var fn = currentFunction.toString();
-           var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf("(")) || "anonymous";
-           callstack.push(fname);
-           currentFunction = currentFunction.caller;
-       }
+        var currentFunction = arguments.callee.caller;
+        while (currentFunction) {
+            var fn = currentFunction.toString();
+            var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf("(")) || "anonymous";
+            callstack.push(fname);
+            currentFunction = currentFunction.caller;
+        }
     }
     return callstack;
 }
@@ -62,9 +62,9 @@ function logStackTrace(len) {
     var callstack = getStackTrace();
     var end = callstack.length;
     if (len > 0)
-       end = Math.min(len + 1, end);
+        end = Math.min(len + 1, end);
     for (var i = 1; i < end; i++)
-       log(callstack[i]);
+        log(callstack[i]);
 }
 
 var grab = new Object();
@@ -115,15 +115,15 @@ var GDK_RELEASE_MASK  = 1 << 30;
 
 function getButtonMask (button) {
     if (button == 1)
-       return GDK_BUTTON1_MASK;
+        return GDK_BUTTON1_MASK;
     if (button == 2)
-       return GDK_BUTTON2_MASK;
+        return GDK_BUTTON2_MASK;
     if (button == 3)
-       return GDK_BUTTON3_MASK;
+        return GDK_BUTTON3_MASK;
     if (button == 4)
-       return GDK_BUTTON4_MASK;
+        return GDK_BUTTON4_MASK;
     if (button == 5)
-       return GDK_BUTTON5_MASK;
+        return GDK_BUTTON5_MASK;
     return 0;
 }
 
@@ -141,25 +141,19 @@ function cmdCreateSurface(id, x, y, width, height, isTemp)
     surface.visible = false;
     surface.imageData = null;
 
-    var image = new Image();
-    image.width = width;
-    image.height = height;
-    image.surface = surface;
-    surface.image = image;
-    var toplevelElement;
-
-    toplevelElement = image;
-    document.body.appendChild(image);
+    var div = document.createElement('div');
+    div.surface = surface;
+    surface.div = div;
 
-    surface.toplevelElement = toplevelElement;
-    toplevelElement.style["position"] = "absolute";
-    /* This positioning isn't strictly right for apps in another topwindow,
-     * but that will be fixed up when showing. */
-    toplevelElement.style["left"] = surface.x + "px";
-    toplevelElement.style["top"] = surface.y + "px";
-    toplevelElement.style["display"] = "inline";
+    document.body.appendChild(div);
 
-    toplevelElement.style["visibility"] = "hidden";
+    div.style["position"] = "absolute";
+    div.style["left"] = surface.x + "px";
+    div.style["top"] = surface.y + "px";
+    div.style["width"] = surface.width + "px";
+    div.style["height"] = surface.height + "px";
+    div.style["display"] = "block";
+    div.style["visibility"] = "hidden";
 
     surfaces[id] = surface;
     stackingOrder.push(surface);
@@ -172,15 +166,15 @@ function cmdShowSurface(id)
     var surface = surfaces[id];
 
     if (surface.visible)
-       return;
+        return;
     surface.visible = true;
 
     var xOffset = surface.x;
     var yOffset = surface.y;
 
-    surface.toplevelElement.style["left"] = xOffset + "px";
-    surface.toplevelElement.style["top"] = yOffset + "px";
-    surface.toplevelElement.style["visibility"] = "visible";
+    surface.div.style["left"] = xOffset + "px";
+    surface.div.style["top"] = yOffset + "px";
+    surface.div.style["visibility"] = "visible";
 
     restackWindows();
 }
@@ -188,17 +182,14 @@ function cmdShowSurface(id)
 function cmdHideSurface(id)
 {
     if (grab.window == id)
-       doUngrab();
+        doUngrab();
 
     var surface = surfaces[id];
-
     if (!surface.visible)
-       return;
+        return;
     surface.visible = false;
 
-    var element = surface.toplevelElement;
-
-    element.style["visibility"] = "hidden";
+    surface.div.style["visibility"] = "hidden";
 }
 
 function cmdSetTransientFor(id, parentId)
@@ -206,22 +197,22 @@ function cmdSetTransientFor(id, parentId)
     var surface = surfaces[id];
 
     if (surface.transientParent == parentId)
-       return;
+        return;
 
     surface.transientParent = parentId;
     if (parentId != 0 && surfaces[parentId]) {
-       moveToHelper(surface, stackingOrder.indexOf(surfaces[parentId])+1);
+        moveToHelper(surface, stackingOrder.indexOf(surfaces[parentId])+1);
     }
 
     if (surface.visible) {
-       restackWindows();
+        restackWindows();
     }
 }
 
 function restackWindows() {
     for (var i = 0; i < stackingOrder.length; i++) {
-       var surface = stackingOrder[i];
-       surface.toplevelElement.style.zIndex = i;
+        var surface = stackingOrder[i];
+        surface.div.style.zIndex = i;
     }
 }
 
@@ -229,28 +220,28 @@ function moveToHelper(surface, position) {
     var i = stackingOrder.indexOf(surface);
     stackingOrder.splice(i, 1);
     if (position != undefined)
-       stackingOrder.splice(position, 0, surface);
+        stackingOrder.splice(position, 0, surface);
     else
-       stackingOrder.push(surface);
+        stackingOrder.push(surface);
 
     for (var cid in surfaces) {
-       var child = surfaces[cid];
-       if (child.transientParent == surface.id)
-           moveToHelper(child, stackingOrder.indexOf(surface) + 1);
+        var child = surfaces[cid];
+        if (child.transientParent == surface.id)
+            moveToHelper(child, stackingOrder.indexOf(surface) + 1);
     }
 }
 
 function cmdDeleteSurface(id)
 {
     if (grab.window == id)
-       doUngrab();
+        doUngrab();
 
     var surface = surfaces[id];
     var i = stackingOrder.indexOf(surface);
     if (i >= 0)
-       stackingOrder.splice(i, 1);
-    var image = surface.image;
-    image.parentNode.removeChild(image);
+        stackingOrder.splice(i, 1);
+    var div = surface.div;
+    div.parentNode.removeChild(div);
     delete surfaces[id];
 }
 
@@ -258,30 +249,27 @@ function cmdMoveResizeSurface(id, has_pos, x, y, has_size, w, h)
 {
     var surface = surfaces[id];
     if (has_pos) {
-       surface.positioned = true;
-       surface.x = x;
-       surface.y = y;
-    }
-    if (has_size) {
-       surface.width = w;
-       surface.height = h;
+        surface.positioned = true;
+        surface.x = x;
+        surface.y = y;
     }
 
     if (has_size) {
-       surface.image.width = w;
-       surface.image.height = h;
+        surface.width = w;
+        surface.height = h;
+
+        surface.div.style["width"] = surface.width + "px";
+        surface.div.style["height"] = surface.height + "px";
     }
 
     if (surface.visible) {
-       if (has_pos) {
-           var xOffset = surface.x;
-           var yOffset = surface.y;
+        if (has_pos) {
+            var xOffset = surface.x;
+            var yOffset = surface.y;
 
-           var element = surface.image;
-
-           element.style["left"] = xOffset + "px";
-           element.style["top"] = yOffset + "px";
-       }
+            surface.div.style["left"] = xOffset + "px";
+            surface.div.style["top"] = yOffset + "px";
+        }
     }
 
     sendConfigureNotify(surface);
@@ -303,204 +291,70 @@ function cmdLowerSurface(id)
     restackWindows();
 }
 
-function copyRect(src, srcX, srcY, dest, destX, destY, width, height)
+function cmdWindowUpdate(id, texture_id)
 {
-    // Clip to src
-    if (srcX + width > src.width)
-        width = src.width - srcX;
-    if (srcY + height > src.height)
-        height = src.height - srcY;
-
-    // Clip to dest
-    if (destX + width > dest.width)
-        width = dest.width - destX;
-    if (destY + height > dest.height)
-        height = dest.height - destY;
-
-    var srcRect = src.width * 4 * srcY + srcX * 4;
-    var destRect = dest.width * 4 * destY + destX * 4;
-
-    for (var i = 0; i < height; i++) {
-        var line = src.data.subarray(srcRect, srcRect + width *4);
-        dest.data.set(line, destRect);
-        srcRect += src.width * 4;
-        destRect += dest.width * 4;
-    }
-}
-
+    var surface = surfaces[id];
+    var texture_url = textures[texture_id];
 
-function markRun(dest, start, length, r, g, b)
-{
-    for (var i = start; i < start + length * 4; i += 4) {
-        dest[i+0] = dest[i+0] / 2 | 0 + r;
-        dest[i+1] = dest[i+1] / 2 | 0 + g;
-        dest[i+2] = dest[i+2] / 2 | 0 + b;
-    }
+    surface.div.src = texture_url;
 }
 
-function markRect(src, srcX, srcY, dest, destX, destY, width, height, r, g, b)
+function handleNode(parent, node_data, data_pos)
 {
-    // Clip to src
-    if (srcX + width > src.width)
-        width = src.width - srcX;
-    if (srcY + height > src.height)
-        height = src.height - srcY;
-
-    // Clip to dest
-    if (destX + width > dest.width)
-        width = dest.width - destX;
-    if (destY + height > dest.height)
-        height = dest.height - destY;
-
-    var destRect = dest.width * 4 * destY + destX * 4;
-
-    for (var i = 0; i < height; i++) {
-        if (i == 0 || i == height-1)
-            markRun(dest.data, destRect, width, 0, 0, 0);
-        else {
-            markRun(dest.data, destRect, 1, 0 ,0, 0);
-            markRun(dest.data, destRect+4, width-2, r, g, b);
-            markRun(dest.data, destRect+4*width-4, 1, 0, 0, 0);
+    var type = node_data[data_pos++];
+    switch (type)
+    {
+    case 0:  // TEXTURE
+        var x = node_data[data_pos++];
+        var y = node_data[data_pos++];
+        var width = node_data[data_pos++];
+        var height = node_data[data_pos++];
+        var texture_id = node_data[data_pos++];
+        var image = new Image();
+        image.width = width;
+        image.height = height;
+        image.style["position"] = "absolute";
+        image.style["left"] = x + "px";
+        image.style["top"] = y + "px";
+        var texture_url = textures[texture_id];
+        image.src = texture_url;
+        parent.appendChild(image);
+        break;
+
+    case 1: // CONTAINER
+        var len = node_data[data_pos++];
+        for (var i = 0; i < len; i++) {
+            data_pos = handleNode(parent, node_data, data_pos);
         }
-        destRect += dest.width * 4;
+        break;
+    default:
+        alert("Unexpected node type " + type);
     }
+    return data_pos;
 }
 
-function decodeBuffer(context, oldData, w, h, data, debug)
+function cmdWindowSetNodes(id, node_data)
 {
-    var i, j;
-    var imageData = context.createImageData(w, h);
-
-    if (oldData != null) {
-        // Copy old frame into new buffer
-        copyRect(oldData, 0, 0, imageData, 0, 0, oldData.width, oldData.height);
-    }
-
-    var src = 0;
-    var dest = 0;
-
-    while (src < data.length)  {
-        var b = data[src++];
-        var g = data[src++];
-        var r = data[src++];
-        var alpha = data[src++];
-        var len, start;
-
-        if (alpha != 0) {
-            // Regular data is red
-            if (debug) {
-                r = r / 2 | 0 + 128;
-                g = g / 2 | 0;
-                b = r / 2 | 0;
-            }
-
-            imageData.data[dest++] = r;
-            imageData.data[dest++] = g;
-            imageData.data[dest++] = b;
-            imageData.data[dest++] = alpha;
-        } else {
-            var cmd = r & 0xf0;
-            switch (cmd) {
-            case 0x00: // Transparent pixel
-                //log("Got transparent");
-                imageData.data[dest++] = 0;
-                imageData.data[dest++] = 0;
-                imageData.data[dest++] = 0;
-                imageData.data[dest++] = 0;
-                break;
-
-            case 0x10: // Delta 0 run
-                len = (r & 0xf) << 16 | g << 8 | b;
-                //log("Got delta0, len: " + len);
-                dest += len * 4;
-                break;
-
-            case 0x20: // Block reference
-                var blockid = (r & 0xf) << 16 | g << 8 | b;
-
-                var block_stride = (oldData.width + 32 - 1) / 32 | 0;
-                var srcY = (blockid / block_stride | 0) * 32;
-                var srcX = (blockid % block_stride | 0) * 32;
-
-                b = data[src++];
-                g = data[src++];
-                r = data[src++];
-                alpha = data[src++];
-
-                var destX = alpha << 8 | r;
-                var destY = g << 8 | b;
-
-                copyRect(oldData, srcX, srcY, imageData, destX, destY, 32, 32);
-                if (debug) // blocks are green
-                    markRect(oldData, srcX, srcY, imageData, destX, destY, 32, 32, 0x00, 128, 0x00);
-
-                //log("Got block, id: " + blockid +  "(" + srcX +"," + srcY + ") at " + destX + "," + destY);
-
-                break;
-
-            case 0x30: // Color run
-                len = (r & 0xf) << 16 | g << 8 | b;
-                //log("Got color run, len: " + len);
-
-                b = data[src++];
-                g = data[src++];
-                r = data[src++];
-                alpha = data[src++];
-
-                start = dest;
-
-                for (i = 0; i < len; i++) {
-                    imageData.data[dest++] = r;
-                    imageData.data[dest++] = g;
-                    imageData.data[dest++] = b;
-                    imageData.data[dest++] = alpha;
-                }
-
-                if (debug) // Color runs are blue
-                    markRun(imageData.data, start, len, 0x00, 0x00, 128);
-
-                break;
-
-            case 0x40: // Delta run
-                len = (r & 0xf) << 16 | g << 8 | b;
-                //log("Got delta run, len: " + len);
+    var surface = surfaces[id];
+    surface.node_data = node_data;
 
-                b = data[src++];
-                g = data[src++];
-                r = data[src++];
-                alpha = data[src++];
+    var div = surface.div;
 
-                start = dest;
+    /* We use a secondary div so that we can remove all previous children in one go */
+    var oldDiv2 = null;
 
-                for (i = 0; i < len; i++) {
-                    imageData.data[dest] = (imageData.data[dest] + r) & 0xff;
-                    dest++;
-                    imageData.data[dest] = (imageData.data[dest] + g) & 0xff;
-                    dest++;
-                    imageData.data[dest] = (imageData.data[dest] + b) & 0xff;
-                    dest++;
-                    imageData.data[dest] = (imageData.data[dest] + alpha) & 0xff;
-                    dest++;
-                }
-                if (debug) // Delta runs are violet
-                    markRun(imageData.data, start, len, 0xff, 0x00, 0xff);
-                break;
-
-            default:
-                alert("Unknown buffer commend " + cmd);
-            }
-        }
-    }
+    if (div.hasChildNodes())
+        oldDiv2 = div.lastChild;
 
-    return imageData;
-}
+    var div2 = document.createElement('div');
 
-function cmdWindowUpdate(id, texture_id)
-{
-    var surface = surfaces[id];
-    var texture_url = textures[texture];
+    var end = handleNode(div2, node_data, 0);
+    if (end != node_data.length)
+        alert ("Did not consume entire array (len " + node_data.length + " end " + end + ")");
 
-    surface.image.src = texture_url;
+    div.appendChild(div2);
+    if (oldDiv2)
+        div.removeChild(oldDiv2);
 }
 
 function cmdUploadTexture(id, data)
@@ -527,7 +381,7 @@ function cmdUngrabPointer()
 {
     sendInput ("u", []);
     if (grab.window)
-       doUngrab();
+        doUngrab();
 }
 
 var active = false;
@@ -539,108 +393,118 @@ function handleCommands(cmd)
     }
 
     while (cmd.pos < cmd.length) {
-       var id, x, y, w, h, q;
-       var command = cmd.get_char();
-       lastSerial = cmd.get_32();
-       switch (command) {
-       case 'D':
-           alert ("disconnected");
-           inputSocket = null;
-           break;
-
-       case 's': // create new surface
-           id = cmd.get_16();
-           x = cmd.get_16s();
-           y = cmd.get_16s();
-           w = cmd.get_16();
-           h = cmd.get_16();
-           var isTemp = cmd.get_bool();
-           cmdCreateSurface(id, x, y, w, h, isTemp);
-           break;
-
-       case 'S': // Show a surface
-           id = cmd.get_16();
-           cmdShowSurface(id);
-           break;
-
-       case 'H': // Hide a surface
-           id = cmd.get_16();
-           cmdHideSurface(id);
-           break;
-
-       case 'p': // Set transient parent
-           id = cmd.get_16();
-           var parentId = cmd.get_16();
-           cmdSetTransientFor(id, parentId);
-           break;
-
-       case 'd': // Delete surface
-           id = cmd.get_16();
-           cmdDeleteSurface(id);
-           break;
-
-       case 'm': // Move a surface
-           id = cmd.get_16();
-           var ops = cmd.get_flags();
-           var has_pos = ops & 1;
-           if (has_pos) {
-               x = cmd.get_16s();
-               y = cmd.get_16s();
-           }
-           var has_size = ops & 2;
-           if (has_size) {
-               w = cmd.get_16();
-               h = cmd.get_16();
-           }
-           cmdMoveResizeSurface(id, has_pos, x, y, has_size, w, h);
-           break;
-
-       case 'r': // Raise a surface
-           id = cmd.get_16();
-           cmdRaiseSurface(id);
-           break;
-
-       case 'R': // Lower a surface
-           id = cmd.get_16();
-           cmdLowerSurface(id);
-           break;
-
-       case 'b': // Update window
-           id = cmd.get_16();
-           texture = cmd.get_32();
+        var id, x, y, w, h, q;
+        var command = cmd.get_char();
+        lastSerial = cmd.get_32();
+        switch (command) {
+        case 'D':
+            alert ("disconnected");
+            inputSocket = null;
+            break;
+
+        case 's': // create new surface
+            id = cmd.get_16();
+            x = cmd.get_16s();
+            y = cmd.get_16s();
+            w = cmd.get_16();
+            h = cmd.get_16();
+            var isTemp = cmd.get_bool();
+            cmdCreateSurface(id, x, y, w, h, isTemp);
+            break;
+
+        case 'S': // Show a surface
+            id = cmd.get_16();
+            cmdShowSurface(id);
+            break;
+
+        case 'H': // Hide a surface
+            id = cmd.get_16();
+            cmdHideSurface(id);
+            break;
+
+        case 'p': // Set transient parent
+            id = cmd.get_16();
+            var parentId = cmd.get_16();
+            cmdSetTransientFor(id, parentId);
+            break;
+
+        case 'd': // Delete surface
+            id = cmd.get_16();
+            cmdDeleteSurface(id);
+            break;
+
+        case 'm': // Move a surface
+            id = cmd.get_16();
+            var ops = cmd.get_flags();
+            var has_pos = ops & 1;
+            if (has_pos) {
+                x = cmd.get_16s();
+                y = cmd.get_16s();
+            }
+            var has_size = ops & 2;
+            if (has_size) {
+                w = cmd.get_16();
+                h = cmd.get_16();
+            }
+            cmdMoveResizeSurface(id, has_pos, x, y, has_size, w, h);
+            break;
+
+        case 'r': // Raise a surface
+            id = cmd.get_16();
+            cmdRaiseSurface(id);
+            break;
+
+        case 'R': // Lower a surface
+            id = cmd.get_16();
+            cmdLowerSurface(id);
+            break;
+
+        case 'b': // Update window
+            id = cmd.get_16();
+            var texture = cmd.get_32();
             cmdWindowUpdate(id, texture);
             break;
 
-       case 't': // Upload texture
-           id = cmd.get_32();
+        case 't': // Upload texture
+            id = cmd.get_32();
             var data = cmd.get_data();
             cmdUploadTexture(id, data);
             break;
 
-       case 'T': // Upload texture
-           id = cmd.get_32();
+        case 'T': // Release texture
+            id = cmd.get_32();
             cmdReleaseTexture(id);
             break;
 
-       case 'g': // Grab
-           id = cmd.get_16();
-           var ownerEvents = cmd.get_bool ();
+        case 'n': // Set nodes
+            id = cmd.get_16();
+            var len = cmd.get_32();
+            var node_data = new Uint32Array(len);
+            for (var i = 0; i < len; i++)
+                node_data[i] = cmd.get_32();
+
+            cmdWindowSetNodes(id, node_data);
+            break;
+
+        case 'g': // Grab
+            id = cmd.get_16();
+            var ownerEvents = cmd.get_bool ();
 
-           cmdGrabPointer(id, ownerEvents);
-           break;
+            cmdGrabPointer(id, ownerEvents);
+            break;
 
-       case 'u': // Ungrab
-           cmdUngrabPointer();
-           break;
+        case 'u': // Ungrab
+            cmdUngrabPointer();
+            break;
 
         case 'k': // show keyboard
             showKeyboard = cmd.get_16() != 0;
             showKeyboardChanged = true;
             break;
 
-       default:
-           alert("Unknown op " + command);
-       }
+        default:
+            alert("Unknown op " + command);
+        }
     }
     return true;
 }
@@ -648,11 +512,11 @@ function handleCommands(cmd)
 function handleOutstanding()
 {
     while (outstandingCommands.length > 0) {
-       var cmd = outstandingCommands.shift();
-       if (!handleCommands(cmd)) {
-           outstandingCommands.unshift(cmd);
-           return;
-       }
+        var cmd = outstandingCommands.shift();
+        if (!handleCommands(cmd)) {
+            outstandingCommands.unshift(cmd);
+            return;
+        }
     }
 }
 
@@ -674,24 +538,24 @@ BinCommands.prototype.get_flags = function() {
 }
 BinCommands.prototype.get_16 = function() {
     var v =
-       this.u8[this.pos] +
-       (this.u8[this.pos+1] << 8);
+        this.u8[this.pos] +
+        (this.u8[this.pos+1] << 8);
     this.pos = this.pos + 2;
     return v;
 };
 BinCommands.prototype.get_16s = function() {
     var v = this.get_16 ();
     if (v > 32767)
-       return v - 65536;
+        return v - 65536;
     else
-       return v;
+        return v;
 };
 BinCommands.prototype.get_32 = function() {
     var v =
-       this.u8[this.pos] +
-       (this.u8[this.pos+1] << 8) +
-       (this.u8[this.pos+2] << 16) +
-       (this.u8[this.pos+3] << 24);
+        this.u8[this.pos] +
+        (this.u8[this.pos+1] << 8) +
+        (this.u8[this.pos+2] << 16) +
+        (this.u8[this.pos+3] << 24);
     this.pos = this.pos + 4;
     return v;
 };
@@ -707,14 +571,14 @@ function handleMessage(message)
     var cmd = new BinCommands(message);
     outstandingCommands.push(cmd);
     if (outstandingCommands.length == 1) {
-       handleOutstanding();
+        handleOutstanding();
     }
 }
 
 function getSurfaceId(ev) {
     var surface = ev.target.surface;
     if (surface != undefined)
-       return surface.id;
+        return surface.id;
     return 0;
 }
 
@@ -741,9 +605,9 @@ function getPositionsFromAbsCoord(absX, absY, relativeId) {
     res.winX = absX;
     res.winY = absY;
     if (relativeId != 0) {
-       var surface = surfaces[relativeId];
-       res.winX = res.winX - surface.x;
-       res.winY = res.winY - surface.y;
+        var surface = surfaces[relativeId];
+        res.winX = res.winX - surface.x;
+        res.winY = res.winY - surface.y;
     }
 
     return res;
@@ -763,10 +627,10 @@ function getPositionsFromEvent(ev, relativeId) {
 
 function getEffectiveEventTarget (id) {
     if (grab.window != null) {
-       if (!grab.ownerEvents)
-           return grab.window;
-       if (id == 0)
-           return grab.window;
+        if (!grab.ownerEvents)
+            return grab.window;
+        if (id == 0)
+            return grab.window;
     }
     return id;
 }
@@ -784,11 +648,11 @@ function updateKeyboardStatus() {
 function updateForEvent(ev) {
     lastState &= ~(GDK_SHIFT_MASK|GDK_CONTROL_MASK|GDK_MOD1_MASK);
     if (ev.shiftKey)
-       lastState |= GDK_SHIFT_MASK;
+        lastState |= GDK_SHIFT_MASK;
     if (ev.ctrlKey)
-       lastState |= GDK_CONTROL_MASK;
+        lastState |= GDK_CONTROL_MASK;
     if (ev.altKey)
-       lastState |= GDK_MOD1_MASK;
+        lastState |= GDK_MOD1_MASK;
 
     lastTimeStamp = ev.timeStamp;
 }
@@ -810,7 +674,7 @@ function onMouseOver (ev) {
     var pos = getPositionsFromEvent(ev, id);
     windowWithMouse = id;
     if (windowWithMouse != 0) {
-       sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, 
GDK_CROSSING_NORMAL]);
+        sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, 
GDK_CROSSING_NORMAL]);
     }
 }
 
@@ -822,7 +686,7 @@ function onMouseOut (ev) {
     var pos = getPositionsFromEvent(ev, id);
 
     if (id != 0) {
-       sendInput ("l", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, 
GDK_CROSSING_NORMAL]);
+        sendInput ("l", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, 
GDK_CROSSING_NORMAL]);
     }
     realWindowWithMouse = 0;
     windowWithMouse = 0;
@@ -832,13 +696,13 @@ function doGrab(id, ownerEvents, implicit) {
     var pos;
 
     if (windowWithMouse != id) {
-       if (windowWithMouse != 0) {
-           pos = getPositionsFromAbsCoord(lastX, lastY, windowWithMouse);
-           sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, 
lastState, GDK_CROSSING_GRAB]);
-       }
-       pos = getPositionsFromAbsCoord(lastX, lastY, id);
-       sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, 
GDK_CROSSING_GRAB]);
-       windowWithMouse = id;
+        if (windowWithMouse != 0) {
+            pos = getPositionsFromAbsCoord(lastX, lastY, windowWithMouse);
+            sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, 
lastState, GDK_CROSSING_GRAB]);
+        }
+        pos = getPositionsFromAbsCoord(lastX, lastY, id);
+        sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, 
GDK_CROSSING_GRAB]);
+        windowWithMouse = id;
     }
 
     grab.window = id;
@@ -849,15 +713,15 @@ function doGrab(id, ownerEvents, implicit) {
 function doUngrab() {
     var pos;
     if (realWindowWithMouse != windowWithMouse) {
-       if (windowWithMouse != 0) {
-           pos = getPositionsFromAbsCoord(lastX, lastY, windowWithMouse);
-           sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, 
lastState, GDK_CROSSING_UNGRAB]);
-       }
-       if (realWindowWithMouse != 0) {
-           pos = getPositionsFromAbsCoord(lastX, lastY, realWindowWithMouse);
-           sendInput ("e", [realWindowWithMouse, realWindowWithMouse, pos.rootX, pos.rootY, pos.winX, 
pos.winY, lastState, GDK_CROSSING_UNGRAB]);
-       }
-       windowWithMouse = realWindowWithMouse;
+        if (windowWithMouse != 0) {
+            pos = getPositionsFromAbsCoord(lastX, lastY, windowWithMouse);
+            sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, 
lastState, GDK_CROSSING_UNGRAB]);
+        }
+        if (realWindowWithMouse != 0) {
+            pos = getPositionsFromAbsCoord(lastX, lastY, realWindowWithMouse);
+            sendInput ("e", [realWindowWithMouse, realWindowWithMouse, pos.rootX, pos.rootY, pos.winX, 
pos.winY, lastState, GDK_CROSSING_UNGRAB]);
+        }
+        windowWithMouse = realWindowWithMouse;
     }
     grab.window = null;
 }
@@ -871,7 +735,7 @@ function onMouseDown (ev) {
 
     var pos = getPositionsFromEvent(ev, id);
     if (grab.window == null)
-       doGrab (id, false, true);
+        doGrab (id, false, true);
     sendInput ("b", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, button]);
     return false;
 }
@@ -887,7 +751,7 @@ function onMouseUp (ev) {
     sendInput ("B", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, button]);
 
     if (grab.window != null && grab.implicit)
-       doUngrab();
+        doUngrab();
 
     return false;
 }
@@ -2200,7 +2064,7 @@ var specialKeyTable = {
 
 function getEventKeySym(ev) {
     if (typeof ev.which !== "undefined" && ev.which > 0)
-       return ev.which;
+        return ev.which;
     return ev.keyCode;
 }
 
@@ -2211,19 +2075,19 @@ function getEventKeySym(ev) {
 // translated keysym.
 function getKeysymSpecial(ev) {
     if (ev.keyCode in specialKeyTable) {
-       var r = specialKeyTable[ev.keyCode];
-       var flags = 0;
-       if (typeof r != 'number') {
-           flags = r[1];
-           r = r[0];
-       }
-       if (ev.type === 'keydown' || flags & ON_KEYDOWN)
-           return r;
+        var r = specialKeyTable[ev.keyCode];
+        var flags = 0;
+        if (typeof r != 'number') {
+            flags = r[1];
+            r = r[0];
+        }
+        if (ev.type === 'keydown' || flags & ON_KEYDOWN)
+            return r;
     }
     // If we don't hold alt or ctrl, then we should be safe to pass
     // on to keypressed and look at the translated data
     if (!ev.ctrlKey && !ev.altKey)
-       return null;
+        return null;
 
     var keysym = getEventKeySym(ev);
 
@@ -2233,9 +2097,9 @@ function getKeysymSpecial(ev) {
     case 187 : keysym = 61; break; // = (IE)
     case 188 : keysym = 44; break; // , (Mozilla, IE)
     case 109 : // - (Mozilla, Opera)
-       if (true /* TODO: check if browser is firefox or opera */)
-           keysym = 45;
-       break;
+        if (true /* TODO: check if browser is firefox or opera */)
+            keysym = 45;
+        break;
     case 189 : keysym = 45; break; // - (IE)
     case 190 : keysym = 46; break; // . (Mozilla, IE)
     case 191 : keysym = 47; break; // / (Mozilla, IE)
@@ -2248,49 +2112,49 @@ function getKeysymSpecial(ev) {
 
     /* Remap shifted and unshifted keys */
     if (!!ev.shiftKey) {
-       switch (keysym) {
-       case 48 : keysym = 41 ; break; // ) (shifted 0)
-       case 49 : keysym = 33 ; break; // ! (shifted 1)
-       case 50 : keysym = 64 ; break; // @ (shifted 2)
-       case 51 : keysym = 35 ; break; // # (shifted 3)
-       case 52 : keysym = 36 ; break; // $ (shifted 4)
-       case 53 : keysym = 37 ; break; // % (shifted 5)
-       case 54 : keysym = 94 ; break; // ^ (shifted 6)
-       case 55 : keysym = 38 ; break; // & (shifted 7)
-       case 56 : keysym = 42 ; break; // * (shifted 8)
-       case 57 : keysym = 40 ; break; // ( (shifted 9)
-       case 59 : keysym = 58 ; break; // : (shifted `)
-       case 61 : keysym = 43 ; break; // + (shifted ;)
-       case 44 : keysym = 60 ; break; // < (shifted ,)
-       case 45 : keysym = 95 ; break; // _ (shifted -)
-       case 46 : keysym = 62 ; break; // > (shifted .)
-       case 47 : keysym = 63 ; break; // ? (shifted /)
-       case 96 : keysym = 126; break; // ~ (shifted `)
-       case 91 : keysym = 123; break; // { (shifted [)
-       case 92 : keysym = 124; break; // | (shifted \)
-       case 93 : keysym = 125; break; // } (shifted ])
-       case 39 : keysym = 34 ; break; // " (shifted ')
-       }
+        switch (keysym) {
+        case 48 : keysym = 41 ; break; // ) (shifted 0)
+        case 49 : keysym = 33 ; break; // ! (shifted 1)
+        case 50 : keysym = 64 ; break; // @ (shifted 2)
+        case 51 : keysym = 35 ; break; // # (shifted 3)
+        case 52 : keysym = 36 ; break; // $ (shifted 4)
+        case 53 : keysym = 37 ; break; // % (shifted 5)
+        case 54 : keysym = 94 ; break; // ^ (shifted 6)
+        case 55 : keysym = 38 ; break; // & (shifted 7)
+        case 56 : keysym = 42 ; break; // * (shifted 8)
+        case 57 : keysym = 40 ; break; // ( (shifted 9)
+        case 59 : keysym = 58 ; break; // : (shifted `)
+        case 61 : keysym = 43 ; break; // + (shifted ;)
+        case 44 : keysym = 60 ; break; // < (shifted ,)
+        case 45 : keysym = 95 ; break; // _ (shifted -)
+        case 46 : keysym = 62 ; break; // > (shifted .)
+        case 47 : keysym = 63 ; break; // ? (shifted /)
+        case 96 : keysym = 126; break; // ~ (shifted `)
+        case 91 : keysym = 123; break; // { (shifted [)
+        case 92 : keysym = 124; break; // | (shifted \)
+        case 93 : keysym = 125; break; // } (shifted ])
+        case 39 : keysym = 34 ; break; // " (shifted ')
+        }
     } else if ((keysym >= 65) && (keysym <=90)) {
-       /* Remap unshifted A-Z */
-       keysym += 32;
+        /* Remap unshifted A-Z */
+        keysym += 32;
     } else if (ev.keyLocation === 3) {
-       // numpad keys
-       switch (keysym) {
-       case 96 : keysym = 48; break; // 0
-       case 97 : keysym = 49; break; // 1
-       case 98 : keysym = 50; break; // 2
-       case 99 : keysym = 51; break; // 3
-       case 100: keysym = 52; break; // 4
-       case 101: keysym = 53; break; // 5
-       case 102: keysym = 54; break; // 6
-       case 103: keysym = 55; break; // 7
-       case 104: keysym = 56; break; // 8
-       case 105: keysym = 57; break; // 9
-       case 109: keysym = 45; break; // -
-       case 110: keysym = 46; break; // .
-       case 111: keysym = 47; break; // /
-       }
+        // numpad keys
+        switch (keysym) {
+        case 96 : keysym = 48; break; // 0
+        case 97 : keysym = 49; break; // 1
+        case 98 : keysym = 50; break; // 2
+        case 99 : keysym = 51; break; // 3
+        case 100: keysym = 52; break; // 4
+        case 101: keysym = 53; break; // 5
+        case 102: keysym = 54; break; // 6
+        case 103: keysym = 55; break; // 7
+        case 104: keysym = 56; break; // 8
+        case 105: keysym = 57; break; // 9
+        case 109: keysym = 45; break; // -
+        case 110: keysym = 46; break; // .
+        case 111: keysym = 47; break; // /
+        }
     }
 
     return keysym;
@@ -2303,10 +2167,10 @@ function getKeysym(ev) {
     keysym = getEventKeySym(ev);
 
     if ((keysym > 255) && (keysym < 0xFF00)) {
-       // Map Unicode outside Latin 1 to gdk keysyms
-       keysym = unicodeTable[keysym];
-       if (typeof keysym === 'undefined')
-           keysym = 0;
+        // Map Unicode outside Latin 1 to gdk keysyms
+        keysym = unicodeTable[keysym];
+        if (typeof keysym === 'undefined')
+            keysym = 0;
     }
 
     return keysym;
@@ -2314,11 +2178,11 @@ function getKeysym(ev) {
 
 function copyKeyEvent(ev) {
     var members = ['type', 'keyCode', 'charCode', 'which',
-                  'altKey', 'ctrlKey', 'shiftKey',
-                  'keyLocation', 'keyIdentifier'], i, obj = {};
+                   'altKey', 'ctrlKey', 'shiftKey',
+                   'keyLocation', 'keyIdentifier'], i, obj = {};
     for (i = 0; i < members.length; i++) {
-       if (typeof ev[members[i]] !== "undefined")
-           obj[members[i]] = ev[members[i]];
+        if (typeof ev[members[i]] !== "undefined")
+            obj[members[i]] = ev[members[i]];
     }
     return obj;
 }
@@ -2330,13 +2194,13 @@ function pushKeyEvent(fev) {
 function getKeyEvent(keyCode, pop) {
     var i, fev = null;
     for (i = keyDownList.length-1; i >= 0; i--) {
-       if (keyDownList[i].keyCode === keyCode) {
-           if ((typeof pop !== "undefined") && pop)
-               fev = keyDownList.splice(i, 1)[0];
-           else
-               fev = keyDownList[i];
-           break;
-       }
+        if (keyDownList[i].keyCode === keyCode) {
+            if ((typeof pop !== "undefined") && pop)
+                fev = keyDownList.splice(i, 1)[0];
+            else
+                fev = keyDownList[i];
+            break;
+        }
     }
     return fev;
 }
@@ -2344,10 +2208,10 @@ function getKeyEvent(keyCode, pop) {
 function ignoreKeyEvent(ev) {
     // Blarg. Some keys have a different keyCode on keyDown vs keyUp
     if (ev.keyCode === 229) {
-       // French AZERTY keyboard dead key.
-       // Lame thing is that the respective keyUp is 219 so we can't
-       // properly ignore the keyUp event
-       return true;
+        // French AZERTY keyboard dead key.
+        // Lame thing is that the respective keyUp is 219 so we can't
+        // properly ignore the keyUp event
+        return true;
     }
     return false;
 }
@@ -2361,22 +2225,22 @@ function handleKeyDown(e) {
     // Save keysym decoding for use in keyUp
     fev.keysym = keysym;
     if (keysym) {
-       // If it is a key or key combination that might trigger
-       // browser behaviors or it has no corresponding keyPress
-       // event, then send it immediately
-       if (!ignoreKeyEvent(ev))
-           sendInput("k", [keysym, lastState]);
-       suppress = true;
+        // If it is a key or key combination that might trigger
+        // browser behaviors or it has no corresponding keyPress
+        // event, then send it immediately
+        if (!ignoreKeyEvent(ev))
+            sendInput("k", [keysym, lastState]);
+        suppress = true;
     }
 
     if (! ignoreKeyEvent(ev)) {
-       // Add it to the list of depressed keys
-       pushKeyEvent(fev);
+        // Add it to the list of depressed keys
+        pushKeyEvent(fev);
     }
 
     if (suppress) {
-       // Suppress bubbling/default actions
-       return cancelEvent(ev);
+        // Suppress bubbling/default actions
+        return cancelEvent(ev);
     }
 
     // Allow the event to bubble and become a keyPress event which
@@ -2388,13 +2252,13 @@ function handleKeyPress(e) {
     var ev = (e ? e : window.event), kdlen = keyDownList.length, keysym = null;
 
     if (((ev.which !== "undefined") && (ev.which === 0)) ||
-       getKeysymSpecial(ev)) {
-       // Firefox and Opera generate a keyPress event even if keyDown
-       // is suppressed. But the keys we want to suppress will have
-       // either:
-       // - the which attribute set to 0
-       // - getKeysymSpecial() will identify it
-       return cancelEvent(ev);
+        getKeysymSpecial(ev)) {
+        // Firefox and Opera generate a keyPress event even if keyDown
+        // is suppressed. But the keys we want to suppress will have
+        // either:
+        // - the which attribute set to 0
+        // - getKeysymSpecial() will identify it
+        return cancelEvent(ev);
     }
 
     keysym = getKeysym(ev);
@@ -2403,14 +2267,14 @@ function handleKeyPress(e) {
     // that the keyUp event will be able to have the character code
     // translation available.
     if (kdlen > 0) {
-       keyDownList[kdlen-1].keysym = keysym;
+        keyDownList[kdlen-1].keysym = keysym;
     } else {
-       //log("keyDownList empty when keyPress triggered");
+        //log("keyDownList empty when keyPress triggered");
     }
 
     // Send the translated keysym
     if (keysym > 0)
-       sendInput ("k", [keysym, lastState]);
+        sendInput ("k", [keysym, lastState]);
 
     // Stop keypress events just in case
     return cancelEvent(ev);
@@ -2422,14 +2286,14 @@ function handleKeyUp(e) {
     fev = getKeyEvent(ev.keyCode, true);
 
     if (fev)
-       keysym = fev.keysym;
+        keysym = fev.keysym;
     else {
-       //log("Key event (keyCode = " + ev.keyCode + ") not found on keyDownList");
-       keysym = 0;
+        //log("Key event (keyCode = " + ev.keyCode + ") not found on keyDownList");
+        keysym = 0;
     }
 
     if (keysym > 0)
-       sendInput ("K", [keysym, lastState]);
+        sendInput ("K", [keysym, lastState]);
     return cancelEvent(ev);
 }
 
@@ -2452,9 +2316,9 @@ function cancelEvent(ev)
 {
     ev = ev ? ev : window.event;
     if (ev.stopPropagation)
-       ev.stopPropagation();
+        ev.stopPropagation();
     if (ev.preventDefault)
-       ev.preventDefault();
+        ev.preventDefault();
     ev.cancelBubble = true;
     ev.cancel = true;
     ev.returnValue = false;
@@ -2472,7 +2336,7 @@ function onMouseWheel(ev)
     var offset = ev.detail ? ev.detail : -ev.wheelDelta;
     var dir = 0;
     if (offset > 0)
-       dir = 1;
+        dir = 1;
     sendInput ("s", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, dir]);
 
     return cancelEvent(ev);
@@ -2588,10 +2452,10 @@ function start()
     w = window.innerWidth;
     h = window.innerHeight;
     window.onresize = function(ev) {
-       var w, h;
-       w = window.innerWidth;
-       h = window.innerHeight;
-       sendInput ("d", [w, h]);
+        var w, h;
+        w = window.innerWidth;
+        h = window.innerHeight;
+        sendInput ("d", [w, h]);
     };
     sendInput ("d", [w, h]);
 }
@@ -2601,7 +2465,7 @@ function connect()
     var url = window.location.toString();
     var query_string = url.split("?");
     if (query_string.length > 1) {
-       var params = query_string[1].split("&");
+        var params = query_string[1].split("&");
 
         for (var i=0; i<params.length; i++) {
             var pair = params[i].split("=");
@@ -2616,15 +2480,15 @@ function connect()
     ws.binaryType = "arraybuffer";
 
     ws.onopen = function() {
-       inputSocket = ws;
+        inputSocket = ws;
     };
     ws.onclose = function() {
-       if (inputSocket != null)
-           alert ("disconnected");
-       inputSocket = null;
+        if (inputSocket != null)
+            alert ("disconnected");
+        inputSocket = null;
     };
     ws.onmessage = function(event) {
-       handleMessage(event.data);
+        handleMessage(event.data);
     };
 
     var iOS = /(iPad|iPhone|iPod)/g.test( navigator.userAgent );
diff --git a/gdk/broadway/broadwayd.c b/gdk/broadway/broadwayd.c
index 2706cc9..b95e8c8 100644
--- a/gdk/broadway/broadwayd.c
+++ b/gdk/broadway/broadwayd.c
@@ -289,9 +289,20 @@ client_handle_request (BroadwayClient *client,
                                     request->update.id,
                                     global_id);
       break;
+    case BROADWAY_REQUEST_SET_NODES:
+      {
+        gsize array_size = request->base.size - sizeof (BroadwayRequestSetNodes) + sizeof(guint32);
+        int n_data = array_size / sizeof(guint32);
+
+        broadway_server_window_set_nodes (server,
+                                          request->set_nodes.id,
+                                          n_data,
+                                          request->set_nodes.data);
+      }
+      break;
     case BROADWAY_REQUEST_UPLOAD_TEXTURE:
       if (client->fds == NULL)
-        g_warning ("FD passing mismatch");
+        g_warning ("FD passing mismatch for texture upload %d", request->release_texture.id);
       else
         {
           char *data, *p;
diff --git a/gdk/broadway/gdkbroadway-server.c b/gdk/broadway/gdkbroadway-server.c
index 4023833..f873a77 100644
--- a/gdk/broadway/gdkbroadway-server.c
+++ b/gdk/broadway/gdkbroadway-server.c
@@ -668,6 +668,7 @@ gdk_broadway_server_upload_texture (GdkBroadwayServer *server,
   return id;
 }
 
+
 void
 gdk_broadway_server_release_texture (GdkBroadwayServer *server,
                                      guint32            id)
@@ -680,6 +681,22 @@ gdk_broadway_server_release_texture (GdkBroadwayServer *server,
                                     BROADWAY_REQUEST_RELEASE_TEXTURE);
 }
 
+void
+gdk_broadway_server_window_set_nodes (GdkBroadwayServer *server,
+                                      guint32 id,
+                                      GArray *nodes)
+{
+  gsize size = sizeof(BroadwayRequestSetNodes) + sizeof(guint32) * (nodes->len - 1);
+  BroadwayRequestSetNodes *msg = g_alloca (size);
+  int i;
+
+  for (i = 0; i < nodes->len; i++)
+    msg->data[i] = g_array_index (nodes, guint32, i);
+
+  msg->id = id;
+  gdk_broadway_server_send_message_with_size (server, (BroadwayRequestBase *) msg, size, 
BROADWAY_REQUEST_SET_NODES, -1);
+}
+
 gboolean
 _gdk_broadway_server_window_move_resize (GdkBroadwayServer *server,
                                         gint id,
diff --git a/gdk/broadway/gdkbroadway-server.h b/gdk/broadway/gdkbroadway-server.h
index 91554e5..f679153 100644
--- a/gdk/broadway/gdkbroadway-server.h
+++ b/gdk/broadway/gdkbroadway-server.h
@@ -66,6 +66,9 @@ void                gdk_broadway_server_release_texture          (GdkBroadwaySer
 void               _gdk_broadway_server_window_update            (GdkBroadwayServer  *server,
                                                                  gint                id,
                                                                  guint32             texture);
+void               gdk_broadway_server_window_set_nodes          (GdkBroadwayServer *server,
+                                                                  guint32            id,
+                                                                  GArray             *nodes);
 gboolean           _gdk_broadway_server_window_move_resize       (GdkBroadwayServer  *server,
                                                                  gint                id,
                                                                  gboolean            with_move,
diff --git a/gdk/broadway/gdkdisplay-broadway.c b/gdk/broadway/gdkdisplay-broadway.c
index eb9e1f5..de276f6 100644
--- a/gdk/broadway/gdkdisplay-broadway.c
+++ b/gdk/broadway/gdkdisplay-broadway.c
@@ -318,6 +318,45 @@ gdk_broadway_display_get_setting (GdkDisplay *display,
   return FALSE;
 }
 
+typedef struct {
+  int id;
+  GdkDisplay *display;
+} BroadwayTextureData;
+
+static void
+broadway_texture_data_free (BroadwayTextureData *data)
+{
+  GdkBroadwayDisplay *broadway_display = GDK_BROADWAY_DISPLAY (data->display);
+
+  gdk_broadway_server_release_texture (broadway_display->server, data->id);
+  g_object_unref (data->display);
+  g_free (data);
+}
+
+guint32
+gdk_broadway_display_ensure_texture (GdkDisplay *display,
+                                     GdkTexture *texture)
+{
+  GdkBroadwayDisplay *broadway_display = GDK_BROADWAY_DISPLAY (display);
+  BroadwayTextureData *data;
+  guint32 id;
+
+  data = gdk_texture_get_render_data (texture, display);
+  if (data != NULL)
+    return data->id;
+
+  id = gdk_broadway_server_upload_texture (broadway_display->server, texture);
+
+  data = g_new0 (BroadwayTextureData, 1);
+  data->id = id;
+  data->display = g_object_ref (display);
+
+  if (!gdk_texture_set_render_data (texture, display, data, (GDestroyNotify)broadway_texture_data_free))
+    g_warning ("Failed to set render data, will leak texture");
+
+  return id;
+}
+
 static void
 gdk_broadway_display_class_init (GdkBroadwayDisplayClass * class)
 {
diff --git a/gdk/broadway/gdkprivate-broadway.h b/gdk/broadway/gdkprivate-broadway.h
index 30adbfe..92f2049 100644
--- a/gdk/broadway/gdkprivate-broadway.h
+++ b/gdk/broadway/gdkprivate-broadway.h
@@ -39,6 +39,13 @@
 
 void _gdk_broadway_resync_windows (void);
 
+guint32 gdk_broadway_display_ensure_texture (GdkDisplay *display,
+                                            GdkTexture *texture);
+
+void gdk_broadway_window_set_nodes (GdkWindow *window,
+                                   GArray *nodes,
+                                    GPtrArray *node_textures);
+
 void     _gdk_broadway_window_register_dnd (GdkWindow      *window);
 GdkDragContext * _gdk_broadway_window_drag_begin (GdkWindow *window,
                                                  GdkDevice *device,
diff --git a/gdk/broadway/gdkwindow-broadway.c b/gdk/broadway/gdkwindow-broadway.c
index 2f05c31..f1fe25d 100644
--- a/gdk/broadway/gdkwindow-broadway.c
+++ b/gdk/broadway/gdkwindow-broadway.c
@@ -71,8 +71,8 @@ gdk_broadway_window_init (GdkBroadwayWindow *broadway_window)
 }
 
 G_DEFINE_TYPE (GdkWindowImplBroadway,
-              gdk_window_impl_broadway,
-              GDK_TYPE_WINDOW_IMPL)
+               gdk_window_impl_broadway,
+               GDK_TYPE_WINDOW_IMPL)
 
 static GdkDisplay *
 find_broadway_display (void)
@@ -112,28 +112,32 @@ update_dirty_windows_and_sync (void)
       GdkWindowImplBroadway *impl = l->data;
 
       if (impl->dirty)
-       {
-         GdkTexture *texture;
-         guint32 texture_id;
+        {
+          GdkTexture *texture;
+          guint32 texture_id;
+
+          impl->dirty = FALSE;
+          updated_surface = TRUE;
 
-         impl->dirty = FALSE;
-         updated_surface = TRUE;
+          if (impl->texture_id)
+            gdk_broadway_server_release_texture (display->server, impl->texture_id);
+          impl->texture_id = 0;
 
-         if (impl->texture_id)
-           gdk_broadway_server_release_texture (display->server, impl->texture_id);
-         impl->texture_id = 0;
+          texture = gdk_texture_new_for_surface (impl->surface);
+          texture_id =  gdk_broadway_server_upload_texture (display->server, texture);
+          g_object_unref (texture);
 
-         texture = gdk_texture_new_for_surface (impl->surface);
-         texture_id =  gdk_broadway_server_upload_texture (display->server, texture);
-         g_object_unref (texture);
+          impl->texture_id = texture_id;
 
-         impl->texture_id = texture_id;
 
-         _gdk_broadway_server_window_update (display->server,
-                                             impl->id,
-                                             texture_id);
+          if (impl->node_data)
+            gdk_broadway_server_window_set_nodes (display->server, impl->id, impl->node_data);
 
-       }
+          _gdk_broadway_server_window_update (display->server,
+                                              impl->id,
+                                              texture_id);
+
+        }
     }
 
   /* We sync here to ensure all references to the impl->surface memory
@@ -223,10 +227,10 @@ connect_frame_clock (GdkWindow *window)
 
 void
 _gdk_broadway_display_create_window_impl (GdkDisplay    *display,
-                                         GdkWindow     *window,
-                                         GdkWindow     *real_parent,
-                                         GdkEventMask   event_mask,
-                                         GdkWindowAttr *attributes)
+                                          GdkWindow     *window,
+                                          GdkWindow     *real_parent,
+                                          GdkEventMask   event_mask,
+                                          GdkWindowAttr *attributes)
 {
   GdkWindowImplBroadway *impl;
   GdkBroadwayDisplay *broadway_display;
@@ -236,16 +240,16 @@ _gdk_broadway_display_create_window_impl (GdkDisplay    *display,
   impl = g_object_new (GDK_TYPE_WINDOW_IMPL_BROADWAY, NULL);
   window->impl = (GdkWindowImpl *)impl;
   impl->id = _gdk_broadway_server_new_window (broadway_display->server,
-                                             window->x,
-                                             window->y,
-                                             window->width,
-                                             window->height,
-                                             window->window_type == GDK_WINDOW_TEMP);
+                                              window->x,
+                                              window->y,
+                                              window->width,
+                                              window->height,
+                                              window->window_type == GDK_WINDOW_TEMP);
   g_hash_table_insert (broadway_display->id_ht, GINT_TO_POINTER(impl->id), window);
   impl->wrapper = window;
 
   g_assert (window->window_type == GDK_WINDOW_TOPLEVEL ||
-           window->window_type == GDK_WINDOW_TEMP);
+            window->window_type == GDK_WINDOW_TEMP);
   g_assert (window->parent == NULL);
 
   broadway_display->toplevels = g_list_prepend (broadway_display->toplevels, impl);
@@ -263,14 +267,14 @@ _gdk_broadway_window_resize_surface (GdkWindow *window)
       cairo_surface_destroy (impl->surface);
 
       impl->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-                                                 gdk_window_get_width (impl->wrapper),
-                                                 gdk_window_get_height (impl->wrapper));
+                                                  gdk_window_get_width (impl->wrapper),
+                                                  gdk_window_get_height (impl->wrapper));
     }
 
   if (impl->ref_surface)
     {
       cairo_surface_set_user_data (impl->ref_surface, &gdk_broadway_cairo_key,
-                                  NULL, NULL);
+                                   NULL, NULL);
       impl->ref_surface = NULL;
     }
 
@@ -306,12 +310,12 @@ gdk_window_broadway_ref_cairo_surface (GdkWindow *window)
   if (!impl->ref_surface)
     {
       impl->ref_surface =
-       cairo_surface_create_for_rectangle (impl->surface,
-                                           0, 0,
-                                           w, h);
+        cairo_surface_create_for_rectangle (impl->surface,
+                                            0, 0,
+                                            w, h);
       if (impl->ref_surface)
-       cairo_surface_set_user_data (impl->ref_surface, &gdk_broadway_cairo_key,
-                                    impl, ref_surface_destroyed);
+        cairo_surface_set_user_data (impl->ref_surface, &gdk_broadway_cairo_key,
+                                     impl, ref_surface_destroyed);
     }
   else
     cairo_surface_reference (impl->ref_surface);
@@ -321,8 +325,8 @@ gdk_window_broadway_ref_cairo_surface (GdkWindow *window)
 
 static void
 _gdk_broadway_window_destroy (GdkWindow *window,
-                             gboolean   recursing,
-                             gboolean   foreign_destroy)
+                              gboolean   recursing,
+                              gboolean   foreign_destroy)
 {
   GdkWindowImplBroadway *impl;
   GdkBroadwayDisplay *broadway_display;
@@ -331,6 +335,11 @@ _gdk_broadway_window_destroy (GdkWindow *window,
 
   impl = GDK_WINDOW_IMPL_BROADWAY (window->impl);
 
+  if (impl->node_data)
+    g_array_unref (impl->node_data);
+  if (impl->node_data_textures)
+    g_ptr_array_unref (impl->node_data_textures);
+
   _gdk_broadway_selection_window_destroyed (window);
   _gdk_broadway_window_grab_check_destroy (window);
 
@@ -338,7 +347,7 @@ _gdk_broadway_window_destroy (GdkWindow *window,
     {
       cairo_surface_finish (impl->ref_surface);
       cairo_surface_set_user_data (impl->ref_surface, &gdk_broadway_cairo_key,
-                                  NULL, NULL);
+                                   NULL, NULL);
     }
 
   if (impl->surface)
@@ -356,6 +365,35 @@ _gdk_broadway_window_destroy (GdkWindow *window,
 
 }
 
+void
+gdk_broadway_window_set_nodes (GdkWindow *window,
+                               GArray *nodes,
+                               GPtrArray *node_textures)
+{
+  GdkWindowImplBroadway *impl;
+  GdkBroadwayDisplay *broadway_display;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  impl = GDK_WINDOW_IMPL_BROADWAY (window->impl);
+
+  broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (window));
+
+  if (nodes)
+    g_array_ref (nodes);
+  if (impl->node_data)
+    g_array_unref (impl->node_data);
+  impl->node_data = nodes;
+
+  if (node_textures)
+    g_ptr_array_ref (node_textures);
+  if (impl->node_data_textures)
+    g_ptr_array_unref (impl->node_data_textures);
+  impl->node_data_textures = node_textures;
+
+  gdk_broadway_server_window_set_nodes (broadway_display->server, impl->id, impl->node_data);
+}
+
 /* This function is called when the XWindow is really gone.
  */
 static void
@@ -406,7 +444,7 @@ gdk_window_broadway_hide (GdkWindow *window)
   broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (window));
 
   _gdk_broadway_window_grab_check_unmap (window,
-                                        _gdk_broadway_server_get_next_serial (broadway_display->server));
+                                         _gdk_broadway_server_get_next_serial (broadway_display->server));
 
   if (_gdk_broadway_server_window_hide (broadway_display->server, impl->id))
     queue_flush (window);
@@ -422,11 +460,11 @@ gdk_window_broadway_withdraw (GdkWindow *window)
 
 static void
 gdk_window_broadway_move_resize (GdkWindow *window,
-                                gboolean   with_move,
-                                gint       x,
-                                gint       y,
-                                gint       width,
-                                gint       height)
+                                 gboolean   with_move,
+                                 gint       x,
+                                 gint       y,
+                                 gint       width,
+                                 gint       height)
 {
   GdkWindowImplBroadway *impl = GDK_WINDOW_IMPL_BROADWAY (window->impl);
   GdkBroadwayDisplay *broadway_display;
@@ -439,31 +477,31 @@ gdk_window_broadway_move_resize (GdkWindow *window,
   if (width > 0 || height > 0)
     {
       if (width < 1)
-       width = 1;
+        width = 1;
 
       if (height < 1)
-       height = 1;
+        height = 1;
 
       if (width != window->width ||
-         height != window->height)
-       {
-         size_changed = TRUE;
-
-         /* Resize clears the content */
-         impl->dirty = TRUE;
-         impl->last_synced = FALSE;
-
-         window->width = width;
-         window->height = height;
-         _gdk_broadway_window_resize_surface (window);
-       }
+          height != window->height)
+        {
+          size_changed = TRUE;
+
+          /* Resize clears the content */
+          impl->dirty = TRUE;
+          impl->last_synced = FALSE;
+
+          window->width = width;
+          window->height = height;
+          _gdk_broadway_window_resize_surface (window);
+        }
     }
 
   _gdk_broadway_server_window_move_resize (broadway_display->server,
-                                          impl->id,
-                                          with_move,
-                                          x, y,
-                                          window->width, window->height);
+                                           impl->id,
+                                           with_move,
+                                           x, y,
+                                           window->width, window->height);
   queue_flush (window);
   if (size_changed)
     window->resize_count++;
@@ -476,8 +514,8 @@ gdk_window_broadway_raise (GdkWindow *window)
 
 static void
 gdk_window_broadway_restack_toplevel (GdkWindow *window,
-                                     GdkWindow *sibling,
-                                     gboolean   above)
+                                      GdkWindow *sibling,
+                                      gboolean   above)
 {
 }
 
@@ -489,7 +527,7 @@ gdk_window_broadway_lower (GdkWindow *window)
 
 static void
 gdk_broadway_window_focus (GdkWindow *window,
-                          guint32    timestamp)
+                           guint32    timestamp)
 {
   GdkWindowImplBroadway *impl;
   GdkBroadwayDisplay *broadway_display;
@@ -503,12 +541,12 @@ gdk_broadway_window_focus (GdkWindow *window,
   impl = GDK_WINDOW_IMPL_BROADWAY (window->impl);
   broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (window));
   _gdk_broadway_server_window_focus (broadway_display->server,
-                                    impl->id);
+                                     impl->id);
 }
 
 static void
 gdk_broadway_window_set_type_hint (GdkWindow        *window,
-                                  GdkWindowTypeHint hint)
+                                   GdkWindowTypeHint hint)
 {
 }
 
@@ -520,32 +558,32 @@ gdk_broadway_window_get_type_hint (GdkWindow *window)
 
 static void
 gdk_broadway_window_set_modal_hint (GdkWindow *window,
-                                   gboolean   modal)
+                                    gboolean   modal)
 {
 }
 
 static void
 gdk_broadway_window_set_skip_taskbar_hint (GdkWindow *window,
-                                          gboolean   skips_taskbar)
+                                           gboolean   skips_taskbar)
 {
 }
 
 static void
 gdk_broadway_window_set_skip_pager_hint (GdkWindow *window,
-                                        gboolean   skips_pager)
+                                         gboolean   skips_pager)
 {
 }
 
 static void
 gdk_broadway_window_set_urgency_hint (GdkWindow *window,
-                                     gboolean   urgent)
+                                      gboolean   urgent)
 {
 }
 
 static void
 gdk_broadway_window_set_geometry_hints (GdkWindow         *window,
-                                       const GdkGeometry *geometry,
-                                       GdkWindowHints     geom_mask)
+                                        const GdkGeometry *geometry,
+                                        GdkWindowHints     geom_mask)
 {
   GdkWindowImplBroadway *impl;
 
@@ -557,25 +595,25 @@ gdk_broadway_window_set_geometry_hints (GdkWindow         *window,
 
 static void
 gdk_broadway_window_set_title (GdkWindow   *window,
-                              const gchar *title)
+                               const gchar *title)
 {
 }
 
 static void
 gdk_broadway_window_set_role (GdkWindow   *window,
-                             const gchar *role)
+                              const gchar *role)
 {
 }
 
 static void
 gdk_broadway_window_set_startup_id (GdkWindow   *window,
-                                   const gchar *startup_id)
+                                    const gchar *startup_id)
 {
 }
 
 static void
 gdk_broadway_window_set_transient_for (GdkWindow *window,
-                                      GdkWindow *parent)
+                                       GdkWindow *parent)
 {
   GdkBroadwayDisplay *display;
   GdkWindowImplBroadway *impl;
@@ -595,10 +633,10 @@ gdk_broadway_window_set_transient_for (GdkWindow *window,
 
 static void
 gdk_window_broadway_get_geometry (GdkWindow *window,
-                                 gint      *x,
-                                 gint      *y,
-                                 gint      *width,
-                                 gint      *height)
+                                  gint      *x,
+                                  gint      *y,
+                                  gint      *width,
+                                  gint      *height)
 {
   GdkWindowImplBroadway *impl;
 
@@ -621,10 +659,10 @@ gdk_window_broadway_get_geometry (GdkWindow *window,
 
 static void
 gdk_window_broadway_get_root_coords (GdkWindow *window,
-                                    gint       x,
-                                    gint       y,
-                                    gint      *root_x,
-                                    gint      *root_y)
+                                     gint       x,
+                                     gint       y,
+                                     gint      *root_x,
+                                     gint      *root_y)
 {
   GdkWindowImplBroadway *impl;
 
@@ -638,7 +676,7 @@ gdk_window_broadway_get_root_coords (GdkWindow *window,
 
 static void
 gdk_broadway_window_get_frame_extents (GdkWindow    *window,
-                                      GdkRectangle *rect)
+                                       GdkRectangle *rect)
 {
   g_return_if_fail (rect != NULL);
 
@@ -652,10 +690,10 @@ gdk_broadway_window_get_frame_extents (GdkWindow    *window,
 
 static gboolean
 gdk_window_broadway_get_device_state (GdkWindow       *window,
-                                     GdkDevice       *device,
-                                     gdouble         *x,
-                                     gdouble         *y,
-                                     GdkModifierType *mask)
+                                      GdkDevice       *device,
+                                      gdouble         *x,
+                                      gdouble         *y,
+                                      GdkModifierType *mask)
 {
   GdkWindow *child;
 
@@ -682,7 +720,7 @@ gdk_window_broadway_get_events (GdkWindow *window)
 
 static void
 gdk_window_broadway_set_events (GdkWindow    *window,
-                               GdkEventMask  event_mask)
+                                GdkEventMask  event_mask)
 {
   if (!GDK_WINDOW_DESTROYED (window))
     {
@@ -691,23 +729,23 @@ gdk_window_broadway_set_events (GdkWindow    *window,
 
 static void
 gdk_window_broadway_shape_combine_region (GdkWindow       *window,
-                                         const cairo_region_t *shape_region,
-                                         gint             offset_x,
-                                         gint             offset_y)
+                                          const cairo_region_t *shape_region,
+                                          gint             offset_x,
+                                          gint             offset_y)
 {
 }
 
 static void
 gdk_window_broadway_input_shape_combine_region (GdkWindow       *window,
-                                               const cairo_region_t *shape_region,
-                                               gint             offset_x,
-                                               gint             offset_y)
+                                                const cairo_region_t *shape_region,
+                                                gint             offset_x,
+                                                gint             offset_y)
 {
 }
 
 static void
 gdk_broadway_window_set_accept_focus (GdkWindow *window,
-                                     gboolean accept_focus)
+                                      gboolean accept_focus)
 {
   accept_focus = accept_focus != FALSE;
 
@@ -719,7 +757,7 @@ gdk_broadway_window_set_accept_focus (GdkWindow *window,
 
 static void
 gdk_broadway_window_set_focus_on_map (GdkWindow *window,
-                                     gboolean focus_on_map)
+                                      gboolean focus_on_map)
 {
   focus_on_map = focus_on_map != FALSE;
 
@@ -732,20 +770,20 @@ gdk_broadway_window_set_focus_on_map (GdkWindow *window,
 
 static void
 gdk_broadway_window_set_icon_list (GdkWindow *window,
-                                  GList     *surfaces)
+                                   GList     *surfaces)
 {
 }
 
 static void
 gdk_broadway_window_set_icon_name (GdkWindow   *window,
-                                  const gchar *name)
+                                   const gchar *name)
 {
   if (GDK_WINDOW_DESTROYED (window) ||
       !WINDOW_IS_TOPLEVEL (window))
     return;
 
   g_object_set_qdata (G_OBJECT (window), g_quark_from_static_string ("gdk-icon-name-set"),
-                     GUINT_TO_POINTER (name != NULL));
+                      GUINT_TO_POINTER (name != NULL));
 }
 
 static void
@@ -836,10 +874,10 @@ gdk_broadway_window_unmaximize (GdkWindow *window)
   gdk_synthesize_window_state (window, GDK_WINDOW_STATE_MAXIMIZED, 0);
 
   gdk_window_move_resize (window,
-                         impl->pre_maximize_x,
-                         impl->pre_maximize_y,
-                         impl->pre_maximize_width,
-                         impl->pre_maximize_height);
+                          impl->pre_maximize_x,
+                          impl->pre_maximize_y,
+                          impl->pre_maximize_width,
+                          impl->pre_maximize_height);
 }
 
 static void
@@ -862,7 +900,7 @@ gdk_broadway_window_unfullscreen (GdkWindow *window)
 
 static void
 gdk_broadway_window_set_keep_above (GdkWindow *window,
-                                   gboolean   setting)
+                                    gboolean   setting)
 {
   g_return_if_fail (GDK_IS_WINDOW (window));
 
@@ -895,13 +933,13 @@ gdk_broadway_window_get_group (GdkWindow *window)
 
 static void
 gdk_broadway_window_set_group (GdkWindow *window,
-                              GdkWindow *leader)
+                               GdkWindow *leader)
 {
 }
 
 static void
 gdk_broadway_window_set_decorations (GdkWindow      *window,
-                                    GdkWMDecoration decorations)
+                                     GdkWMDecoration decorations)
 {
   if (GDK_WINDOW_DESTROYED (window) ||
       !WINDOW_IS_TOPLEVEL (window))
@@ -911,7 +949,7 @@ gdk_broadway_window_set_decorations (GdkWindow      *window,
 
 static gboolean
 gdk_broadway_window_get_decorations (GdkWindow       *window,
-                                    GdkWMDecoration *decorations)
+                                     GdkWMDecoration *decorations)
 {
   gboolean result = FALSE;
 
@@ -924,7 +962,7 @@ gdk_broadway_window_get_decorations (GdkWindow       *window,
 
 static void
 gdk_broadway_window_set_functions (GdkWindow    *window,
-                                  GdkWMFunction functions)
+                                   GdkWMFunction functions)
 {
   g_return_if_fail (GDK_IS_WINDOW (window));
 
@@ -966,7 +1004,7 @@ struct _MoveResizeData
 
 static MoveResizeData *
 get_move_resize_data (GdkDisplay *display,
-                     gboolean    create)
+                      gboolean    create)
 {
   GdkBroadwayDisplay *broadway_display;
   MoveResizeData *mv_resize;
@@ -988,8 +1026,8 @@ get_move_resize_data (GdkDisplay *display,
 
 static void
 update_pos (MoveResizeData *mv_resize,
-           gint            new_root_x,
-           gint            new_root_y)
+            gint            new_root_x,
+            gint            new_root_y)
 {
   gint dx, dy;
 
@@ -1007,44 +1045,44 @@ update_pos (MoveResizeData *mv_resize,
       h = mv_resize->moveresize_orig_height;
 
       switch (mv_resize->resize_edge)
-       {
-       case GDK_WINDOW_EDGE_NORTH_WEST:
-         x += dx;
-         y += dy;
-         w -= dx;
-         h -= dy;
-         break;
-       case GDK_WINDOW_EDGE_NORTH:
-         y += dy;
-         h -= dy;
-         break;
-       case GDK_WINDOW_EDGE_NORTH_EAST:
-         y += dy;
-         h -= dy;
-         w += dx;
-         break;
-       case GDK_WINDOW_EDGE_SOUTH_WEST:
-         h += dy;
-         x += dx;
-         w -= dx;
-         break;
-       case GDK_WINDOW_EDGE_SOUTH_EAST:
-         w += dx;
-         h += dy;
-         break;
-       case GDK_WINDOW_EDGE_SOUTH:
-         h += dy;
-         break;
-       case GDK_WINDOW_EDGE_EAST:
-         w += dx;
-         break;
-       case GDK_WINDOW_EDGE_WEST:
-         x += dx;
-         w -= dx;
-         break;
+        {
+        case GDK_WINDOW_EDGE_NORTH_WEST:
+          x += dx;
+          y += dy;
+          w -= dx;
+          h -= dy;
+          break;
+        case GDK_WINDOW_EDGE_NORTH:
+          y += dy;
+          h -= dy;
+          break;
+        case GDK_WINDOW_EDGE_NORTH_EAST:
+          y += dy;
+          h -= dy;
+          w += dx;
+          break;
+        case GDK_WINDOW_EDGE_SOUTH_WEST:
+          h += dy;
+          x += dx;
+          w -= dx;
+          break;
+        case GDK_WINDOW_EDGE_SOUTH_EAST:
+          w += dx;
+          h += dy;
+          break;
+        case GDK_WINDOW_EDGE_SOUTH:
+          h += dy;
+          break;
+        case GDK_WINDOW_EDGE_EAST:
+          w += dx;
+          break;
+        case GDK_WINDOW_EDGE_WEST:
+          x += dx;
+          w -= dx;
+          break;
         default:
           break;
-       }
+        }
 
       x = MAX (x, 0);
       y = MAX (y, 0);
@@ -1052,11 +1090,11 @@ update_pos (MoveResizeData *mv_resize,
       h = MAX (h, 1);
 
       if (mv_resize->moveresize_geom_mask)
-       {
-         gdk_window_constrain_size (&mv_resize->moveresize_geometry,
-                                    mv_resize->moveresize_geom_mask,
-                                    w, h, &w, &h);
-       }
+        {
+          gdk_window_constrain_size (&mv_resize->moveresize_geometry,
+                                     mv_resize->moveresize_geom_mask,
+                                     w, h, &w, &h);
+        }
 
       gdk_window_move_resize (mv_resize->moveresize_window, x, y, w, h);
     }
@@ -1083,8 +1121,8 @@ finish_drag (MoveResizeData *mv_resize)
 
 static gboolean
 moveresize_lookahead (GdkDisplay *display,
-                     MoveResizeData *mv_resize,
-                     BroadwayInputMsg *event)
+                      MoveResizeData *mv_resize,
+                      BroadwayInputMsg *event)
 {
   GdkBroadwayDisplay *broadway_display;
 
@@ -1095,7 +1133,7 @@ moveresize_lookahead (GdkDisplay *display,
 
 gboolean
 _gdk_broadway_moveresize_handle_event (GdkDisplay *display,
-                                      BroadwayInputMsg *event)
+                                       BroadwayInputMsg *event)
 {
   guint button_mask = 0;
   MoveResizeData *mv_resize = get_move_resize_data (display, FALSE);
@@ -1137,21 +1175,21 @@ _gdk_broadway_moveresize_handle_event (GdkDisplay *display,
 
     case BROADWAY_EVENT_POINTER_MOVE:
       if (mv_resize->moveresize_window->resize_count > 0)
-       {
-         if (mv_resize->moveresize_pending_event)
-           *mv_resize->moveresize_pending_event = *event;
-         else
-           mv_resize->moveresize_pending_event =
-             g_memdup (event, sizeof (BroadwayInputMsg));
-
-         break;
-       }
+        {
+          if (mv_resize->moveresize_pending_event)
+            *mv_resize->moveresize_pending_event = *event;
+          else
+            mv_resize->moveresize_pending_event =
+              g_memdup (event, sizeof (BroadwayInputMsg));
+
+          break;
+        }
       if (!moveresize_lookahead (display, mv_resize, event))
-       break;
+        break;
 
       update_pos (mv_resize,
-                 event->pointer.root_x,
-                 event->pointer.root_y);
+                  event->pointer.root_x,
+                  event->pointer.root_y);
 
       /* This should never be triggered in normal cases, but in the
        * case where the drag started without an implicit grab being
@@ -1160,16 +1198,16 @@ _gdk_broadway_moveresize_handle_event (GdkDisplay *display,
        * get a permanently stuck grab.
        */
       if ((event->pointer.state & button_mask) == 0)
-       finish_drag (mv_resize);
+        finish_drag (mv_resize);
       break;
 
     case BROADWAY_EVENT_BUTTON_RELEASE:
       update_pos (mv_resize,
-                 event->pointer.root_x,
-                 event->pointer.root_y);
+                  event->pointer.root_x,
+                  event->pointer.root_y);
 
       if (event->button.button == mv_resize->moveresize_button)
-       finish_drag (mv_resize);
+        finish_drag (mv_resize);
       break;
     default:
       break;
@@ -1179,7 +1217,7 @@ _gdk_broadway_moveresize_handle_event (GdkDisplay *display,
 
 gboolean
 _gdk_broadway_moveresize_configure_done (GdkDisplay *display,
-                                        GdkWindow  *window)
+                                         GdkWindow  *window)
 {
   BroadwayInputMsg *tmp_event;
   MoveResizeData *mv_resize = get_move_resize_data (display, FALSE);
@@ -1200,7 +1238,7 @@ _gdk_broadway_moveresize_configure_done (GdkDisplay *display,
 
 static void
 create_moveresize_window (MoveResizeData *mv_resize,
-                         guint32         timestamp)
+                          guint32         timestamp)
 {
   GdkGrabStatus status;
   GdkSeat *seat;
@@ -1217,13 +1255,13 @@ create_moveresize_window (MoveResizeData *mv_resize,
 
   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
   status = gdk_device_grab (pointer,
-                            mv_resize->moveresize_emulation_window,
-                            GDK_OWNERSHIP_APPLICATION,
-                            FALSE,
-                            GDK_BUTTON_RELEASE_MASK |
-                            GDK_POINTER_MOTION_MASK,
-                            NULL,
-                            timestamp);
+                             mv_resize->moveresize_emulation_window,
+                             GDK_OWNERSHIP_APPLICATION,
+                             FALSE,
+                             GDK_BUTTON_RELEASE_MASK |
+                             GDK_POINTER_MOTION_MASK,
+                             NULL,
+                             timestamp);
   G_GNUC_END_IGNORE_DEPRECATIONS;
 
   if (status != GDK_GRAB_SUCCESS)
@@ -1247,70 +1285,70 @@ calculate_unmoving_origin (MoveResizeData *mv_resize)
       mv_resize->moveresize_geometry.win_gravity == GDK_GRAVITY_STATIC)
     {
       gdk_window_get_origin (mv_resize->moveresize_window,
-                            &mv_resize->moveresize_orig_x,
-                            &mv_resize->moveresize_orig_y);
+                             &mv_resize->moveresize_orig_x,
+                             &mv_resize->moveresize_orig_y);
     }
   else
     {
       gdk_window_get_frame_extents (mv_resize->moveresize_window, &rect);
       gdk_window_get_geometry (mv_resize->moveresize_window,
-                              NULL, NULL, &width, &height);
+                               NULL, NULL, &width, &height);
 
       switch (mv_resize->moveresize_geometry.win_gravity)
-       {
-       case GDK_GRAVITY_NORTH_WEST:
-         mv_resize->moveresize_orig_x = rect.x;
-         mv_resize->moveresize_orig_y = rect.y;
-         break;
-       case GDK_GRAVITY_NORTH:
-         mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2;
-         mv_resize->moveresize_orig_y = rect.y;
-         break;
-       case GDK_GRAVITY_NORTH_EAST:
-         mv_resize->moveresize_orig_x = rect.x + rect.width - width;
-         mv_resize->moveresize_orig_y = rect.y;
-         break;
-       case GDK_GRAVITY_WEST:
-         mv_resize->moveresize_orig_x = rect.x;
-         mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2;
-         break;
-       case GDK_GRAVITY_CENTER:
-         mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2;
-         mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2;
-         break;
-       case GDK_GRAVITY_EAST:
-         mv_resize->moveresize_orig_x = rect.x + rect.width - width;
-         mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2;
-         break;
-       case GDK_GRAVITY_SOUTH_WEST:
-         mv_resize->moveresize_orig_x = rect.x;
-         mv_resize->moveresize_orig_y = rect.y + rect.height - height;
-         break;
-       case GDK_GRAVITY_SOUTH:
-         mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2;
-         mv_resize->moveresize_orig_y = rect.y + rect.height - height;
-         break;
-       case GDK_GRAVITY_SOUTH_EAST:
-         mv_resize->moveresize_orig_x = rect.x + rect.width - width;
-         mv_resize->moveresize_orig_y = rect.y + rect.height - height;
-         break;
+        {
+        case GDK_GRAVITY_NORTH_WEST:
+          mv_resize->moveresize_orig_x = rect.x;
+          mv_resize->moveresize_orig_y = rect.y;
+          break;
+        case GDK_GRAVITY_NORTH:
+          mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2;
+          mv_resize->moveresize_orig_y = rect.y;
+          break;
+        case GDK_GRAVITY_NORTH_EAST:
+          mv_resize->moveresize_orig_x = rect.x + rect.width - width;
+          mv_resize->moveresize_orig_y = rect.y;
+          break;
+        case GDK_GRAVITY_WEST:
+          mv_resize->moveresize_orig_x = rect.x;
+          mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2;
+          break;
+        case GDK_GRAVITY_CENTER:
+          mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2;
+          mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2;
+          break;
+        case GDK_GRAVITY_EAST:
+          mv_resize->moveresize_orig_x = rect.x + rect.width - width;
+          mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2;
+          break;
+        case GDK_GRAVITY_SOUTH_WEST:
+          mv_resize->moveresize_orig_x = rect.x;
+          mv_resize->moveresize_orig_y = rect.y + rect.height - height;
+          break;
+        case GDK_GRAVITY_SOUTH:
+          mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2;
+          mv_resize->moveresize_orig_y = rect.y + rect.height - height;
+          break;
+        case GDK_GRAVITY_SOUTH_EAST:
+          mv_resize->moveresize_orig_x = rect.x + rect.width - width;
+          mv_resize->moveresize_orig_y = rect.y + rect.height - height;
+          break;
         case GDK_GRAVITY_STATIC:
-       default:
-         mv_resize->moveresize_orig_x = rect.x;
-         mv_resize->moveresize_orig_y = rect.y;
-         break;
-       }
+        default:
+          mv_resize->moveresize_orig_x = rect.x;
+          mv_resize->moveresize_orig_y = rect.y;
+          break;
+        }
     }
 }
 
 static void
 gdk_broadway_window_begin_resize_drag (GdkWindow     *window,
-                                      GdkWindowEdge  edge,
+                                       GdkWindowEdge  edge,
                                        GdkDevice     *device,
-                                      gint           button,
-                                      gint           root_x,
-                                      gint           root_y,
-                                      guint32        timestamp)
+                                       gint           button,
+                                       gint           root_x,
+                                       gint           root_y,
+                                       guint32        timestamp)
 {
   MoveResizeData *mv_resize;
   GdkWindowImplBroadway *impl;
@@ -1347,10 +1385,10 @@ gdk_broadway_window_begin_resize_drag (GdkWindow     *window,
 static void
 gdk_broadway_window_begin_move_drag (GdkWindow *window,
                                      GdkDevice *device,
-                                    gint       button,
-                                    gint       root_x,
-                                    gint       root_y,
-                                    guint32    timestamp)
+                                     gint       button,
+                                     gint       root_x,
+                                     gint       root_y,
+                                     guint32    timestamp)
 {
   MoveResizeData *mv_resize;
   GdkWindowImplBroadway *impl;
@@ -1391,7 +1429,7 @@ gdk_broadway_window_beep (GdkWindow *window)
 
 static void
 gdk_broadway_window_set_opacity (GdkWindow *window,
-                                gdouble    opacity)
+                                 gdouble    opacity)
 {
   g_return_if_fail (GDK_IS_WINDOW (window));
 
diff --git a/gdk/broadway/gdkwindow-broadway.h b/gdk/broadway/gdkwindow-broadway.h
index 4a2a727..925efbf 100644
--- a/gdk/broadway/gdkwindow-broadway.h
+++ b/gdk/broadway/gdkwindow-broadway.h
@@ -72,6 +72,9 @@ struct _GdkWindowImplBroadway
 
   GdkGeometry geometry_hints;
   GdkWindowHints geometry_hints_mask;
+
+  GArray *node_data;
+  GPtrArray *node_data_textures;
 };
 
 struct _GdkWindowImplBroadwayClass
diff --git a/gsk/gskbroadwayrenderer.c b/gsk/gskbroadwayrenderer.c
index 9cb67a5..fcc8583 100644
--- a/gsk/gskbroadwayrenderer.c
+++ b/gsk/gskbroadwayrenderer.c
@@ -23,7 +23,7 @@ struct _GskBroadwayRendererClass
 G_DEFINE_TYPE (GskBroadwayRenderer, gsk_broadway_renderer, GSK_TYPE_RENDERER)
 
 static gboolean
-gsk_broadway_renderer_realize (GskRenderer  *renderer,
+gsk_broadway_renderer_realize (GskRenderer  *self,
                                GdkWindow    *window,
                                GError      **error)
 {
@@ -31,13 +31,36 @@ gsk_broadway_renderer_realize (GskRenderer  *renderer,
 }
 
 static void
-gsk_broadway_renderer_unrealize (GskRenderer *renderer)
+gsk_broadway_renderer_unrealize (GskRenderer *self)
 {
 
 }
 
+static GdkDrawingContext *
+gsk_broadway_renderer_begin_draw_frame (GskRenderer          *renderer,
+                                        const cairo_region_t *update_area)
+{
+  cairo_region_t *region;
+  GdkDrawingContext *result;
+  cairo_rectangle_int_t whole_window;
+  GdkWindow *window;
+
+  window = gsk_renderer_get_window (renderer);
+  whole_window = (cairo_rectangle_int_t) {
+    0, 0,
+    gdk_window_get_width (window),
+    gdk_window_get_height (window)
+  };
+  region = cairo_region_create_rectangle (&whole_window);
+  result = gdk_window_begin_draw_frame (window, NULL, region);
+  cairo_region_destroy (region);
+
+  return result;
+}
+
+
 static GdkTexture *
-gsk_broadway_renderer_render_texture (GskRenderer           *renderer,
+gsk_broadway_renderer_render_texture (GskRenderer           *self,
                                       GskRenderNode         *root,
                                       const graphene_rect_t *viewport)
 {
@@ -61,34 +84,80 @@ gsk_broadway_renderer_render_texture (GskRenderer           *renderer,
 }
 
 static void
-gsk_broadway_renderer_render (GskRenderer   *renderer,
-                              GskRenderNode *root)
+add_uint32 (GArray *nodes, guint32 v)
 {
-  GdkDrawingContext *context = gsk_renderer_get_drawing_context (renderer);
-  graphene_rect_t viewport;
-  cairo_t *cr;
-
-  cr = gdk_drawing_context_get_cairo_context (context);
-
-  g_return_if_fail (cr != NULL);
+  g_array_append_val (nodes, v);
+}
 
-  gsk_renderer_get_viewport (renderer, &viewport);
+static void
+gsk_broadway_renderer_add_node (GskRenderer *self,
+                                GArray *nodes,
+                                GPtrArray *node_textures,
+                                GskRenderNode *node)
+{
+  GdkDisplay *display = gsk_renderer_get_display (self);
 
-  if (GSK_RENDER_MODE_CHECK (GEOMETRY))
+  switch (gsk_render_node_get_node_type (node))
     {
-      cairo_save (cr);
-      cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
-      cairo_rectangle (cr,
-                       viewport.origin.x,
-                       viewport.origin.y,
-                       viewport.size.width,
-                       viewport.size.height);
-      cairo_set_source_rgba (cr, 0, 0, 0.85, 0.5);
-      cairo_stroke (cr);
-      cairo_restore (cr);
+    case GSK_NOT_A_RENDER_NODE:
+      g_assert_not_reached ();
+      return;
+    case GSK_CONTAINER_NODE:
+      {
+        guint i;
+
+        add_uint32 (nodes, BROADWAY_NODE_CONTAINER);
+        add_uint32 (nodes, gsk_container_node_get_n_children (node));
+
+        for (i = 0; i < gsk_container_node_get_n_children (node); i++)
+          gsk_broadway_renderer_add_node (self, nodes, node_textures,
+                                          gsk_container_node_get_child (node, i));
+      }
+      return;
+
+    default:
+      {
+        int x = floorf (node->bounds.origin.x);
+        int y = floorf (node->bounds.origin.y);
+        int width = ceil (node->bounds.origin.x + node->bounds.size.width) - x;
+        int height = ceil (node->bounds.origin.y + node->bounds.size.height) - y;
+        cairo_surface_t *surface;
+        GdkTexture *texture;
+        guint32 texture_id;
+        cairo_t *cr;
+
+        surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+        cr = cairo_create (surface);
+        cairo_translate (cr, -x, -y);
+        gsk_render_node_draw (node, cr);
+        cairo_destroy (cr);
+
+        texture = gdk_texture_new_for_surface (surface);
+        g_ptr_array_add (node_textures, texture); /* Transfers ownership to node_textures */
+        texture_id = gdk_broadway_display_ensure_texture (display, texture);
+        add_uint32 (nodes, BROADWAY_NODE_TEXTURE);
+        add_uint32 (nodes, x);
+        add_uint32 (nodes, y);
+        add_uint32 (nodes, width);
+        add_uint32 (nodes, height);
+        add_uint32 (nodes, texture_id);
+      }
+      return;
     }
+}
 
-  gsk_render_node_draw (root, cr);
+static void
+gsk_broadway_renderer_render (GskRenderer   *self,
+                              GskRenderNode *root)
+{
+  GdkWindow *window = gsk_renderer_get_window (self);
+  GArray *nodes = g_array_new (FALSE, FALSE, sizeof(guint32));
+  GPtrArray *node_textures = g_ptr_array_new_with_free_func (g_object_unref);
+
+  gsk_broadway_renderer_add_node (self, nodes, node_textures, root);
+  gdk_broadway_window_set_nodes (window, nodes, node_textures);
+  g_array_unref (nodes);
+  g_ptr_array_unref (node_textures);
 }
 
 static void
@@ -96,6 +165,7 @@ gsk_broadway_renderer_class_init (GskBroadwayRendererClass *klass)
 {
   GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass);
 
+  renderer_class->begin_draw_frame = gsk_broadway_renderer_begin_draw_frame;
   renderer_class->realize = gsk_broadway_renderer_realize;
   renderer_class->unrealize = gsk_broadway_renderer_unrealize;
   renderer_class->render = gsk_broadway_renderer_render;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]