      */
     protected function removeWindow (id :int) :void
     {
+        var idx :int = getWindowIndex(id);
+        if (idx != -1) {
+            addDirtyRegion(_windows[idx].bounds);
+            _windows.splice(idx, 1);
+        }
+    }
+
+    /**
+     * Updates the identified window.
+     */
+    protected function updateWindow (id :int, layer :int, bounds :Rectangle, fill :int) :void
+    {
+        var idx :int = getWindowIndex(id);
+        if (idx == -1) {
+            return;
+        }
+        var window :Window = _windows[idx];
+        if (window.layer != layer) {
+            // remove and reinsert
+            _windows.splice(idx, 1);
+            window.layer = layer;
+            addWindow(window);
+        }
+        addDirtyRegion(window.bounds);
+        if (!window.bounds.equals(bounds)) {
+            addDirtyRegion(bounds);
+            if (!window.bounds.size.equals(bounds.size)) {
+                window.resizeContents(bounds.size, fill);
+            }
+            window.bounds.copyFrom(bounds);
+        }
+    }
+
+    /**
+     * Sets part of a window's contents.
+     */
+    protected function setWindowContents (id :int, bounds :Rectangle, contents :Array) :void
+    {
+        var window :Window = getWindow(id);
+        if (window != null) {
+            addDirtyRegion(new Rectangle(window.bounds.x + bounds.x, window.bounds.y + bounds.y,
+                bounds.width, bounds.height));
+            window.setContents(bounds, contents);
+        }
+    }
+
+    /**
+     * Moves part of a window's contents.
+     */
+    protected function moveWindowContents (
+        id :int, source :Rectangle, dest :Point, fill :int) :void
+    {
+        var window :Window = getWindow(id);
+        if (window != null) {
+            addDirtyRegion(new Rectangle(window.bounds.x + source.x, window.bounds.y + source.y,
+                source.width, source.height));
+            addDirtyRegion(new Rectangle(window.bounds.x + dest.x, window.bounds.y + dest.y,
+                source.width, source.height));
+            window.moveContents(source, dest, fill);
+        }
+    }
+
+    /**
+     * Returns a reference to the window with the specified id, or null if not found.
+     */
+    protected function getWindow (id :int) :Window
+    {
+        var idx :int = getWindowIndex(id);
+        return (idx == -1) ? null : _windows[idx];
+    }
+
+    /**
+     * Returns the index of the window with the specified id, or -1 if not found.
+     */
+    protected function getWindowIndex (id :int) :int
+    {
         for (var ii :int = 0; ii < _windows.length; ii++) {
             if (_windows[ii].id == id) {
-                _windows.splice(ii, 1);
-                return;
-            }
-        }
-    }
-
-    /**
-     * Updates the portion of the display covered by the given bounds.
-     */
-    protected function updateDisplay (bounds :Rectangle) :void
-    {
+                return ii;
+            }
+        }
+        trace("Window not found.", id);
+        return -1;
+    }
+
+    /**
+     * Adds a region to the rectangle that will need updating.
+     */
+    protected function addDirtyRegion (region :Rectangle) :void
+    {
+        _dirty = _dirty.union(region);
+    }
+
+    /**
+     * Updates the portion of the display covered by the dirty region.
+     */
+    protected function updateDisplay () :void
+    {
+        // get the intersection of the screen bounds and the dirty region
+        var bounds :Rectangle = _dirty.intersection(new Rectangle(0, 0, _width, _height));
+        _dirty.setEmpty();
+        if (bounds.isEmpty()) {
+            return;
+        }
+
         // first, combine the contents of all intersecting windows
         var combined :Array = new Array(bounds.width * bounds.height);
         for (var ii :int = 0; ii < _windows.length; ii++) {

     */
    protected function addWindow (window :Window) :void
    {
        for (var ii :int = _windows.length - 1; ii >= 0; ii--) {
            if (window.layer >= _windows[ii].layer) {
                _windows.splice(ii + 1, 0, window);

            return;
        }
        _socket.writeShort(6);
        _socket.writeShort(event.type == KeyboardEvent.KEY_DOWN ? KEY_PRESSED : KEY_RELEASED);
        _socket.writeUnsignedInt(getQtKeyCode(event));
    }

