[Spice-commits] 2 commits - client/canvas.cpp client/canvas.h client/debug.h client/display_channel.cpp client/display_channel.h client/red_cairo_canvas.cpp client/red_cairo_canvas.h client/red_gdi_canvas.cpp client/red_gdi_canvas.h client/red_gl_canvas.cpp client/red_gl_canvas.h common/cairo_canvas.c common/cairo_canvas.h common/canvas_base.c common/canvas_base.h common/gdi_canvas.c common/gdi_canvas.h common/gl_canvas.c common/gl_canvas.h server/red_dispatcher.c server/red_worker.c server/red_worker.h server/vd_interface.h

Izik Eidus izik at kemper.freedesktop.org
Fri Apr 2 19:45:51 PDT 2010


 client/canvas.cpp           |    5 
 client/canvas.h             |  143 ++++++-
 client/debug.h              |    5 
 client/display_channel.cpp  |  434 ++++++++++++++++-----
 client/display_channel.h    |   39 +
 client/red_cairo_canvas.cpp |    5 
 client/red_cairo_canvas.h   |    2 
 client/red_gdi_canvas.cpp   |    6 
 client/red_gdi_canvas.h     |    2 
 client/red_gl_canvas.cpp    |    5 
 client/red_gl_canvas.h      |    2 
 common/cairo_canvas.c       |  402 ++++++++++++++++---
 common/cairo_canvas.h       |    1 
 common/canvas_base.c        |  530 +++++++++++++++++++------
 common/canvas_base.h        |   76 +++
 common/gdi_canvas.c         |  465 +++++++++++++---------
 common/gdi_canvas.h         |    1 
 common/gl_canvas.c          |    2 
 common/gl_canvas.h          |    1 
 server/red_dispatcher.c     |    1 
 server/red_worker.c         |  897 +++++++++++++++++++++++++++++++++++---------
 server/red_worker.h         |    1 
 server/vd_interface.h       |    5 
 23 files changed, 2347 insertions(+), 683 deletions(-)

New commits:
commit 4a208b3644ab3a5e3e950400692303bf608f0129
Author: Izik Eidus <ieidus at redhat.com>
Date:   Sat Apr 3 05:35:31 2010 +0300

    libspice: add off screens support
    
    Signed-off-by: Izik Eidus <ieidus at redhat.com>

diff --git a/client/canvas.cpp b/client/canvas.cpp
index ba446c9..b72aad5 100644
--- a/client/canvas.cpp
+++ b/client/canvas.cpp
@@ -22,11 +22,12 @@
 
 
 Canvas::Canvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-               GlzDecoderWindow &glz_decoder_window)
+               GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces)
     : _canvas (NULL)
     , _pixmap_cache (pixmap_cache)
     , _palette_cache (palette_cache)
     , _glz_decoder(glz_decoder_window, _glz_handler, _glz_debug)
+    , _csurfaces(csurfaces)
 {
 }
 
@@ -65,6 +66,8 @@ void Canvas::localalize_image(SPICE_ADDRESS* in_bitmap)
     localalize_ptr(in_bitmap);
     image = (SpiceImageDescriptor*)SPICE_GET_ADDRESS(*in_bitmap);
     switch (image->type) {
+    case SPICE_IMAGE_TYPE_SURFACE:
+        break;
     case SPICE_IMAGE_TYPE_BITMAP: {
         SpiceBitmapImage *bitmap = (SpiceBitmapImage *)image;
         localalize_ptr(&bitmap->bitmap.data);
diff --git a/client/canvas.h b/client/canvas.h
index 8e3a8f1..1e251ed 100644
--- a/client/canvas.h
+++ b/client/canvas.h
@@ -36,6 +36,102 @@ enum CanvasType {
     CANVAS_TYPE_GDI,
 };
 
+template <class T, int HASH_SIZE, class Base = EmptyBase>
+class CHash : public Base {
+public:
+    CHash()
+    {
+        memset(_hash, 0, sizeof(_hash));
+    }
+
+    ~CHash()
+    {
+    }
+
+    void add(uint32_t id, T* data)
+    {
+        Item** item = &_hash[key(id)];
+
+        while (*item) {
+            PANIC_ON((*item)->id == id);
+            item = &(*item)->next;
+        }
+        *item = new Item(id, data);
+    }
+
+    bool is_present(uint32_t id)
+    {
+        Item* item = _hash[key(id)];
+
+        for (;;) {
+            if (!item) {
+                return false;
+            }
+
+            if (item->id != id) {
+                item = item->next;
+                continue;
+            }
+
+            return true;
+        }
+    }
+
+    T* get(uint32_t id)
+    {
+        Item* item = _hash[key(id)];
+
+        for (;;) {
+            PANIC_ON(!item);
+
+            if (item->id != id) {
+                item = item->next;
+                continue;
+            }
+
+            return item->data;
+        }
+    }
+
+    void remove(uint32_t id)
+    {
+        Item** item = &_hash[key(id)];
+
+        while (*item) {
+            if ((*item)->id == id) {
+                Item *rm_item = *item;
+                *item = rm_item->next;
+                delete rm_item;
+                return;
+            }
+            item = &(*item)->next;
+        }
+        THROW("id %lu, not found", id);
+    }
+
+private:
+    inline uint32_t key(uint32_t id) {return id % HASH_SIZE;}
+
+private:
+    class Item {
+    public:
+        Item(uint32_t in_id, T* data)
+            : id (in_id)
+            , next (NULL)
+            , data (data) {}
+
+        ~Item()
+        {
+        }
+
+        uint64_t id;
+        Item* next;
+        T* data;
+    };
+
+    Item* _hash[HASH_SIZE];
+};
+
 class PixmapCacheTreat {
 public:
     static inline pixman_image_t *get(pixman_image_t *surf)
@@ -81,6 +177,45 @@ public:
     }
 };
 
+class SpiceImageSurfacesBase;
+
+typedef CHash<SpiceCanvas, 1024, SpiceImageSurfacesBase> CSurfaces;
+
+class SpiceImageSurfacesBase {
+public:
+    SpiceImageSurfaces base;
+
+    static void op_put(SpiceImageSurfaces *c, uint32_t surface_id, SpiceCanvas *surface)
+    {
+        CSurfaces* cache = reinterpret_cast<CSurfaces*>(c);
+        cache->add(surface_id, surface);
+    }
+
+    static SpiceCanvas* op_get(SpiceImageSurfaces *s, uint32_t surface_id)
+    {
+        CSurfaces* cache = reinterpret_cast<CSurfaces*>(s);
+        return cache->get(surface_id);
+    }
+
+    static void op_del(SpiceImageSurfaces *c, uint32_t surface_id)
+    {
+        CSurfaces* cache = reinterpret_cast<CSurfaces*>(c);
+        cache->remove(surface_id);
+    }
+
+    SpiceImageSurfacesBase()
+    {
+        static SpiceImageSurfacesOps cache_ops = {
+            op_get
+        };
+        base.ops = &cache_ops;
+    }
+};
+
+class Canvas;
+
+typedef CHash<Canvas, 1024, SpiceImageSurfacesBase> CCanvases;
+
 class CachedPalette {
 public:
     CachedPalette(SpicePalette* palette)
@@ -241,7 +376,7 @@ public:
 class Canvas {
 public:
     Canvas(PixmapCache& bits_cache, PaletteCache& palette_cache,
-           GlzDecoderWindow &glz_decoder_window);
+           GlzDecoderWindow &glz_decoder_window, CSurfaces& csurfaces);
     virtual ~Canvas();
 
     virtual void copy_pixels(const QRegion& region, RedDrawable* dc,
@@ -274,11 +409,14 @@ public:
 
     virtual CanvasType get_pixmap_type() { return CANVAS_TYPE_INVALID; }
 
+    virtual SpiceCanvas *get_internal_canvas() { return _canvas; }
+
 protected:
     virtual void touched_bbox(const SpiceRect *bbox) {};
 
     PixmapCache& pixmap_cache() { return _pixmap_cache;}
     PaletteCache& palette_cache() { return _palette_cache;}
+    CSurfaces& csurfaces() { return _csurfaces; }
 
     GlzDecoder& glz_decoder() {return _glz_decoder;}
 
@@ -293,6 +431,7 @@ private:
 
 protected:
     SpiceCanvas* _canvas;
+    CSurfaces _surfaces;
 
 private:
     PixmapCache& _pixmap_cache;
@@ -302,6 +441,8 @@ private:
     GlzDecoderCanvasDebug _glz_debug;
     GlzDecoder _glz_decoder;
 
+    CSurfaces& _csurfaces;
+
     unsigned long _base;
     unsigned long _max;
 };
diff --git a/client/display_channel.cpp b/client/display_channel.cpp
index 0d9d20c..d63726c 100644
--- a/client/display_channel.cpp
+++ b/client/display_channel.cpp
@@ -59,7 +59,7 @@ public:
         Application* app = (Application*)events_loop.get_owner();
         _channel.screen()->lock_size();
         _channel.screen()->resize(_width, _height);
-        _channel.create_canvas(app->get_canvas_types(), _width, _height, _depth);
+        _channel.create_canvas(0, app->get_canvas_types(), _width, _height, _depth);
     }
 
 private:
@@ -78,13 +78,56 @@ public:
 
     virtual void do_response(AbstractProcessLoop& events_loop)
     {
-        _channel.destroy_canvas();
+        _channel.destroy_canvas(0);
     }
 
 private:
     DisplayChannel& _channel;
 };
 
+class CreateSurfaceEvent: public SyncEvent {
+public:
+   CreateSurfaceEvent(DisplayChannel& channel, int surface_id, int width, int height, int depth)
+        : _channel (channel)
+        , _surface_id (surface_id)
+        , _width (width)
+        , _height (height)
+        , _depth (depth)
+    {
+    }
+
+    virtual void do_response(AbstractProcessLoop& events_loop)
+    {
+        Application* app = (Application*)events_loop.get_owner();
+        _channel.create_canvas(_surface_id, app->get_canvas_types(), _width, _height, _depth);
+    }
+
+private:
+    DisplayChannel& _channel;
+    int _surface_id;
+    int _width;
+    int _height;
+    int _depth;
+};
+
+class DestroySurfaceEvent: public SyncEvent {
+public:
+    DestroySurfaceEvent(DisplayChannel& channel, int surface_id)
+        : _channel (channel)
+        , _surface_id (surface_id)
+    {
+    }
+
+    virtual void do_response(AbstractProcessLoop& events_loop)
+    {
+        _channel.destroy_canvas(_surface_id);
+    }
+
+private:
+    DisplayChannel& _channel;
+    int _surface_id;
+};
+
 class UnlockScreenEvent: public Event {
 public:
     UnlockScreenEvent(RedScreen* screen)
@@ -633,6 +676,42 @@ void ResetTimer::response(AbstractProcessLoop& events_loop)
     _client.deactivate_interval_timer(this);
 }
 
+void DisplaySurfacesManger::add_surface(int surface_id, SpiceCanvas *surface)
+{
+     surfaces.add(surface_id, surface);
+}
+
+void DisplaySurfacesManger::del_surface(int surface_id)
+{
+    surfaces.remove(surface_id);
+}
+
+void DisplaySurfacesManger::add_canvas(int surface_id, Canvas *canvas)
+{
+    canvases.add(surface_id, canvas);
+}
+
+void DisplaySurfacesManger::del_canvas(int surface_id)
+{
+    canvases.remove(surface_id);
+}
+
+CSurfaces& DisplaySurfacesManger::get_surfaces()
+{ 
+    return surfaces;
+}
+
+bool DisplaySurfacesManger::is_present_canvas(int surface_id)
+{
+
+    return canvases.is_present(surface_id);
+}
+
+Canvas* DisplaySurfacesManger::get_canvas(int surface_id)
+{
+    return canvases.get(surface_id);
+}
+
 class DisplayHandler: public MessageHandlerImp<DisplayChannel, SPICE_MSG_END_DISPLAY> {
 public:
     DisplayHandler(DisplayChannel& channel)
@@ -644,7 +723,6 @@ DisplayChannel::DisplayChannel(RedClient& client, uint32_t id,
     : RedChannel(client, SPICE_CHANNEL_DISPLAY, id, new DisplayHandler(*this),
                  Platform::PRIORITY_LOW)
     , ScreenLayer (SCREEN_LAYER_DISPLAY, true)
-    , _canvas (NULL)
     , _pixmap_cache (pixmap_cache)
     , _glz_window (glz_window)
     , _mark (false)
@@ -706,6 +784,8 @@ DisplayChannel::DisplayChannel(RedClient& client, uint32_t id,
     get_process_loop().add_trigger(_gl_interrupt_recreate);
 #endif
     get_process_loop().add_trigger(_interrupt_update);
+
+    set_draw_handlers();
 }
 
 DisplayChannel::~DisplayChannel()
@@ -714,7 +794,7 @@ DisplayChannel::~DisplayChannel()
         screen()->set_update_interrupt_trigger(NULL);
     }
 
-    destroy_canvas();
+    //destroy_canvas(); fixme destroy all
     destroy_strams();
 }
 
@@ -786,33 +866,40 @@ void DisplayChannel::clear_draw_handlers()
 void DisplayChannel::copy_pixels(const QRegion& dest_region,
                                  const PixmapHeader &dest_pixmap)
 {
-    if (!_canvas.get()) {
+    Canvas *canvas;
+
+    if (!surfaces_mngr.is_present_canvas(0)) {
         return;
     }
 
-    _canvas->copy_pixels(dest_region, NULL, &dest_pixmap);
+    canvas = surfaces_mngr.get_canvas(0);
+    canvas->copy_pixels(dest_region, NULL, &dest_pixmap);
 }
 
 #ifdef USE_OGL
 void DisplayChannel::recreate_ogl_context_interrupt()
 {
-    Canvas* canvas = _canvas.release();
-    if (canvas) {
+    Canvas* canvas;
+
+    if (surfaces_mngr.is_present_canvas(0)) { //fix me to all surfaces
+        canvas = surfaces_mngr.get_canvas(0);
         ((GCanvas *)(canvas))->touch_context();
         ((GCanvas *)canvas)->textures_lost();
         delete canvas;
     }
 
-    if (!create_ogl_canvas(_x_res, _y_res, _depth, 0, _rendertype)) {
+    if (!create_ogl_canvas(0, _x_res, _y_res, _depth, 0, _rendertype)) {
         THROW("create_ogl_canvas failed");
     }
 
-    ((GCanvas *)(_canvas.get()))->touch_context();
+    canvas = surfaces_mngr.get_canvas(0);
+    ((GCanvas *)(canvas))->touch_context();
 }
 
 void DisplayChannel::recreate_ogl_context()
 {
-    if (_canvas.get() && _canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+    if (surfaces_mngr.is_present_canvas(0) && surfaces_mngr.get_canvas(0)->get_pixmap_type() ==
+        CANVAS_TYPE_GL) {
         if (!screen()->need_recreate_context_gl()) {
             _gl_interrupt_recreate.trigger();
         }
@@ -947,21 +1034,25 @@ void DisplayChannel::set_capture_mode(bool on)
 
 void DisplayChannel::update_interrupt()
 {
-    if (!_canvas.get() || !screen()) {
+    Canvas *canvas;
+
+    if (!surfaces_mngr.is_present_canvas(0) || !screen()) {
         return;
     }
 
+    canvas = surfaces_mngr.get_canvas(0);
+
 #ifdef USE_OGL
-    if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
-        ((GCanvas *)(_canvas.get()))->pre_gl_copy();
+    if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+        ((GCanvas *)(canvas))->pre_gl_copy();
     }
 #endif
 
     screen()->update();
 
 #ifdef USE_OGL
-    if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
-        ((GCanvas *)(_canvas.get()))->post_gl_copy();
+    if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+        ((GCanvas *)(canvas))->post_gl_copy();
     }
 #endif
 }
@@ -975,7 +1066,7 @@ void DisplayChannel::pre_migrate()
 void DisplayChannel::post_migrate()
 {
 #ifdef USE_OGL
-    if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+    if (surfaces_mngr.get_canvas(0)->get_pixmap_type() == CANVAS_TYPE_GL) {
         _gl_interrupt_recreate.trigger();
     }
 #endif
@@ -986,11 +1077,15 @@ void DisplayChannel::post_migrate()
 void DisplayChannel::copy_pixels(const QRegion& dest_region,
                                  RedDrawable& dest_dc)
 {
-    if (!_canvas.get()) {
+    Canvas *canvas;
+
+    if (!surfaces_mngr.is_present_canvas(0)) {
         return;
     }
 
-    _canvas->copy_pixels(dest_region, dest_dc);
+    canvas = surfaces_mngr.get_canvas(0);
+
+    canvas->copy_pixels(dest_region, dest_dc);
 }
 
 class ActivateTimerEvent: public Event {
@@ -1095,8 +1190,11 @@ void DisplayChannel::on_connect()
 
 void DisplayChannel::on_disconnect()
 {
-    if (_canvas.get()) {
-        _canvas->clear();
+    if (surfaces_mngr.is_present_canvas(0)) {
+        Canvas *canvas;
+
+        canvas = surfaces_mngr.get_canvas(0);
+        canvas->clear();
     }
 
     if (screen()) {
@@ -1116,13 +1214,17 @@ void DisplayChannel::on_disconnect()
     (*sync_event)->wait();
 }
 
-bool DisplayChannel::create_cairo_canvas(int width, int height, int depth)
+bool DisplayChannel::create_cairo_canvas(int surface_id, int width, int height, int depth)
 {
     try {
-        std::auto_ptr<CCanvas> canvas(new CCanvas(_pixmap_cache, _palette_cache, _glz_window));
+        CCanvas *canvas = new CCanvas(_pixmap_cache, _palette_cache, _glz_window,
+                                      surfaces_mngr.get_surfaces());
         canvas->set_mode(width, height, depth, screen()->get_window());
-        _canvas.reset(canvas.release());
-        LOG_INFO("display %d: using cairo", get_id());
+        surfaces_mngr.add_canvas(surface_id, canvas);
+        surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
+        if (surface_id == 0) {
+            LOG_INFO("display %d: using cairo", get_id());
+        }
     } catch (...) {
         return false;
     }
@@ -1130,29 +1232,29 @@ bool DisplayChannel::create_cairo_canvas(int width, int height, int depth)
 }
 
 #ifdef USE_OGL
-bool DisplayChannel::create_ogl_canvas(int width, int height, int depth,
+bool DisplayChannel::create_ogl_canvas(int surface_id, int width, int height, int depth,
                                        bool recreate, RenderType rendertype)
 {
     try {
         RedWindow *win;
-        int ret = 1;
 
-        std::auto_ptr<GCanvas> canvas(new GCanvas(_pixmap_cache,
-                                                  _palette_cache,
-                                                  _glz_window));
+        GCanvas *canvas = new GCanvas(_pixmap_cache,
+                                      _palette_cache,
+                                      _glz_window,
+                                      surfaces_mngr.get_surfaces());
 
         win = screen()->get_window();
-        if (!ret) {
-            return false;
-        }
 
         canvas->set_mode(width, height, depth, win, rendertype);
 
         screen()->untouch_context();
 
-        _canvas.reset(canvas.release());
+        surfaces_mngr.add_canvas(surface_id, canvas);
+        surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
         _rendertype = rendertype;
-        LOG_INFO("display %d: using ogl", get_id());
+        if (surface_id == 0) {
+            LOG_INFO("display %d: using ogl", get_id());
+        }
     } catch (...) {
         return false;
     }
@@ -1162,14 +1264,17 @@ bool DisplayChannel::create_ogl_canvas(int width, int height, int depth,
 #endif
 
 #ifdef WIN32
-bool DisplayChannel::create_gdi_canvas(int width, int height, int depth)
+bool DisplayChannel::create_gdi_canvas(int surface_id, int width, int height, int depth)
 {
     try {
-        std::auto_ptr<GDICanvas> canvas(
-            new GDICanvas(_pixmap_cache, _palette_cache, _glz_window));
+        GDICanvas *canvas = new GDICanvas(_pixmap_cache, _palette_cache, _glz_window,
+                                          surfaces_mngr.get_surfaces());
         canvas->set_mode(width, height, depth);
-        _canvas.reset(canvas.release());
-        LOG_INFO("display %d: using gdi", get_id());
+        surfaces_mngr.add_canvas(surface_id, canvas);
+        surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
+        if (surface_id == 0) {
+            LOG_INFO("display %d: using gdi", get_id());
+        }
     } catch (...) {
         return false;
     }
@@ -1178,21 +1283,29 @@ bool DisplayChannel::create_gdi_canvas(int width, int height, int depth)
 
 #endif
 
-void DisplayChannel::destroy_canvas()
+void DisplayChannel::destroy_canvas(int surface_id)
 {
-    Canvas* canvas = _canvas.release();
+    Canvas *canvas;
+
+    if (!surfaces_mngr.is_present_canvas(surface_id)) {
+        return;
+    }
+    
+    canvas = surfaces_mngr.get_canvas(surface_id);
 
-    if (canvas) {
 #ifdef USE_OGL
-        if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
-            ((GCanvas *)(canvas))->touch_context();
-        }
-#endif
-        delete canvas;
+    if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+        ((GCanvas *)(canvas))->touch_context();
     }
+#endif
+
+    surfaces_mngr.del_canvas(surface_id);
+    surfaces_mngr.del_surface(surface_id);
+
+    delete canvas;
 }
 
-void DisplayChannel::create_canvas(const std::vector<int>& canvas_types, int width,
+void DisplayChannel::create_canvas(int surface_id, const std::vector<int>& canvas_types, int width,
                                    int height, int depth)
 {
 #ifdef USE_OGL
@@ -1200,8 +1313,6 @@ void DisplayChannel::create_canvas(const std::vector<int>& canvas_types, int wid
 #endif
     unsigned int i;
 
-    clear_draw_handlers();
-
 #ifdef USE_OGL
     if (screen()->need_recreate_context_gl()) {
         recreate = false;
@@ -1212,23 +1323,23 @@ void DisplayChannel::create_canvas(const std::vector<int>& canvas_types, int wid
 
     for (i = 0; i < canvas_types.size(); i++) {
 
-        if (canvas_types[i] == CANVAS_OPTION_CAIRO && create_cairo_canvas(width, height, depth)) {
+        if (canvas_types[i] == CANVAS_OPTION_CAIRO && create_cairo_canvas(surface_id, width, height, depth)) {
             break;
         }
 #ifdef USE_OGL
-        if (canvas_types[i] == CANVAS_OPTION_OGL_FBO && create_ogl_canvas(width, height, depth,
+        if (canvas_types[i] == CANVAS_OPTION_OGL_FBO && create_ogl_canvas(surface_id, width, height, depth,
                                                                           recreate,
                                                                           RENDER_TYPE_FBO)) {
             break;
         }
-        if (canvas_types[i] == CANVAS_OPTION_OGL_PBUFF && create_ogl_canvas(width, height, depth,
+        if (canvas_types[i] == CANVAS_OPTION_OGL_PBUFF && create_ogl_canvas(surface_id, width, height, depth,
                                                                             recreate,
                                                                             RENDER_TYPE_PBUFF)) {
             break;
         }
 #endif
 #ifdef WIN32
-        if (canvas_types[i] == CANVAS_OPTION_GDI && create_gdi_canvas(width, height, depth)) {
+        if (canvas_types[i] == CANVAS_OPTION_GDI && create_gdi_canvas(surface_id, width, height, depth)) {
             break;
         }
 #endif
@@ -1237,8 +1348,6 @@ void DisplayChannel::create_canvas(const std::vector<int>& canvas_types, int wid
     if (i == canvas_types.size()) {
         THROW("create canvas failed");
     }
-
-    set_draw_handlers();
 }
 
 void DisplayChannel::handle_mark(RedPeer::InMessage *message)
@@ -1258,8 +1367,10 @@ void DisplayChannel::handle_reset(RedPeer::InMessage *message)
 {
     screen()->set_update_interrupt_trigger(NULL);
 
-    if (_canvas.get()) {
-        _canvas->clear();
+    if (surfaces_mngr.is_present_canvas(0)) {
+        Canvas *canvas;
+        canvas = surfaces_mngr.get_canvas(0);
+        canvas->clear();
     }
 
     AutoRef<ResetTimer> reset_timer(new ResetTimer(screen()->ref(), get_client()));
@@ -1341,8 +1452,8 @@ void DisplayChannel::set_clip_rects(const SpiceClip& clip, uint32_t& num_clip_re
 void DisplayChannel::handle_stream_create(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayStreamCreate* stream_create = (SpiceMsgDisplayStreamCreate*)message->data();
+    int surface_id = stream_create->surface_id;
 
-    PANIC_ON(stream_create->surface_id != 0);
     Lock lock(_streams_lock);
     if (_streams.size() <= stream_create->id) {
         _streams.resize(stream_create->id + 1);
@@ -1357,7 +1468,7 @@ void DisplayChannel::handle_stream_create(RedPeer::InMessage* message)
     set_clip_rects(stream_create->clip, num_clip_rects, clip_rects,
                    (unsigned long)message->data(), (uint8_t*)(stream_create + 1),
                    message->data() + message->size());
-    _streams[stream_create->id] = new VideoStream(get_client(), *_canvas.get(),
+    _streams[stream_create->id] = new VideoStream(get_client(), *surfaces_mngr.get_canvas(surface_id),
                                                   *this, stream_create->codec_type,
                                                   !!(stream_create->flags & SPICE_STREAM_FLAGS_TOP_DOWN),
                                                   stream_create->stream_width,
@@ -1443,6 +1554,7 @@ void DisplayChannel::handle_stream_destroy_all(RedPeer::InMessage* message)
 
 void DisplayChannel::create_primary_surface(int width, int height, int depth)
 {
+   Canvas *canvas;
    _mark = false;
     attach_to_screen(get_client().get_application(), get_id());
     clear_area();
@@ -1459,21 +1571,47 @@ void DisplayChannel::create_primary_surface(int width, int height, int depth)
     _y_res = height;
     _depth = depth;
 
+    canvas = surfaces_mngr.get_canvas(0);
+
 #ifdef USE_OGL
-    if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
-        ((GCanvas *)(_canvas.get()))->touch_context();
+    if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+        ((GCanvas *)(canvas))->touch_context();
         screen()->set_update_interrupt_trigger(&_interrupt_update);
         screen()->set_type_gl();
     }
 #endif
 }
 
+void DisplayChannel::create_surface(int surface_id, int width, int height, int depth)
+{
+   Canvas *canvas;
+
+    AutoRef<CreateSurfaceEvent> event(new CreateSurfaceEvent(*this, surface_id, width, height,
+                                                             depth));
+    get_client().push_event(*event);
+    (*event)->wait();
+    if (!(*event)->success()) {
+        THROW("Create surface failed");
+    }
+
+    canvas = surfaces_mngr.get_canvas(surface_id);
+
+#ifdef USE_OGL
+    if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+        ((GCanvas *)(canvas))->touch_context();
+    }
+#endif
+}
+
 void DisplayChannel::destroy_primary_surface()
 {
 #ifdef USE_OGL
     if (screen()) {
-        if (_canvas.get()) {
-            if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+        if (surfaces_mngr.is_present_canvas(0)) {
+            Canvas *canvas;
+
+            canvas = surfaces_mngr.get_canvas(0);
+            if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
                 screen()->unset_type_gl();
                 screen()->untouch_context();
             }
@@ -1481,6 +1619,9 @@ void DisplayChannel::destroy_primary_surface()
     }
 #endif
 
+	AutoRef<UnlockScreenEvent> unlock_event(new UnlockScreenEvent(screen()));
+    get_client().push_event(*unlock_event);
+
     AutoRef<DestroyPrimarySurfaceEvent> event(new DestroyPrimarySurfaceEvent(*this));
     get_client().push_event(*event);
     (*event)->wait();
@@ -1489,21 +1630,35 @@ void DisplayChannel::destroy_primary_surface()
     }
 }
 
+void DisplayChannel::destroy_surface(int surface_id)
+{
+    AutoRef<DestroySurfaceEvent> event(new DestroySurfaceEvent(*this, surface_id));
+    get_client().push_event(*event);
+    (*event)->wait();
+    if (!(*event)->success()) {
+        THROW("Destroying surface failed");
+    }
+}
+
 void DisplayChannel::handle_surface_create(RedPeer::InMessage* message)
 {
     SpiceMsgSurfaceCreate* surface_create = (SpiceMsgSurfaceCreate*)message->data();
-    PANIC_ON(surface_create->surface_id != 0);
-    PANIC_ON(surface_create->flags != SPICE_SURFACE_FLAGS_PRIMARY);
-
-    create_primary_surface(surface_create->width, surface_create->height, surface_create->depth);
+    if (surface_create->flags == SPICE_SURFACE_FLAGS_PRIMARY) {
+        create_primary_surface(surface_create->width, surface_create->height, surface_create->depth);
+    } else {
+        create_surface(surface_create->surface_id, surface_create->width, surface_create->height,
+                       surface_create->depth);
+    }
 }
 
 void DisplayChannel::handle_surface_destroy(RedPeer::InMessage* message)
 {
     SpiceMsgSurfaceDestroy* surface_destroy = (SpiceMsgSurfaceDestroy*)message->data();
-    PANIC_ON(surface_destroy->surface_id != 0);
-
-    destroy_primary_surface();
+    if (surface_destroy->surface_id == 0) { //fixme
+        destroy_primary_surface();
+    } else {
+        destroy_surface(surface_destroy->surface_id);
+    }
 }
 
 #define PRE_DRAW
@@ -1511,102 +1666,115 @@ void DisplayChannel::handle_surface_destroy(RedPeer::InMessage* message)
 
 #define DRAW(type) {                                \
     PRE_DRAW;                                       \
-    _canvas->draw_##type(*type, message->size());   \
+    canvas->draw_##type(*type, message->size());    \
     POST_DRAW;                                      \
     invalidate(type->base.box, false);              \
 }
 
 void DisplayChannel::handle_copy_bits(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayCopyBits* copy_bits = (SpiceMsgDisplayCopyBits*)message->data();
-    PANIC_ON(copy_bits->base.surface_id != 0);
     PRE_DRAW;
-    _canvas->copy_bits(*copy_bits, message->size());
+    canvas = surfaces_mngr.get_canvas(copy_bits->base.surface_id);
+    canvas->copy_bits(*copy_bits, message->size());
     POST_DRAW;
     invalidate(copy_bits->base.box, false);
 }
 
 void DisplayChannel::handle_draw_fill(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawFill* fill = (SpiceMsgDisplayDrawFill*)message->data();
-    PANIC_ON(fill->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(fill->base.surface_id);
     DRAW(fill);
 }
 
 void DisplayChannel::handle_draw_opaque(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawOpaque* opaque = (SpiceMsgDisplayDrawOpaque*)message->data();
-    PANIC_ON(opaque->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(opaque->base.surface_id);
     DRAW(opaque);
 }
 
 void DisplayChannel::handle_draw_copy(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawCopy* copy = (SpiceMsgDisplayDrawCopy*)message->data();
-    PANIC_ON(copy->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(copy->base.surface_id);
     DRAW(copy);
 }
 
 void DisplayChannel::handle_draw_blend(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawBlend* blend = (SpiceMsgDisplayDrawBlend*)message->data();
-    PANIC_ON(blend->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(blend->base.surface_id);
     DRAW(blend);
 }
 
 void DisplayChannel::handle_draw_blackness(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawBlackness* blackness = (SpiceMsgDisplayDrawBlackness*)message->data();
-    PANIC_ON(blackness->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(blackness->base.surface_id);
     DRAW(blackness);
 }
 
 void DisplayChannel::handle_draw_whiteness(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawWhiteness* whiteness = (SpiceMsgDisplayDrawWhiteness*)message->data();
-    PANIC_ON(whiteness->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(whiteness->base.surface_id);
     DRAW(whiteness);
 }
 
 void DisplayChannel::handle_draw_invers(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawInvers* invers = (SpiceMsgDisplayDrawInvers*)message->data();
-    PANIC_ON(invers->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(invers->base.surface_id);
     DRAW(invers);
 }
 
 void DisplayChannel::handle_draw_rop3(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawRop3* rop3 = (SpiceMsgDisplayDrawRop3*)message->data();
-    PANIC_ON(rop3->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(rop3->base.surface_id);
     DRAW(rop3);
 }
 
 void DisplayChannel::handle_draw_stroke(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawStroke* stroke = (SpiceMsgDisplayDrawStroke*)message->data();
-    PANIC_ON(stroke->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(stroke->base.surface_id);
     DRAW(stroke);
 }
 
 void DisplayChannel::handle_draw_text(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawText* text = (SpiceMsgDisplayDrawText*)message->data();
-    PANIC_ON(text->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(text->base.surface_id);
     DRAW(text);
 }
 
 void DisplayChannel::handle_draw_transparent(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawTransparent* transparent = (SpiceMsgDisplayDrawTransparent*)message->data();
-    PANIC_ON(transparent->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(transparent->base.surface_id);
     DRAW(transparent);
 }
 
 void DisplayChannel::handle_draw_alpha_blend(RedPeer::InMessage* message)
 {
+    Canvas *canvas;
     SpiceMsgDisplayDrawAlphaBlend* alpha_blend = (SpiceMsgDisplayDrawAlphaBlend*)message->data();
-    PANIC_ON(alpha_blend->base.surface_id != 0);
+    canvas = surfaces_mngr.get_canvas(alpha_blend->base.surface_id);
     DRAW(alpha_blend);
 }
 
diff --git a/client/display_channel.h b/client/display_channel.h
index d603a26..b0bfbf2 100644
--- a/client/display_channel.h
+++ b/client/display_channel.h
@@ -79,6 +79,21 @@ private:
     DisplayChannel& _channel;
 };
 
+class DisplaySurfacesManger {
+public:
+    void add_surface(int surface_id, SpiceCanvas *surface);
+    void del_surface(int surface_id);
+    void add_canvas(int surface_id, Canvas *canvas);
+    void del_canvas(int surface_id);
+
+    CSurfaces& get_surfaces();
+    bool is_present_canvas(int surface_id);
+    Canvas* get_canvas(int surface_id);
+private:
+    CSurfaces surfaces;
+    CCanvases canvases;
+};
+
 class DisplayChannel: public RedChannel, public ScreenLayer {
 public:
     DisplayChannel(RedClient& client, uint32_t id,
@@ -117,22 +132,24 @@ protected:
 private:
     void set_draw_handlers();
     void clear_draw_handlers();
-    bool create_cairo_canvas(int width, int height, int depth);
+    bool create_cairo_canvas(int surface_id, int width, int height, int depth);
 #ifdef USE_OGL
-    bool create_ogl_canvas(int width, int height, int depth, bool recreate,
+    bool create_ogl_canvas(int surface_id, int width, int height, int depth, bool recreate,
                            RenderType rendertype);
 #endif
 #ifdef WIN32
-    bool create_gdi_canvas(int width, int height, int depth);
+    bool create_gdi_canvas(int surface_id, int width, int height, int depth);
 #endif
-    void destroy_canvas();
-    void create_canvas(const std::vector<int>& canvas_type, int width, int height,
+    void destroy_canvas(int surface_id);
+    void create_canvas(int surface_id, const std::vector<int>& canvas_type, int width, int height,
                        int depth);
     void destroy_strams();
     void update_cursor();
 
     void create_primary_surface(int width, int height, int depth);
+    void create_surface(int surface_id, int width, int height, int depth);
     void destroy_primary_surface();
+    void destroy_surface(int surface_id);
 
     void handle_mode(RedPeer::InMessage* message);
     void handle_mark(RedPeer::InMessage* message);
@@ -174,7 +191,7 @@ private:
     static void set_clip_rects(const SpiceClip& clip, uint32_t& num_clip_rects, SpiceRect*& clip_rects,
                                unsigned long addr_offset, uint8_t *min, uint8_t *max);
 private:
-    std::auto_ptr<Canvas> _canvas;
+    DisplaySurfacesManger surfaces_mngr;
     PixmapCache& _pixmap_cache;
     PaletteCache _palette_cache;
     GlzDecoderWindow& _glz_window;
@@ -216,6 +233,8 @@ private:
     friend class SetModeEvent;
     friend class CreatePrimarySurfaceEvent;
     friend class DestroyPrimarySurfaceEvent;
+    friend class CreateSurfaceEvent;
+    friend class DestroySurfaceEvent;
     friend class ActivateTimerEvent;
     friend class VideoStream;
     friend class StreamsTrigger;
diff --git a/client/red_cairo_canvas.cpp b/client/red_cairo_canvas.cpp
index 02c105a..71621d1 100644
--- a/client/red_cairo_canvas.cpp
+++ b/client/red_cairo_canvas.cpp
@@ -25,8 +25,8 @@
 #include "red_pixmap_cairo.h"
 
 CCanvas::CCanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-                 GlzDecoderWindow &glz_decoder_window)
-    : Canvas (pixmap_cache, palette_cache, glz_decoder_window)
+                 GlzDecoderWindow &glz_decoder_window, CSurfaces& csurfaces)
+    : Canvas (pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
     , _pixmap (0)
 {
 }
@@ -94,6 +94,7 @@ void CCanvas::set_mode(int width, int height, int depth, RedWindow *win)
     if (!(_canvas = canvas_create(surface, depth,
                                   &pixmap_cache().base,
                                   &palette_cache().base,
+                                  &csurfaces().base,
                                   &glz_decoder()))) {
         THROW("create canvas failed");
     }
diff --git a/client/red_cairo_canvas.h b/client/red_cairo_canvas.h
index 51c6c5a..ce76fef 100644
--- a/client/red_cairo_canvas.h
+++ b/client/red_cairo_canvas.h
@@ -26,7 +26,7 @@ class RedPixmap;
 class CCanvas: public Canvas {
 public:
     CCanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-            GlzDecoderWindow &glz_decoder_window);
+            GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces);
     virtual ~CCanvas();
 
     virtual void set_mode(int x, int y, int bits, RedWindow *win);
diff --git a/client/red_gdi_canvas.cpp b/client/red_gdi_canvas.cpp
index 9f38dfb..bfab546 100644
--- a/client/red_gdi_canvas.cpp
+++ b/client/red_gdi_canvas.cpp
@@ -24,8 +24,8 @@
 #include "red_pixmap_gdi.h"
 
 GDICanvas::GDICanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-                     GlzDecoderWindow &glz_decoder_window)
-    : Canvas (pixmap_cache, palette_cache, glz_decoder_window)
+                     GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces)
+    : Canvas (pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
     , _pixmap (0)
 {
 }
@@ -38,6 +38,7 @@ GDICanvas::~GDICanvas()
 void GDICanvas::destroy()
 {
     if (_canvas) {
+        _canvas->ops->destroy(_canvas);
         _canvas = NULL;
     }
     destroy_pixmap();
@@ -84,6 +85,7 @@ void GDICanvas::set_mode(int width, int height, int depth)
                                       &_pixmap->get_mutex(),
                                       depth, &pixmap_cache().base,
                                       &palette_cache().base,
+                                      &csurfaces().base,
                                       &glz_decoder()))) {
         THROW("create canvas failed");
     }
diff --git a/client/red_gdi_canvas.h b/client/red_gdi_canvas.h
index 83fc120..b0a2158 100644
--- a/client/red_gdi_canvas.h
+++ b/client/red_gdi_canvas.h
@@ -28,7 +28,7 @@ class RedPixmap;
 class GDICanvas: public Canvas {
 public:
     GDICanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-              GlzDecoderWindow &glz_decoder_window);
+              GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces);
     virtual ~GDICanvas();
 
     virtual void set_mode(int x, int y, int bits);
diff --git a/client/red_gl_canvas.cpp b/client/red_gl_canvas.cpp
index c6bb113..f9c4256 100644
--- a/client/red_gl_canvas.cpp
+++ b/client/red_gl_canvas.cpp
@@ -25,8 +25,8 @@
 #include <GL/glx.h>
 
 GCanvas::GCanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-                 GlzDecoderWindow &glz_decoder_window)
-    : Canvas(pixmap_cache, palette_cache, glz_decoder_window)
+                 GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces)
+    : Canvas(pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
     , _pixmap (0)
     , _textures_lost (false)
 {
@@ -92,6 +92,7 @@ void GCanvas::set_mode(int width, int height, int depth, RedWindow *win,
     if (!(_canvas = gl_canvas_create(width, height, depth,
                                      &pixmap_cache().base,
                                      &palette_cache().base,
+                                     &csurfaces().base,
                                      &glz_decoder()))) {
         THROW("create canvas failed");
     }
diff --git a/client/red_gl_canvas.h b/client/red_gl_canvas.h
index 983f772..4a9f5a9 100644
--- a/client/red_gl_canvas.h
+++ b/client/red_gl_canvas.h
@@ -29,7 +29,7 @@ class RedPixmapGL;
 class GCanvas: public Canvas {
 public:
     GCanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
-            GlzDecoderWindow &glz_decoder_window);
+            GlzDecoderWindow &glz_decoder_window, CSurfaces &csurfaces);
     virtual ~GCanvas();
 
     void set_mode(int width, int height, int depth, RedWindow *win,
diff --git a/common/cairo_canvas.c b/common/cairo_canvas.c
index 770ca4f..17a40a4 100644
--- a/common/cairo_canvas.c
+++ b/common/cairo_canvas.c
@@ -51,11 +51,18 @@ static pixman_image_t *canvas_get_pixman_brush(CairoCanvas *canvas,
 
         return pixman_image_create_solid_fill(&c);
     }
-    case SPICE_BRUSH_TYPE_PATTERN: {
+    case SPICE_BRUSH_TYPE_PATTERN: { 
+        CairoCanvas *surface_canvas;
         pixman_image_t* surface;
         pixman_transform_t t;
 
-        surface = canvas_get_image(&canvas->base, brush->u.pattern.pat);
+        surface_canvas = (CairoCanvas *)canvas_get_surface(&canvas->base, brush->u.pattern.pat);
+        if (surface_canvas) {
+            surface = surface_canvas->image;
+            surface = pixman_image_ref(surface);
+        } else {
+            surface = canvas_get_image(&canvas->base, brush->u.pattern.pat);
+        }
         pixman_transform_init_translate(&t,
                                         pixman_int_to_fixed(-brush->u.pattern.pos.x),
                                         pixman_int_to_fixed(-brush->u.pattern.pos.y));
@@ -70,6 +77,14 @@ static pixman_image_t *canvas_get_pixman_brush(CairoCanvas *canvas,
     }
 }
 
+static pixman_image_t *get_image(SpiceCanvas *canvas)
+{
+    CairoCanvas *cairo_canvas = (CairoCanvas *)canvas;
+
+    pixman_image_ref(cairo_canvas->image);
+
+    return cairo_canvas->image;
+}
 
 static void copy_region(SpiceCanvas *spice_canvas,
                         pixman_region32_t *dest_region,
@@ -194,16 +209,16 @@ static void fill_solid_rects_rop(SpiceCanvas *spice_canvas,
     }
 }
 
-static void fill_tiled_rects(SpiceCanvas *spice_canvas,
-                             pixman_box32_t *rects,
-                             int n_rects,
-                             pixman_image_t *tile,
-                             int offset_x, int offset_y)
+static void __fill_tiled_rects(SpiceCanvas *spice_canvas,
+                               pixman_box32_t *rects,
+                               int n_rects,
+                               pixman_image_t *tile,
+                               int offset_x, int offset_y)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     int i;
 
-   for (i = 0; i < n_rects; i++) {
+    for (i = 0; i < n_rects; i++) {
         spice_pixman_tile_rect(canvas->image,
                                rects[i].x1, rects[i].y1,
                                rects[i].x2 - rects[i].x1,
@@ -212,17 +227,37 @@ static void fill_tiled_rects(SpiceCanvas *spice_canvas,
     }
 }
 
-static void fill_tiled_rects_rop(SpiceCanvas *spice_canvas,
-                                 pixman_box32_t *rects,
-                                 int n_rects,
-                                 pixman_image_t *tile,
-                                 int offset_x, int offset_y,
-                                 SpiceROP rop)
+static void fill_tiled_rects(SpiceCanvas *spice_canvas,
+                               pixman_box32_t *rects,
+                               int n_rects,
+                               pixman_image_t *tile,
+                               int offset_x, int offset_y)
+{
+    __fill_tiled_rects(spice_canvas, rects, n_rects, tile, offset_x, offset_y);
+}
+
+static void fill_tiled_rects_from_surface(SpiceCanvas *spice_canvas,
+                                          pixman_box32_t *rects,
+                                          int n_rects,
+                                          SpiceCanvas *surface_canvas,
+                                          int offset_x, int offset_y)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __fill_tiled_rects(spice_canvas, rects, n_rects, cairo_surface_canvas->image, offset_x,
+                       offset_y);
+}
+
+static void __fill_tiled_rects_rop(SpiceCanvas *spice_canvas,
+                                   pixman_box32_t *rects,
+                                   int n_rects,
+                                   pixman_image_t *tile,
+                                   int offset_x, int offset_y,
+                                   SpiceROP rop)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     int i;
 
-   for (i = 0; i < n_rects; i++) {
+    for (i = 0; i < n_rects; i++) {
         spice_pixman_tile_rect_rop(canvas->image,
                                    rects[i].x1, rects[i].y1,
                                    rects[i].x2 - rects[i].x1,
@@ -231,11 +266,32 @@ static void fill_tiled_rects_rop(SpiceCanvas *spice_canvas,
                                    rop);
     }
 }
+static void fill_tiled_rects_rop(SpiceCanvas *spice_canvas,
+                                 pixman_box32_t *rects,
+                                 int n_rects,
+                                 pixman_image_t *tile,
+                                 int offset_x, int offset_y,
+                                 SpiceROP rop)
+{
+    __fill_tiled_rects_rop(spice_canvas, rects, n_rects, tile, offset_x, offset_y, rop);
+}
 
-static void blit_image(SpiceCanvas *spice_canvas,
-                       pixman_region32_t *region,
-                       pixman_image_t *src_image,
-                       int offset_x, int offset_y)
+static void fill_tiled_rects_rop_from_surface(SpiceCanvas *spice_canvas,
+                                              pixman_box32_t *rects,
+                                              int n_rects,
+                                              SpiceCanvas *surface_canvas,
+                                              int offset_x, int offset_y,
+                                              SpiceROP rop)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __fill_tiled_rects_rop(spice_canvas, rects, n_rects, cairo_surface_canvas->image, offset_x,
+                           offset_y, rop);
+}
+
+static void __blit_image(SpiceCanvas *spice_canvas,
+                         pixman_region32_t *region,
+                         pixman_image_t *src_image,
+                         int offset_x, int offset_y)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_box32_t *rects;
@@ -262,11 +318,28 @@ static void blit_image(SpiceCanvas *spice_canvas,
     }
 }
 
-static void blit_image_rop(SpiceCanvas *spice_canvas,
-                           pixman_region32_t *region,
-                           pixman_image_t *src_image,
-                           int offset_x, int offset_y,
-                           SpiceROP rop)
+static void blit_image(SpiceCanvas *spice_canvas,
+                       pixman_region32_t *region,
+                       pixman_image_t *src_image,
+                       int offset_x, int offset_y)
+{
+    __blit_image(spice_canvas, region, src_image, offset_x, offset_y);
+}
+
+static void blit_image_from_surface(SpiceCanvas *spice_canvas,
+                                    pixman_region32_t *region,
+                                    SpiceCanvas *surface_canvas,
+                                    int offset_x, int offset_y)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __blit_image(spice_canvas, region, cairo_surface_canvas->image, offset_x, offset_y);
+}
+
+static void __blit_image_rop(SpiceCanvas *spice_canvas,
+                             pixman_region32_t *region,
+                             pixman_image_t *src_image,
+                             int offset_x, int offset_y,
+                             SpiceROP rop)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_box32_t *rects;
@@ -293,14 +366,35 @@ static void blit_image_rop(SpiceCanvas *spice_canvas,
     }
 }
 
-static void scale_image(SpiceCanvas *spice_canvas,
-                        pixman_region32_t *region,
-                        pixman_image_t *src,
-                        int src_x, int src_y,
-                        int src_width, int src_height,
-                        int dest_x, int dest_y,
-                        int dest_width, int dest_height,
-                        int scale_mode)
+static void blit_image_rop(SpiceCanvas *spice_canvas,
+                           pixman_region32_t *region,
+                           pixman_image_t *src_image,
+                           int offset_x, int offset_y,
+                           SpiceROP rop)
+{
+    __blit_image_rop(spice_canvas, region, src_image, offset_x, offset_y, rop);
+}
+
+static void blit_image_rop_from_surface(SpiceCanvas *spice_canvas,
+                                        pixman_region32_t *region,
+                                        SpiceCanvas *surface_canvas,
+                                        int offset_x, int offset_y,
+                                        SpiceROP rop)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __blit_image_rop(spice_canvas, region, cairo_surface_canvas->image, offset_x, offset_y, rop);
+}
+
+
+
+static void __scale_image(SpiceCanvas *spice_canvas,
+                          pixman_region32_t *region,
+                          pixman_image_t *src,
+                          int src_x, int src_y,
+                          int src_width, int src_height,
+                          int dest_x, int dest_y,
+                          int dest_width, int dest_height,
+                          int scale_mode)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_transform_t transform;
@@ -337,14 +431,41 @@ static void scale_image(SpiceCanvas *spice_canvas,
     pixman_image_set_clip_region32(canvas->image, NULL);
 }
 
-static void scale_image_rop(SpiceCanvas *spice_canvas,
-                            pixman_region32_t *region,
-                            pixman_image_t *src,
-                            int src_x, int src_y,
-                            int src_width, int src_height,
-                            int dest_x, int dest_y,
-                            int dest_width, int dest_height,
-                            int scale_mode, SpiceROP rop)
+static void scale_image(SpiceCanvas *spice_canvas,
+                        pixman_region32_t *region,
+                        pixman_image_t *src,
+                        int src_x, int src_y,
+                        int src_width, int src_height,
+                        int dest_x, int dest_y,
+                        int dest_width, int dest_height,
+                        int scale_mode)
+{
+    __scale_image(spice_canvas, region, src, src_x, src_y, src_width, src_height, dest_x, dest_y,
+                  dest_width,dest_height,scale_mode);
+}
+
+static void scale_image_from_surface(SpiceCanvas *spice_canvas,
+                                     pixman_region32_t *region,
+                                     SpiceCanvas *surface_canvas,
+                                     int src_x, int src_y,
+                                     int src_width, int src_height,
+                                     int dest_x, int dest_y,
+                                     int dest_width, int dest_height,
+                                     int scale_mode)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __scale_image(spice_canvas, region, cairo_surface_canvas->image, src_x, src_y, src_width,
+                  src_height, dest_x, dest_y, dest_width,dest_height,scale_mode);
+}
+
+static void __scale_image_rop(SpiceCanvas *spice_canvas,
+                              pixman_region32_t *region,
+                              pixman_image_t *src,
+                              int src_x, int src_y,
+                              int src_width, int src_height,
+                              int dest_x, int dest_y,
+                              int dest_width, int dest_height,
+                              int scale_mode, SpiceROP rop)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_transform_t transform;
@@ -407,13 +528,40 @@ static void scale_image_rop(SpiceCanvas *spice_canvas,
     pixman_image_unref(scaled);
 }
 
-static void blend_image(SpiceCanvas *spice_canvas,
-                        pixman_region32_t *region,
-                        pixman_image_t *src,
-                        int src_x, int src_y,
-                        int dest_x, int dest_y,
-                        int width, int height,
-                        int overall_alpha)
+static void scale_image_rop(SpiceCanvas *spice_canvas,
+                            pixman_region32_t *region,
+                            pixman_image_t *src,
+                            int src_x, int src_y,
+                            int src_width, int src_height,
+                            int dest_x, int dest_y,
+                            int dest_width, int dest_height,
+                            int scale_mode, SpiceROP rop)
+{
+    __scale_image_rop(spice_canvas, region, src, src_x, src_y, src_width, src_height, dest_x,
+                      dest_y, dest_width, dest_height, scale_mode, rop);
+}
+
+static void scale_image_rop_from_surface(SpiceCanvas *spice_canvas,
+                                         pixman_region32_t *region,
+                                         SpiceCanvas *surface_canvas,
+                                         int src_x, int src_y,
+                                         int src_width, int src_height,
+                                         int dest_x, int dest_y,
+                                         int dest_width, int dest_height,
+                                         int scale_mode, SpiceROP rop)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __scale_image_rop(spice_canvas, region, cairo_surface_canvas->image, src_x, src_y, src_width,
+                      src_height, dest_x, dest_y, dest_width, dest_height, scale_mode, rop);
+}
+
+static void __blend_image(SpiceCanvas *spice_canvas,
+                          pixman_region32_t *region,
+                          pixman_image_t *src,
+                          int src_x, int src_y,
+                          int dest_x, int dest_y,
+                          int width, int height,
+                          int overall_alpha)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_image_t *mask;
@@ -444,15 +592,40 @@ static void blend_image(SpiceCanvas *spice_canvas,
     pixman_image_set_clip_region32(canvas->image, NULL);
 }
 
-static void blend_scale_image(SpiceCanvas *spice_canvas,
-                              pixman_region32_t *region,
-                              pixman_image_t *src,
-                              int src_x, int src_y,
-                              int src_width, int src_height,
-                              int dest_x, int dest_y,
-                              int dest_width, int dest_height,
-                              int scale_mode,
-                              int overall_alpha)
+static void blend_image(SpiceCanvas *spice_canvas,
+                        pixman_region32_t *region,
+                        pixman_image_t *src,
+                        int src_x, int src_y,
+                        int dest_x, int dest_y,
+                        int width, int height,
+                        int overall_alpha)
+{
+    __blend_image(spice_canvas, region, src, src_x, src_y, dest_x, dest_y, width, height,
+                  overall_alpha);
+}
+
+static void blend_image_from_surface(SpiceCanvas *spice_canvas,
+                                     pixman_region32_t *region,
+                                     SpiceCanvas *surface_canvas,
+                                     int src_x, int src_y,
+                                     int dest_x, int dest_y,
+                                     int width, int height,
+                                     int overall_alpha)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __blend_image(spice_canvas, region, cairo_surface_canvas->image, src_x, src_y, dest_x, dest_y,
+                  width, height, overall_alpha);
+}
+
+static void __blend_scale_image(SpiceCanvas *spice_canvas,
+                                pixman_region32_t *region,
+                                pixman_image_t *src,
+                                int src_x, int src_y,
+                                int src_width, int src_height,
+                                int dest_x, int dest_y,
+                                int dest_width, int dest_height,
+                                int scale_mode,
+                                int overall_alpha)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_transform_t transform;
@@ -501,11 +674,41 @@ static void blend_scale_image(SpiceCanvas *spice_canvas,
     pixman_image_set_clip_region32(canvas->image, NULL);
 }
 
-static void colorkey_image(SpiceCanvas *spice_canvas,
-                           pixman_region32_t *region,
-                           pixman_image_t *src_image,
-                           int offset_x, int offset_y,
-                           uint32_t transparent_color)
+static void blend_scale_image(SpiceCanvas *spice_canvas,
+                              pixman_region32_t *region,
+                              pixman_image_t *src,
+                              int src_x, int src_y,
+                              int src_width, int src_height,
+                              int dest_x, int dest_y,
+                              int dest_width, int dest_height,
+                              int scale_mode,
+                              int overall_alpha)
+{
+    __blend_scale_image(spice_canvas, region, src, src_x, src_y, src_width, src_height, dest_x,
+                        dest_y, dest_width, dest_height, scale_mode, overall_alpha);
+}
+
+static void blend_scale_image_from_surface(SpiceCanvas *spice_canvas,
+                                           pixman_region32_t *region,
+                                           SpiceCanvas *surface_canvas,
+                                           int src_x, int src_y,
+                                           int src_width, int src_height,
+                                           int dest_x, int dest_y,
+                                           int dest_width, int dest_height,
+                                           int scale_mode,
+                                           int overall_alpha)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __blend_scale_image(spice_canvas, region, cairo_surface_canvas->image, src_x, src_y, src_width,
+                        src_height, dest_x, dest_y, dest_width, dest_height, scale_mode,
+                        overall_alpha);
+}
+
+static void __colorkey_image(SpiceCanvas *spice_canvas,
+                             pixman_region32_t *region,
+                             pixman_image_t *src_image,
+                             int offset_x, int offset_y,
+                             uint32_t transparent_color)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_box32_t *rects;
@@ -533,14 +736,34 @@ static void colorkey_image(SpiceCanvas *spice_canvas,
     }
 }
 
-static void colorkey_scale_image(SpiceCanvas *spice_canvas,
-                                 pixman_region32_t *region,
-                                 pixman_image_t *src,
-                                 int src_x, int src_y,
-                                 int src_width, int src_height,
-                                 int dest_x, int dest_y,
-                                 int dest_width, int dest_height,
-                                 uint32_t transparent_color)
+static void colorkey_image(SpiceCanvas *spice_canvas,
+                           pixman_region32_t *region,
+                           pixman_image_t *src_image,
+                           int offset_x, int offset_y,
+                           uint32_t transparent_color)
+{
+    __colorkey_image(spice_canvas, region, src_image, offset_x, offset_y, transparent_color);
+}
+
+static void colorkey_image_from_surface(SpiceCanvas *spice_canvas,
+                                        pixman_region32_t *region,
+                                        SpiceCanvas *surface_canvas,
+                                        int offset_x, int offset_y,
+                                        uint32_t transparent_color)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __colorkey_image(spice_canvas, region, cairo_surface_canvas->image, offset_x, offset_y,
+                     transparent_color);
+}
+
+static void __colorkey_scale_image(SpiceCanvas *spice_canvas,
+                                   pixman_region32_t *region,
+                                   pixman_image_t *src,
+                                   int src_x, int src_y,
+                                   int src_width, int src_height,
+                                   int dest_x, int dest_y,
+                                   int dest_width, int dest_height,
+                                   uint32_t transparent_color)
 {
     CairoCanvas *canvas = (CairoCanvas *)spice_canvas;
     pixman_transform_t transform;
@@ -600,6 +823,34 @@ static void colorkey_scale_image(SpiceCanvas *spice_canvas,
     pixman_image_unref(scaled);
 }
 
+static void colorkey_scale_image(SpiceCanvas *spice_canvas,
+                                 pixman_region32_t *region,
+                                 pixman_image_t *src,
+                                 int src_x, int src_y,
+                                 int src_width, int src_height,
+                                 int dest_x, int dest_y,
+                                 int dest_width, int dest_height,
+                                 uint32_t transparent_color)
+{
+    __colorkey_scale_image(spice_canvas, region, src, src_x, src_y, src_width, src_height, dest_x,
+                           dest_y, dest_width, dest_height, transparent_color);
+}
+
+static void colorkey_scale_image_from_surface(SpiceCanvas *spice_canvas,
+                                              pixman_region32_t *region,
+                                              SpiceCanvas *surface_canvas,
+                                              int src_x, int src_y,
+                                              int src_width, int src_height,
+                                              int dest_x, int dest_y,
+                                              int dest_width, int dest_height,
+                                              uint32_t transparent_color)
+{
+    CairoCanvas *cairo_surface_canvas = (CairoCanvas *)surface_canvas;
+    __colorkey_scale_image(spice_canvas, region, cairo_surface_canvas->image, src_x, src_y,
+                           src_width, src_height, dest_x, dest_y, dest_width, dest_height,
+                           transparent_color);
+}
+
 static void canvas_put_image(SpiceCanvas *spice_canvas,
 #ifdef WIN32
                              HDC dc,
@@ -803,6 +1054,7 @@ SpiceCanvas *canvas_create(pixman_image_t *image, int bits
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                            , SpiceImageCache *bits_cache
 #endif
+                           , SpiceImageSurfaces *surfaces
                            , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , SpiceVirtMapping *virt_mapping
@@ -826,6 +1078,7 @@ SpiceCanvas *canvas_create(pixman_image_t *image, int bits
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                                , bits_cache
 #endif
+                               , surfaces
                                , glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                                , virt_mapping
@@ -858,15 +1111,26 @@ void cairo_canvas_init() //unsafe global function
     cairo_canvas_ops.fill_solid_rects = fill_solid_rects;
     cairo_canvas_ops.fill_solid_rects_rop = fill_solid_rects_rop;
     cairo_canvas_ops.fill_tiled_rects = fill_tiled_rects;
+    cairo_canvas_ops.fill_tiled_rects_from_surface = fill_tiled_rects_from_surface;
     cairo_canvas_ops.fill_tiled_rects_rop = fill_tiled_rects_rop;
+    cairo_canvas_ops.fill_tiled_rects_rop_from_surface = fill_tiled_rects_rop_from_surface;
     cairo_canvas_ops.blit_image = blit_image;
+    cairo_canvas_ops.blit_image_from_surface = blit_image_from_surface;
     cairo_canvas_ops.blit_image_rop = blit_image_rop;
+    cairo_canvas_ops.blit_image_rop_from_surface = blit_image_rop_from_surface;
     cairo_canvas_ops.scale_image = scale_image;
+    cairo_canvas_ops.scale_image_from_surface = scale_image_from_surface;
     cairo_canvas_ops.scale_image_rop = scale_image_rop;
+    cairo_canvas_ops.scale_image_rop_from_surface = scale_image_rop_from_surface;
     cairo_canvas_ops.blend_image = blend_image;
+    cairo_canvas_ops.blend_image_from_surface = blend_image_from_surface;
     cairo_canvas_ops.blend_scale_image = blend_scale_image;
+    cairo_canvas_ops.blend_scale_image_from_surface = blend_scale_image_from_surface;
     cairo_canvas_ops.colorkey_image = colorkey_image;
+    cairo_canvas_ops.colorkey_image_from_surface = colorkey_image_from_surface;
     cairo_canvas_ops.colorkey_scale_image = colorkey_scale_image;
+    cairo_canvas_ops.colorkey_scale_image_from_surface = colorkey_scale_image_from_surface;
     cairo_canvas_ops.copy_region = copy_region;
+    cairo_canvas_ops.get_image = get_image;
     rop3_init();
 }
diff --git a/common/cairo_canvas.h b/common/cairo_canvas.h
index 3f6fbbc..e65b407 100644
--- a/common/cairo_canvas.h
+++ b/common/cairo_canvas.h
@@ -33,6 +33,7 @@ SpiceCanvas *canvas_create(pixman_image_t *image, int bits
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                            , SpiceImageCache *bits_cache
 #endif
+			   , SpiceImageSurfaces *surfaces
                            , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                            , SpiceVirtMapping *virt_mapping
diff --git a/common/canvas_base.c b/common/canvas_base.c
index e13a005..e15a971 100644
--- a/common/canvas_base.c
+++ b/common/canvas_base.c
@@ -190,6 +190,8 @@ typedef struct CanvasBase {
     HDC dc;
 #endif
 
+    SpiceImageSurfaces *surfaces;
+
     LzData lz_data;
     GlzData glz_data;
 
@@ -948,6 +950,34 @@ static void dump_surface(pixman_image_t *surface, int cache)
 
 #endif
 
+static SpiceCanvas *canvas_get_surface_internal(CanvasBase *canvas, SPICE_ADDRESS addr)
+{
+    SpiceImageDescriptor *descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
+    access_test(canvas, descriptor, sizeof(SpiceImageDescriptor));
+
+    if (descriptor->type == SPICE_IMAGE_TYPE_SURFACE) {
+        SpiceSurfaceImage *surface = (SpiceSurfaceImage *)descriptor;
+        access_test(canvas, descriptor, sizeof(SpiceSurfaceImage));
+        return canvas->surfaces->ops->get(canvas->surfaces, surface->surface.surface_id);
+    }
+    return NULL;
+}
+
+static SpiceCanvas *canvas_get_surface_mask_internal(CanvasBase *canvas, SPICE_ADDRESS addr)
+{
+    SpiceImageDescriptor *descriptor;
+ 
+    descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(addr);
+    access_test(canvas, descriptor, sizeof(SpiceImageDescriptor));
+
+    if (descriptor->type == SPICE_IMAGE_TYPE_SURFACE) {
+        SpiceSurfaceImage *surface = (SpiceSurfaceImage *)descriptor;
+        access_test(canvas, descriptor, sizeof(SpiceSurfaceImage));
+        return canvas->surfaces->ops->get(canvas->surfaces, surface->surface.surface_id);
+    }
+    return NULL;
+}
+
 #if defined(CAIRO_CANVAS_CACHE) || defined(CAIRO_CANVAS_IMAGE_CACHE)
 
 //#define DEBUG_LZ
@@ -1065,6 +1095,16 @@ static pixman_image_t *canvas_get_image_internal(CanvasBase *canvas, SPICE_ADDRE
 
 #endif
 
+static SpiceCanvas *canvas_get_surface_mask(CanvasBase *canvas, SPICE_ADDRESS addr)
+{
+    return canvas_get_surface_mask_internal(canvas, addr);
+}
+
+static SpiceCanvas *canvas_get_surface(CanvasBase *canvas, SPICE_ADDRESS addr)
+{
+    return canvas_get_surface_internal(canvas, addr);
+}
+
 static pixman_image_t *canvas_get_image(CanvasBase *canvas, SPICE_ADDRESS addr)
 {
     return canvas_get_image_internal(canvas, addr, TRUE);
@@ -1318,10 +1358,6 @@ static pixman_image_t *canvas_get_mask(CanvasBase *canvas, SpiceQMask *mask, int
         *needs_invert_out = 0;
     }
 
-    if (!mask->bitmap) {
-        return NULL;
-    }
-
     descriptor = (SpiceImageDescriptor *)SPICE_GET_ADDRESS(mask->bitmap);
     access_test(canvas, descriptor, sizeof(SpiceImageDescriptor));
     need_invers = mask->flags & SPICE_MASK_FLAGS_INVERS;
@@ -1807,6 +1843,7 @@ static void canvas_mask_pixman(CanvasBase *canvas,
                                pixman_region32_t *dest_region,
                                SpiceQMask *mask, int x, int y)
 {
+    SpiceCanvas *surface_canvas;
     pixman_image_t *image, *subimage;
     int needs_invert;
     pixman_region32_t mask_region;
@@ -1815,13 +1852,19 @@ static void canvas_mask_pixman(CanvasBase *canvas,
     int mask_width, mask_height, mask_stride;
     pixman_box32_t extents;
 
-    needs_invert = FALSE;
-    image = canvas_get_mask(canvas,
-                            mask,
-                            &needs_invert);
+    if (!mask->bitmap) {
+        return;
+    }
 
-    if (image == NULL) {
-        return; /* no mask */
+    surface_canvas = canvas_get_surface_mask(canvas, mask->bitmap);
+    if (surface_canvas) {
+        needs_invert = mask->flags & SPICE_MASK_FLAGS_INVERS;
+        image = surface_canvas->ops->get_image(surface_canvas);
+    } else {
+        needs_invert = FALSE;
+        image = canvas_get_mask(canvas,
+                                mask,
+                                &needs_invert);
     }
 
     mask_data = pixman_image_get_data(image);
@@ -1921,20 +1964,35 @@ static void draw_brush(SpiceCanvas *canvas,
             canvas->ops->fill_solid_rects_rop(canvas, rects, n_rects, color, rop);
         }
         break;
-    case SPICE_BRUSH_TYPE_PATTERN:
+        case SPICE_BRUSH_TYPE_PATTERN: {
+        SpiceCanvas *surface_canvas;
+
         pattern = &brush->u.pattern;
-        tile = canvas_get_image(canvas_base, pattern->pat);
         offset_x = pattern->pos.x;
         offset_y = pattern->pos.y;
 
-        if (rop == SPICE_ROP_COPY) {
-            canvas->ops->fill_tiled_rects(canvas, rects, n_rects, tile, offset_x, offset_y);
+        surface_canvas = canvas_get_surface(canvas_base, pattern->pat);
+        if (surface_canvas) {
+            if (rop == SPICE_ROP_COPY) {
+                canvas->ops->fill_tiled_rects_from_surface(canvas, rects, n_rects, surface_canvas,
+                                                           offset_x, offset_y);
+            } else {
+                canvas->ops->fill_tiled_rects_rop_from_surface(canvas, rects, n_rects,
+                                                               surface_canvas, offset_x, offset_y,
+                                                               rop);
+            }
         } else {
-            canvas->ops->fill_tiled_rects_rop(canvas, rects, n_rects,
-                                              tile, offset_x, offset_y, rop);
+            tile = canvas_get_image(canvas_base, pattern->pat);
+            if (rop == SPICE_ROP_COPY) {
+                canvas->ops->fill_tiled_rects(canvas, rects, n_rects, tile, offset_x, offset_y);
+            } else {
+                canvas->ops->fill_tiled_rects_rop(canvas, rects, n_rects,
+                                                  tile, offset_x, offset_y, rop);
+            }
+            pixman_image_unref(tile);
         }
-        pixman_image_unref(tile);
         break;
+    }
     case SPICE_BRUSH_TYPE_NONE:
         /* Still need to do *something* here, because rop could be e.g invert dest */
         canvas->ops->fill_solid_rects_rop(canvas, rects, n_rects, 0, rop);
@@ -1991,6 +2049,7 @@ static void canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceCl
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
     pixman_region32_t dest_region;
+    SpiceCanvas *surface_canvas;
     pixman_image_t *src_image;
     SpiceROP rop;
 
@@ -2013,36 +2072,67 @@ static void canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceCl
         return;
     }
 
-    src_image = canvas_get_image(canvas, copy->src_bitmap);
-
-    if (rect_is_same_size(bbox, &copy->src_area)) {
-        if (rop == SPICE_ROP_COPY) {
-            spice_canvas->ops->blit_image(spice_canvas, &dest_region,
-                                          src_image,
-                                          bbox->left - copy->src_area.left,
-                                          bbox->top - copy->src_area.top);
+    surface_canvas = canvas_get_surface(canvas, copy->src_bitmap);
+    if (surface_canvas) {
+        if (rect_is_same_size(bbox, &copy->src_area)) {
+            if (rop == SPICE_ROP_COPY) {
+                spice_canvas->ops->blit_image_from_surface(spice_canvas, &dest_region,
+                                                           surface_canvas,
+                                                           bbox->left - copy->src_area.left,
+                                                           bbox->top - copy->src_area.top);
+            } else {
+                spice_canvas->ops->blit_image_rop_from_surface(spice_canvas, &dest_region,
+                                                               surface_canvas,
+                                                               bbox->left - copy->src_area.left,
+                                                               bbox->top - copy->src_area.top,
+                                                               rop);
+            }
         } else {
-            spice_canvas->ops->blit_image_rop(spice_canvas, &dest_region,
-                                              src_image,
-                                              bbox->left - copy->src_area.left,
-                                              bbox->top - copy->src_area.top,
-                                              rop);
+            if (rop == SPICE_ROP_COPY) {
+                spice_canvas->ops->scale_image_from_surface(spice_canvas, &dest_region,
+                                                            surface_canvas,
+                                                            copy->src_area.left,
+                                                            copy->src_area.top,
+                                                            copy->src_area.right - copy->src_area.left,
+                                                            copy->src_area.bottom - copy->src_area.top,
+                                                            bbox->left,
+                                                            bbox->top,
+                                                            bbox->right - bbox->left,
+                                                            bbox->bottom - bbox->top,
+                                                            copy->scale_mode);
+            } else {
+                spice_canvas->ops->scale_image_rop_from_surface(spice_canvas, &dest_region,
+                                                                surface_canvas,
+                                                                copy->src_area.left,
+                                                                copy->src_area.top,
+                                                                copy->src_area.right - copy->src_area.left,
+                                                                copy->src_area.bottom - copy->src_area.top,
+                                                                bbox->left,
+                                                                bbox->top,
+                                                                bbox->right - bbox->left,
+                                                                bbox->bottom - bbox->top,
+                                                                copy->scale_mode,
+                                                                rop);
+            }
         }
     } else {
-        if (rop == SPICE_ROP_COPY) {
-            spice_canvas->ops->scale_image(spice_canvas, &dest_region,
-                                           src_image,
-                                           copy->src_area.left,
-                                           copy->src_area.top,
-                                           copy->src_area.right - copy->src_area.left,
-                                           copy->src_area.bottom - copy->src_area.top,
-                                           bbox->left,
-                                           bbox->top,
-                                           bbox->right - bbox->left,
-                                           bbox->bottom - bbox->top,
-                                           copy->scale_mode);
+        src_image = canvas_get_image(canvas, copy->src_bitmap);
+        if (rect_is_same_size(bbox, &copy->src_area)) {
+            if (rop == SPICE_ROP_COPY) {
+                spice_canvas->ops->blit_image(spice_canvas, &dest_region,
+                                              src_image,
+                                              bbox->left - copy->src_area.left,
+                                              bbox->top - copy->src_area.top);
+            } else {
+                spice_canvas->ops->blit_image_rop(spice_canvas, &dest_region,
+                                                  src_image,
+                                                  bbox->left - copy->src_area.left,
+                                                  bbox->top - copy->src_area.top,
+                                                  rop);
+            }
         } else {
-            spice_canvas->ops->scale_image_rop(spice_canvas, &dest_region,
+            if (rop == SPICE_ROP_COPY) {
+                spice_canvas->ops->scale_image(spice_canvas, &dest_region,
                                                src_image,
                                                copy->src_area.left,
                                                copy->src_area.top,
@@ -2052,18 +2142,31 @@ static void canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceCl
                                                bbox->top,
                                                bbox->right - bbox->left,
                                                bbox->bottom - bbox->top,
-                                               copy->scale_mode,
-                                               rop);
+                                               copy->scale_mode);
+            } else {
+                spice_canvas->ops->scale_image_rop(spice_canvas, &dest_region,
+                                                   src_image,
+                                                   copy->src_area.left,
+                                                   copy->src_area.top,
+                                                   copy->src_area.right - copy->src_area.left,
+                                                   copy->src_area.bottom - copy->src_area.top,
+                                                   bbox->left,
+                                                   bbox->top,
+                                                   bbox->right - bbox->left,
+                                                   bbox->bottom - bbox->top,
+                                                   copy->scale_mode,
+                                                   rop);
+            }
         }
+        pixman_image_unref(src_image);
     }
-
-    pixman_image_unref(src_image);
     pixman_region32_fini(&dest_region);
 }
 
 static void canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent)
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    SpiceCanvas *surface_canvas;
     pixman_image_t *src_image;
     pixman_region32_t dest_region;
 
@@ -2080,29 +2183,50 @@ static void canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox,
         return;
     }
 
-    src_image = canvas_get_image(canvas, transparent->src_bitmap);
-
-    if (rect_is_same_size(bbox, &transparent->src_area)) {
-        spice_canvas->ops->colorkey_image(spice_canvas, &dest_region,
-                                          src_image,
-                                          bbox->left - transparent->src_area.left,
-                                          bbox->top - transparent->src_area.top,
-                                          transparent->true_color);
+    surface_canvas = canvas_get_surface(canvas, transparent->src_bitmap);
+    if (surface_canvas) {
+        if (rect_is_same_size(bbox, &transparent->src_area)) {
+            spice_canvas->ops->colorkey_image_from_surface(spice_canvas, &dest_region,
+                                                           surface_canvas,
+                                                           bbox->left - transparent->src_area.left,
+                                                           bbox->top - transparent->src_area.top,
+                                                           transparent->true_color);
+        } else {
+            spice_canvas->ops->colorkey_scale_image_from_surface(spice_canvas, &dest_region,
+                                                                 surface_canvas,
+                                                                 transparent->src_area.left,
+                                                                 transparent->src_area.top,
+                                                                 transparent->src_area.right - transparent->src_area.left,
+                                                                 transparent->src_area.bottom - transparent->src_area.top,
+                                                                 bbox->left,
+                                                                 bbox->top,
+                                                                 bbox->right - bbox->left,
+                                                                 bbox->bottom - bbox->top,
+                                                                 transparent->true_color);
+        }
     } else {
-        spice_canvas->ops->colorkey_scale_image(spice_canvas, &dest_region,
-                                                src_image,
-                                                transparent->src_area.left,
-                                                transparent->src_area.top,
-                                                transparent->src_area.right - transparent->src_area.left,
-                                                transparent->src_area.bottom - transparent->src_area.top,
-                                                bbox->left,
-                                                bbox->top,
-                                                bbox->right - bbox->left,
-                                                bbox->bottom - bbox->top,
-                                                transparent->true_color);
+        src_image = canvas_get_image(canvas, transparent->src_bitmap);
+        if (rect_is_same_size(bbox, &transparent->src_area)) {
+            spice_canvas->ops->colorkey_image(spice_canvas, &dest_region,
+                                              src_image,
+                                              bbox->left - transparent->src_area.left,
+                                              bbox->top - transparent->src_area.top,
+                                              transparent->true_color);
+        } else {
+            spice_canvas->ops->colorkey_scale_image(spice_canvas, &dest_region,
+                                                    src_image,
+                                                    transparent->src_area.left,
+                                                    transparent->src_area.top,
+                                                    transparent->src_area.right - transparent->src_area.left,
+                                                    transparent->src_area.bottom - transparent->src_area.top,
+                                                    bbox->left,
+                                                    bbox->top,
+                                                    bbox->right - bbox->left,
+                                                    bbox->bottom - bbox->top,
+                                                    transparent->true_color);
+        }
+        pixman_image_unref(src_image);
     }
-
-    pixman_image_unref(src_image);
     pixman_region32_fini(&dest_region);
 }
 
@@ -2110,6 +2234,7 @@ static void canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox,
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
     pixman_region32_t dest_region;
+    SpiceCanvas *surface_canvas;
     pixman_image_t *src_image;
 
     pixman_region32_init_rect(&dest_region,
@@ -2126,34 +2251,62 @@ static void canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox,
         return;
     }
 
-    src_image = canvas_get_image(canvas, alpha_blend->src_bitmap);
-
-    if (rect_is_same_size(bbox, &alpha_blend->src_area)) {
-        spice_canvas->ops->blend_image(spice_canvas, &dest_region,
-                                       src_image,
-                                       alpha_blend->src_area.left,
-                                       alpha_blend->src_area.top,
-                                       bbox->left,
-                                       bbox->top,
-                                       bbox->right - bbox->left,
-                                       bbox->bottom - bbox->top,
-                                       alpha_blend->alpha);
-    } else {
-        spice_canvas->ops->blend_scale_image(spice_canvas, &dest_region,
-                                             src_image,
-                                             alpha_blend->src_area.left,
-                                             alpha_blend->src_area.top,
-                                             alpha_blend->src_area.right - alpha_blend->src_area.left,
-                                             alpha_blend->src_area.bottom - alpha_blend->src_area.top,
-                                             bbox->left,
-                                             bbox->top,
-                                             bbox->right - bbox->left,
-                                             bbox->bottom - bbox->top,
-                                             SPICE_IMAGE_SCALE_MODE_NEAREST,
-                                             alpha_blend->alpha);
+    surface_canvas = canvas_get_surface(canvas, alpha_blend->src_bitmap);
+    if (surface_canvas) {
+        if (rect_is_same_size(bbox, &alpha_blend->src_area)) {
+            spice_canvas->ops->blend_image_from_surface(spice_canvas, &dest_region,
+                                                        surface_canvas,
+                                                        alpha_blend->src_area.left,
+                                                        alpha_blend->src_area.top,
+                                                        bbox->left,
+                                                        bbox->top,
+                                                        bbox->right - bbox->left,
+                                                        bbox->bottom - bbox->top,
+                                                        alpha_blend->alpha);
+        } else {
+            spice_canvas->ops->blend_scale_image_from_surface(spice_canvas, &dest_region,
+                                                              surface_canvas,
+                                                              alpha_blend->src_area.left,
+                                                              alpha_blend->src_area.top,
+                                                              alpha_blend->src_area.right - alpha_blend->src_area.left,
+                                                              alpha_blend->src_area.bottom - alpha_blend->src_area.top,
+                                                              bbox->left,
+                                                              bbox->top,
+                                                              bbox->right - bbox->left,
+                                                              bbox->bottom - bbox->top,
+                                                              SPICE_IMAGE_SCALE_MODE_NEAREST,
+                                                              alpha_blend->alpha);
+        }
+     } else {
+        src_image = canvas_get_image(canvas, alpha_blend->src_bitmap);
+        if (rect_is_same_size(bbox, &alpha_blend->src_area)) {
+            spice_canvas->ops->blend_image(spice_canvas, &dest_region,
+                                           src_image,
+                                           alpha_blend->src_area.left,
+                                           alpha_blend->src_area.top,
+                                           bbox->left,
+                                           bbox->top,
+                                           bbox->right - bbox->left,
+                                           bbox->bottom - bbox->top,
+                                           alpha_blend->alpha);
+        } else {
+            spice_canvas->ops->blend_scale_image(spice_canvas, &dest_region,
+                                                 src_image,
+                                                 alpha_blend->src_area.left,
+                                                 alpha_blend->src_area.top,
+                                                 alpha_blend->src_area.right - alpha_blend->src_area.left,
+                                                 alpha_blend->src_area.bottom - alpha_blend->src_area.top,
+                                                 bbox->left,
+                                                 bbox->top,
+                                                 bbox->right - bbox->left,
+                                                 bbox->bottom - bbox->top,
+                                                 SPICE_IMAGE_SCALE_MODE_NEAREST,
+                                                 alpha_blend->alpha);
+        }
+ 
+        pixman_image_unref(src_image);
     }
 
-    pixman_image_unref(src_image);
     pixman_region32_fini(&dest_region);
 }
 
@@ -2214,6 +2367,7 @@ static void canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, Spice
 static void canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    SpiceCanvas *surface_canvas;
     pixman_image_t *src_image;
     pixman_region32_t dest_region;
     SpiceROP rop;
@@ -2237,40 +2391,74 @@ static void canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceC
         return;
     }
 
-    src_image = canvas_get_image(canvas, blend->src_bitmap);
-
-    if (rect_is_same_size(bbox, &blend->src_area)) {
-        if (rop == SPICE_ROP_COPY)
-            spice_canvas->ops->blit_image(spice_canvas, &dest_region,
-                                          src_image,
-                                          bbox->left - blend->src_area.left,
-                                          bbox->top - blend->src_area.top);
-        else
-            spice_canvas->ops->blit_image_rop(spice_canvas, &dest_region,
+    surface_canvas = canvas_get_surface(canvas, blend->src_bitmap);
+    if (surface_canvas) {
+        if (rect_is_same_size(bbox, &blend->src_area)) {
+            if (rop == SPICE_ROP_COPY)
+                spice_canvas->ops->blit_image_from_surface(spice_canvas, &dest_region,
+                                                           surface_canvas,
+                                                           bbox->left - blend->src_area.left,
+                                                           bbox->top - blend->src_area.top);
+            else
+                spice_canvas->ops->blit_image_rop_from_surface(spice_canvas, &dest_region,
+                                                               surface_canvas,
+                                                               bbox->left - blend->src_area.left,
+                                                               bbox->top - blend->src_area.top,
+                                                               rop);
+        } else {
+            double sx, sy;
+    
+            sx = (double)(blend->src_area.right - blend->src_area.left) / (bbox->right - bbox->left);
+            sy = (double)(blend->src_area.bottom - blend->src_area.top) / (bbox->bottom - bbox->top);
+    
+            if (rop == SPICE_ROP_COPY) {
+                spice_canvas->ops->scale_image_from_surface(spice_canvas, &dest_region,
+                                                            surface_canvas,
+                                                            blend->src_area.left,
+                                                            blend->src_area.top,
+                                                            blend->src_area.right - blend->src_area.left,
+                                                            blend->src_area.bottom - blend->src_area.top,
+                                                            bbox->left,
+                                                            bbox->top,
+                                                            bbox->right - bbox->left,
+                                                            bbox->bottom - bbox->top,
+                                                            blend->scale_mode);
+            } else {
+                spice_canvas->ops->scale_image_rop_from_surface(spice_canvas, &dest_region,
+                                                                surface_canvas,
+                                                                blend->src_area.left,
+                                                                blend->src_area.top,
+                                                                blend->src_area.right - blend->src_area.left,
+                                                                blend->src_area.bottom - blend->src_area.top,
+                                                                bbox->left,
+                                                                bbox->top,
+                                                                bbox->right - bbox->left,
+                                                                bbox->bottom - bbox->top,
+                                                                blend->scale_mode, rop);
+            }
+        }
+    } else {
+        src_image = canvas_get_image(canvas, blend->src_bitmap);
+        if (rect_is_same_size(bbox, &blend->src_area)) {
+            if (rop == SPICE_ROP_COPY)
+                spice_canvas->ops->blit_image(spice_canvas, &dest_region,
                                               src_image,
                                               bbox->left - blend->src_area.left,
-                                              bbox->top - blend->src_area.top,
-                                              rop);
-    } else {
-        double sx, sy;
-
-        sx = (double)(blend->src_area.right - blend->src_area.left) / (bbox->right - bbox->left);
-        sy = (double)(blend->src_area.bottom - blend->src_area.top) / (bbox->bottom - bbox->top);
-
-        if (rop == SPICE_ROP_COPY) {
-            spice_canvas->ops->scale_image(spice_canvas, &dest_region,
-                                           src_image,
-                                           blend->src_area.left,
-                                           blend->src_area.top,
-                                           blend->src_area.right - blend->src_area.left,
-                                           blend->src_area.bottom - blend->src_area.top,
-                                           bbox->left,
-                                           bbox->top,
-                                           bbox->right - bbox->left,
-                                           bbox->bottom - bbox->top,
-                                           blend->scale_mode);
+                                              bbox->top - blend->src_area.top);
+            else
+                spice_canvas->ops->blit_image_rop(spice_canvas, &dest_region,
+                                                  src_image,
+                                                  bbox->left - blend->src_area.left,
+                                                  bbox->top - blend->src_area.top,
+                                                  rop);
         } else {
-            spice_canvas->ops->scale_image_rop(spice_canvas, &dest_region,
+            double sx, sy;
+    
+            sx = (double)(blend->src_area.right - blend->src_area.left) / (bbox->right - bbox->left);
+            sy = (double)(blend->src_area.bottom - blend->src_area.top) / (bbox->bottom - bbox->top);
+    
+            if (rop == SPICE_ROP_COPY) {
+                spice_canvas->ops->scale_image(spice_canvas, &dest_region,
                                                src_image,
                                                blend->src_area.left,
                                                blend->src_area.top,
@@ -2280,11 +2468,23 @@ static void canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceC
                                                bbox->top,
                                                bbox->right - bbox->left,
                                                bbox->bottom - bbox->top,
-                                               blend->scale_mode, rop);
+                                               blend->scale_mode);
+            } else {
+                spice_canvas->ops->scale_image_rop(spice_canvas, &dest_region,
+                                                   src_image,
+                                                   blend->src_area.left,
+                                                   blend->src_area.top,
+                                                   blend->src_area.right - blend->src_area.left,
+                                                   blend->src_area.bottom - blend->src_area.top,
+                                                   bbox->left,
+                                                   bbox->top,
+                                                   bbox->right - bbox->left,
+                                                   bbox->bottom - bbox->top,
+                                                   blend->scale_mode, rop);
+            }
         }
+        pixman_image_unref(src_image);
     }
-
-    pixman_image_unref(src_image);
     pixman_region32_fini(&dest_region);
 }
 
@@ -2382,7 +2582,11 @@ typedef struct {
     SpiceROP back_rop;
     int solid;
     uint32_t color;
-    pixman_image_t *tile;
+    int use_surface_canvas;
+    union {
+        SpiceCanvas *surface_canvas;
+        pixman_image_t *tile;
+    };
     int tile_offset_x;
     int tile_offset_y;
 } StrokeGC;
@@ -2505,16 +2709,31 @@ static void stroke_fill_rects(lineGC * pGC,
         }
     } else {
         if (rop == SPICE_ROP_COPY) {
-            canvas->ops->fill_tiled_rects(canvas, area_rects, n_area_rects,
-                                          strokeGC->tile,
-                                          strokeGC->tile_offset_x,
-                                          strokeGC->tile_offset_y);
-        } else {
-            canvas->ops->fill_tiled_rects_rop(canvas, area_rects, n_area_rects,
+            if (strokeGC->use_surface_canvas) {
+                canvas->ops->fill_tiled_rects_from_surface(canvas, area_rects, n_area_rects,
+                                                           strokeGC->surface_canvas,
+                                                           strokeGC->tile_offset_x,
+                                                           strokeGC->tile_offset_y);
+            } else {
+                canvas->ops->fill_tiled_rects(canvas, area_rects, n_area_rects,
                                               strokeGC->tile,
                                               strokeGC->tile_offset_x,
-                                              strokeGC->tile_offset_y,
-                                              rop);
+                                              strokeGC->tile_offset_y);
+            }
+        } else {
+            if (strokeGC->use_surface_canvas) {
+                canvas->ops->fill_tiled_rects_rop_from_surface(canvas, area_rects, n_area_rects,
+                                                               strokeGC->surface_canvas,
+                                                               strokeGC->tile_offset_x,
+                                                               strokeGC->tile_offset_y,
+                                                               rop);
+            } else {
+                canvas->ops->fill_tiled_rects_rop(canvas, area_rects, n_area_rects,
+                                                  strokeGC->tile,
+                                                  strokeGC->tile_offset_x,
+                                                  strokeGC->tile_offset_y,
+                                                  rop);
+            }
         }
     }
 
@@ -2661,6 +2880,7 @@ static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox,
                                SpiceClip *clip, SpiceStroke *stroke)
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    SpiceCanvas *surface_canvas = NULL;
     StrokeGC gc = { { 0 } };
     lineGCOps ops = {
         stroke_fill_spans,
@@ -2754,8 +2974,16 @@ static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox,
         break;
     case SPICE_BRUSH_TYPE_PATTERN:
         gc.solid = FALSE;
-        gc.tile = canvas_get_image(canvas,
-                                   stroke->brush.u.pattern.pat);
+        surface_canvas = canvas_get_surface(canvas,
+                                            stroke->brush.u.pattern.pat);
+        if (surface_canvas) {
+            gc.use_surface_canvas = TRUE;
+            gc.surface_canvas = surface_canvas;
+        } else {
+            gc.use_surface_canvas = FALSE;
+            gc.tile = canvas_get_image(canvas,
+                                       stroke->brush.u.pattern.pat);
+        }
         gc.tile_offset_x = stroke->brush.u.pattern.pos.x;
         gc.tile_offset_y = stroke->brush.u.pattern.pos.y;
         break;
@@ -2817,17 +3045,20 @@ static void canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox,
     }
     stroke_lines_free(&lines);
 
-    if (!gc.solid && gc.tile) {
+    if (!gc.solid && gc.tile && !surface_canvas) {
         pixman_image_unref(gc.tile);
     }
 
     pixman_region32_fini(&gc.dest_region);
 }
 
+
+//need surfaces handling here !!!
 static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox,
                              SpiceClip *clip, SpiceRop3 *rop3)
 {
     CanvasBase *canvas = (CanvasBase *)spice_canvas;
+    SpiceCanvas *surface_canvas;
     pixman_region32_t dest_region;
     pixman_image_t *d;
     pixman_image_t *s;
@@ -2848,7 +3079,12 @@ static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox,
     heigth = bbox->bottom - bbox->top;
 
     d = canvas_get_image_from_self(spice_canvas, bbox->left, bbox->top, width, heigth);
-    s = canvas_get_image(canvas, rop3->src_bitmap);
+    surface_canvas = canvas_get_surface(canvas, rop3->src_bitmap);
+    if (surface_canvas) {
+        s = surface_canvas->ops->get_image(surface_canvas);
+    } else {
+        s = canvas_get_image(canvas, rop3->src_bitmap);
+    }
 
     if (!rect_is_same_size(bbox, &rop3->src_area)) {
         pixman_image_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, width, heigth,
@@ -2866,7 +3102,15 @@ static void canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox,
         CANVAS_ERROR("bad src bitmap size");
     }
     if (rop3->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
-        pixman_image_t *p = canvas_get_image(canvas, rop3->brush.u.pattern.pat);
+        SpiceCanvas *_surface_canvas;
+        pixman_image_t *p;
+
+        _surface_canvas = canvas_get_surface(canvas, rop3->brush.u.pattern.pat);
+        if (_surface_canvas) {
+            p = _surface_canvas->ops->get_image(_surface_canvas);
+        } else {
+            p = canvas_get_image(canvas, rop3->brush.u.pattern.pat);
+        }
         SpicePoint pat_pos;
 
         pat_pos.x = (bbox->left - rop3->brush.u.pattern.pos.x) % pixman_image_get_width(p);
@@ -2988,6 +3232,7 @@ static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops,
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                             , SpiceImageCache *bits_cache
 #endif
+                            , SpiceImageSurfaces *surfaces
                             , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                             , SpiceVirtMapping *virt_mapping
@@ -3020,6 +3265,7 @@ static int canvas_base_init(CanvasBase *canvas, SpiceCanvasOps *ops,
             return 0;
     }
 #endif
+    canvas->surfaces = surfaces;
     canvas->glz_data.decoder = glz_decoder;
 
     if (depth == 16) {
diff --git a/common/canvas_base.h b/common/canvas_base.h
index bed1e1b..0a663bd 100644
--- a/common/canvas_base.h
+++ b/common/canvas_base.h
@@ -28,6 +28,7 @@
 typedef void (*spice_destroy_fn_t)(void *data);
 
 typedef struct _SpiceImageCache SpiceImageCache;
+typedef struct _SpiceImageSurfaces SpiceImageSurfaces;
 typedef struct _SpicePaletteCache SpicePaletteCache;
 typedef struct _SpiceGlzDecoder SpiceGlzDecoder;
 typedef struct _SpiceVirtMapping SpiceVirtMapping;
@@ -46,6 +47,15 @@ struct _SpiceImageCache {
 };
 
 typedef struct {
+ SpiceCanvas *(*get)(SpiceImageSurfaces *surfaces,
+	             uint32_t surface_id);
+} SpiceImageSurfacesOps;
+
+struct _SpiceImageSurfaces {
+ SpiceImageSurfacesOps *ops;
+};
+
+typedef struct {
     void (*put)(SpicePaletteCache *cache,
                 SpicePalette *palette);
     SpicePalette *(*get)(SpicePaletteCache *cache,
@@ -127,21 +137,41 @@ typedef struct {
                              int n_rects,
                              pixman_image_t *tile,
                              int offset_x, int offset_y);
+    void (*fill_tiled_rects_from_surface)(SpiceCanvas *canvas,
+					  pixman_box32_t *rects,
+					  int n_rects,
+					  SpiceCanvas *tile,
+					  int offset_x, int offset_y);
     void (*fill_tiled_rects_rop)(SpiceCanvas *canvas,
                                  pixman_box32_t *rects,
                                  int n_rects,
                                  pixman_image_t *tile,
                                  int offset_x, int offset_y,
                                  SpiceROP rop);
+    void (*fill_tiled_rects_rop_from_surface)(SpiceCanvas *canvas,
+					      pixman_box32_t *rects,
+					      int n_rects,
+					      SpiceCanvas *tile,
+					      int offset_x, int offset_y,
+					      SpiceROP rop);
     void (*blit_image)(SpiceCanvas *canvas,
                        pixman_region32_t *region,
                        pixman_image_t *src_image,
                        int offset_x, int offset_y);
+    void (*blit_image_from_surface)(SpiceCanvas *canvas,
+				    pixman_region32_t *region,
+				    SpiceCanvas *src_image,
+				    int offset_x, int offset_y);
     void (*blit_image_rop)(SpiceCanvas *canvas,
                            pixman_region32_t *region,
                            pixman_image_t *src_image,
                            int offset_x, int offset_y,
                            SpiceROP rop);
+    void (*blit_image_rop_from_surface)(SpiceCanvas *canvas,
+					pixman_region32_t *region,
+					SpiceCanvas *src_image,
+					int offset_x, int offset_y,
+					SpiceROP rop);
     void (*scale_image)(SpiceCanvas *canvas,
                         pixman_region32_t *region,
                         pixman_image_t *src_image,
@@ -150,6 +180,14 @@ typedef struct {
                         int dest_x, int dest_y,
                         int dest_width, int dest_height,
                         int scale_mode);
+    void (*scale_image_from_surface)(SpiceCanvas *canvas,
+				     pixman_region32_t *region,
+				     SpiceCanvas *src_image,
+				     int src_x, int src_y,
+				     int src_width, int src_height,
+				     int dest_x, int dest_y,
+				     int dest_width, int dest_height,
+				     int scale_mode);
     void (*scale_image_rop)(SpiceCanvas *canvas,
                             pixman_region32_t *region,
                             pixman_image_t *src_image,
@@ -158,6 +196,14 @@ typedef struct {
                             int dest_x, int dest_y,
                             int dest_width, int dest_height,
                             int scale_mode, SpiceROP rop);
+    void (*scale_image_rop_from_surface)(SpiceCanvas *canvas,
+					 pixman_region32_t *region,
+					 SpiceCanvas *src_image,
+					 int src_x, int src_y,
+					 int src_width, int src_height,
+					 int dest_x, int dest_y,
+					 int dest_width, int dest_height,
+					 int scale_mode, SpiceROP rop);
     void (*blend_image)(SpiceCanvas *canvas,
                         pixman_region32_t *region,
                         pixman_image_t *src_image,
@@ -165,6 +211,13 @@ typedef struct {
                         int dest_x, int dest_y,
                         int width, int height,
                         int overall_alpha);
+    void (*blend_image_from_surface)(SpiceCanvas *canvas,
+				     pixman_region32_t *region,
+				     SpiceCanvas *src_image,
+				     int src_x, int src_y,
+				     int dest_x, int dest_y,
+				     int width, int height,
+				     int overall_alpha);
     void (*blend_scale_image)(SpiceCanvas *canvas,
                               pixman_region32_t *region,
                               pixman_image_t *src_image,
@@ -174,11 +227,25 @@ typedef struct {
                               int dest_width, int dest_height,
                               int scale_mode,
                               int overall_alpha);
+    void (*blend_scale_image_from_surface)(SpiceCanvas *canvas,
+					   pixman_region32_t *region,
+					   SpiceCanvas *src_image,
+					   int src_x, int src_y,
+					   int src_width, int src_height,
+					   int dest_x, int dest_y,
+					   int dest_width, int dest_height,
+					   int scale_mode,
+					   int overall_alpha);
     void (*colorkey_image)(SpiceCanvas *canvas,
                            pixman_region32_t *region,
                            pixman_image_t *src_image,
                            int offset_x, int offset_y,
                            uint32_t transparent_color);
+    void (*colorkey_image_from_surface)(SpiceCanvas *canvas,
+					pixman_region32_t *region,
+					SpiceCanvas *src_image,
+					int offset_x, int offset_y,
+					uint32_t transparent_color);
     void (*colorkey_scale_image)(SpiceCanvas *canvas,
                                  pixman_region32_t *region,
                                  pixman_image_t *src_image,
@@ -187,9 +254,18 @@ typedef struct {
                                  int dest_x, int dest_y,
                                  int dest_width, int dest_height,
                                  uint32_t transparent_color);
+    void (*colorkey_scale_image_from_surface)(SpiceCanvas *canvas,
+					      pixman_region32_t *region,
+					      SpiceCanvas *src_image,
+					      int src_x, int src_y,
+					      int src_width, int src_height,
+					      int dest_x, int dest_y,
+					      int dest_width, int dest_height,
+					      uint32_t transparent_color);
     void (*copy_region)(SpiceCanvas *canvas,
                         pixman_region32_t *dest_region,
                         int dx, int dy);
+    pixman_image_t *(*get_image)(SpiceCanvas *canvas);
 } SpiceCanvasOps;
 
 void spice_canvas_set_usr_data(SpiceCanvas *canvas, void *data, spice_destroy_fn_t destroy_fn);
diff --git a/common/gdi_canvas.c b/common/gdi_canvas.c
index 5eb31fe..75dff69 100644
--- a/common/gdi_canvas.c
+++ b/common/gdi_canvas.c
@@ -42,6 +42,7 @@ struct BitmapData {
     uint8_t flags;
     HDC dc;
     int cache;
+    int from_surface;
 };
 
 #define _rop3_brush 0xf0
@@ -644,7 +645,8 @@ static HBRUSH get_brush(GdiCanvas *canvas, SpiceBrush *brush)
             CANVAS_ERROR("CreateSolidBrush failed");
         }
         return hbrush;
-    case SPICE_BRUSH_TYPE_PATTERN: {
+    case SPICE_BRUSH_TYPE_PATTERN: { 
+        GdiCanvas *gdi_surface = NULL;
         GdiImage image;
         HBRUSH hbrush;
         pixman_image_t *surface;
@@ -652,21 +654,31 @@ static HBRUSH get_brush(GdiCanvas *canvas, SpiceBrush *brush)
         HBITMAP bitmap;
         HBITMAP prev_bitmap;
 
-        surface = canvas_get_image(&canvas->base, brush->u.pattern.pat);
-        surface_to_image(surface, &image);
-
-        if (!create_bitmap(&bitmap, &prev_bitmap, &dc, image.pixels, image.width,
-                           image.height, image.stride, 32, 0)) {
-            CANVAS_ERROR("create_bitmap failed");
-            return NULL;
+        gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, brush->u.pattern.pat);
+        if (gdi_surface) {
+            bitmap = (HBITMAP)GetCurrentObject(gdi_surface->dc, OBJ_BITMAP);
+            if (!bitmap) {
+                CANVAS_ERROR("GetCurrentObject failed");
+            }
+        } else {
+            surface = canvas_get_image(&canvas->base, brush->u.pattern.pat);
+            surface_to_image(surface, &image);
+    
+            if (!create_bitmap(&bitmap, &prev_bitmap, &dc, image.pixels, image.width,
+                               image.height, image.stride, 32, 0)) {
+                CANVAS_ERROR("create_bitmap failed");
+                return NULL;
+            }
         }
 
         if (!(hbrush = CreatePatternBrush(bitmap))) {
             CANVAS_ERROR("CreatePatternBrush failed");
         }
 
-        release_bitmap(dc, bitmap, prev_bitmap, 0);
-        pixman_image_unref(surface);
+        if (!gdi_surface) {
+            release_bitmap(dc, bitmap, prev_bitmap, 0);
+            pixman_image_unref(surface);
+        }
         return hbrush;
     }
     case SPICE_BRUSH_TYPE_NONE:
@@ -781,30 +793,53 @@ uint8_t calc_rop3_src_brush(uint16_t rop3_bits)
 
 static struct BitmapData get_mask_bitmap(struct GdiCanvas *canvas, struct SpiceQMask *mask)
 {
+    GdiCanvas *gdi_surface;
     pixman_image_t *surface;
     struct BitmapData bitmap;
     PixmanData *pixman_data;
 
     bitmap.hbitmap = NULL;
-    if (!(surface = canvas_get_mask(&canvas->base, mask, NULL))) {
+    if (!mask->bitmap) {
         return bitmap;
     }
 
-    pixman_data = (PixmanData *)pixman_image_get_destroy_data (surface);
-    if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
-        bitmap.dc = create_compatible_dc();
-        bitmap.prev_hbitmap = (HBITMAP)SelectObject(bitmap.dc, pixman_data->bitmap);
-        bitmap.hbitmap = pixman_data->bitmap;
-        ReleaseMutex(pixman_data->mutex);
-        bitmap.cache = 1;
-    } else if (!create_bitmap(&bitmap.hbitmap, &bitmap.prev_hbitmap, &bitmap.dc,
-                              (uint8_t *)pixman_image_get_data(surface),
-                              pixman_image_get_width(surface),
-                              pixman_image_get_height(surface),
-                              pixman_image_get_stride(surface), 1, 0)) {
-        bitmap.hbitmap = NULL;
+    gdi_surface = (GdiCanvas *)canvas_get_surface_mask(&canvas->base, mask->bitmap);
+    if (gdi_surface) {
+        	HBITMAP _bitmap;
+
+            _bitmap = (HBITMAP)GetCurrentObject(gdi_surface->dc, OBJ_BITMAP);
+            if (!_bitmap) {
+                CANVAS_ERROR ("GetCurrentObject failed");
+            }
+            bitmap.dc = gdi_surface->dc;
+            bitmap.hbitmap = _bitmap;
+            bitmap.prev_hbitmap = (HBITMAP)0;
+            bitmap.cache = 0;
+            bitmap.from_surface = 1;
     } else {
-        bitmap.cache = 0;
+    
+        if (!(surface = canvas_get_mask(&canvas->base, mask, NULL))) {
+            return bitmap;
+        }
+    
+        pixman_data = (PixmanData *)pixman_image_get_destroy_data (surface);
+        if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
+            bitmap.dc = create_compatible_dc();
+            bitmap.prev_hbitmap = (HBITMAP)SelectObject(bitmap.dc, pixman_data->bitmap);
+            bitmap.hbitmap = pixman_data->bitmap;
+            ReleaseMutex(pixman_data->mutex);
+            bitmap.cache = 1;
+        } else if (!create_bitmap(&bitmap.hbitmap, &bitmap.prev_hbitmap, &bitmap.dc,
+                                  (uint8_t *)pixman_image_get_data(surface),
+                                  pixman_image_get_width(surface),
+                                  pixman_image_get_height(surface),
+                                  pixman_image_get_stride(surface), 1, 0)) {
+            bitmap.hbitmap = NULL;
+        } else {
+            bitmap.cache = 0;
+        }
+
+        bitmap.from_surface = 0;
     }
 
     bitmap.flags = mask->flags;
@@ -859,7 +894,9 @@ static void gdi_draw_bitmap_redrop(HDC dest_dc, const SpiceRect *src, const Spic
 static void free_mask(struct BitmapData *bitmap)
 {
     if (bitmap->hbitmap) {
-        release_bitmap(bitmap->dc, bitmap->hbitmap, bitmap->prev_hbitmap, bitmap->cache);
+        if (!bitmap->from_surface) {
+            release_bitmap(bitmap->dc, bitmap->hbitmap, bitmap->prev_hbitmap, bitmap->cache);
+        }
     }
 }
 
@@ -985,13 +1022,15 @@ static void gdi_canvas_draw_fill(SpiceCanvas *spice_canvas, SpiceRect *bbox, Spi
     HBRUSH brush;
     struct BitmapData bitmapmask;
 
+    Lock lock(*canvas->lock);
+
     if (!(brush = get_brush(canvas, &fill->brush))) {
         CANVAS_ERROR("no braash");
         return;
     }
+
     bitmapmask = get_mask_bitmap(canvas, &fill->mask);
 
-    Lock lock(*canvas->lock);
     set_clip(canvas, clip);
     prev_hbrush = set_brush(canvas->dc, brush, &fill->brush);
     gdi_draw_bitmap_redrop(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask,
@@ -1004,40 +1043,51 @@ static void gdi_canvas_draw_fill(SpiceCanvas *spice_canvas, SpiceRect *bbox, Spi
 static void gdi_canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
 {
     GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+    GdiCanvas *gdi_surface;
     pixman_image_t *surface;
     GdiImage image;
     struct BitmapData bitmapmask;
     PixmanData *pixman_data;
 
-    bitmapmask = get_mask_bitmap(canvas, &copy->mask);
-    surface = canvas_get_image(&canvas->base, copy->src_bitmap);
-    pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
-
-    Lock lock(*canvas->lock);
-    set_scale_mode(canvas, copy->scale_mode);
-    set_clip(canvas, clip);
-
-    if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
-        HDC dc;
-        HBITMAP prev_bitmap;
-
-        dc = create_compatible_dc();
-        prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
-        gdi_draw_bitmap_redrop(canvas->dc, &copy->src_area, bbox, dc,
+    gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, copy->src_bitmap);
+    if (gdi_surface) {
+        Lock lock(*canvas->lock);
+        bitmapmask = get_mask_bitmap(canvas, &copy->mask);
+        set_scale_mode(canvas, copy->scale_mode);
+        set_clip(canvas, clip);
+        gdi_draw_bitmap_redrop(canvas->dc, &copy->src_area, bbox, gdi_surface->dc,
                                &bitmapmask, copy->rop_decriptor, 0);
-        SelectObject(dc, prev_bitmap);
-        DeleteObject(dc);
-        ReleaseMutex(pixman_data->mutex);
     } else {
-        surface_to_image(surface, &image);
-        gdi_draw_image(canvas->dc, &copy->src_area, bbox, image.pixels,
-                       image.stride, image.width, image.height, &bitmapmask,
-                       copy->rop_decriptor, 0);
-    }
+        surface = canvas_get_image(&canvas->base, copy->src_bitmap);
+        pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
+    
+        Lock lock(*canvas->lock);
+        bitmapmask = get_mask_bitmap(canvas, &copy->mask);
+        set_scale_mode(canvas, copy->scale_mode);
+        set_clip(canvas, clip);
+    
+        if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
+            HDC dc;
+            HBITMAP prev_bitmap;
+    
+            dc = create_compatible_dc();
+            prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
+            gdi_draw_bitmap_redrop(canvas->dc, &copy->src_area, bbox, dc,
+                                   &bitmapmask, copy->rop_decriptor, 0);
+            SelectObject(dc, prev_bitmap);
+            DeleteObject(dc);
+            ReleaseMutex(pixman_data->mutex);
+        } else {
+            surface_to_image(surface, &image);
+            gdi_draw_image(canvas->dc, &copy->src_area, bbox, image.pixels,
+                           image.stride, image.width, image.height, &bitmapmask,
+                           copy->rop_decriptor, 0);
+        }
 
+        pixman_image_unref(surface);
+    
+    }
     free_mask(&bitmapmask);
-
-    pixman_image_unref(surface);
 }
 
 static void gdi_canvas_put_image(SpiceCanvas *spice_canvas, HDC dc, const SpiceRect *dest, const uint8_t *src_data,
@@ -1136,34 +1186,43 @@ static void gdi_canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bb
                                         SpiceTransparent* transparent)
 {
     GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+    GdiCanvas *gdi_surface;
     pixman_image_t *surface;
     GdiImage image;
     PixmanData *pixman_data;
 
-    surface = canvas_get_image(&canvas->base, transparent->src_bitmap);
-    pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
-    Lock lock(*canvas->lock);
-    set_clip(canvas, clip);
-    if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
-        HDC dc;
-        HBITMAP prev_bitmap;
-
-        dc = create_compatible_dc();
-        prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
-        gdi_draw_bitmap_transparent(canvas, canvas->dc, &transparent->src_area, bbox, dc,
-                                    transparent->true_color);
-
-        SelectObject(dc, prev_bitmap);
-        DeleteObject(dc);
-        ReleaseMutex(pixman_data->mutex);
+    gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, transparent->src_bitmap);
+    if (gdi_surface) {
+        Lock lock(*canvas->lock);
+        set_clip(canvas, clip);
+        gdi_draw_bitmap_transparent(canvas, canvas->dc, &transparent->src_area, bbox,
+                                    gdi_surface->dc, transparent->true_color);
     } else {
-        surface_to_image(surface, &image);
-        gdi_draw_image_transparent(canvas, canvas->dc, &transparent->src_area, bbox, image.pixels,
-                                   image.stride, image.width, image.height,
-                                   transparent->true_color, 0);
+        surface = canvas_get_image(&canvas->base, transparent->src_bitmap);
+        pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
+        Lock lock(*canvas->lock);
+        set_clip(canvas, clip);
+        if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
+            HDC dc;
+            HBITMAP prev_bitmap;
+    
+            dc = create_compatible_dc();
+            prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
+            gdi_draw_bitmap_transparent(canvas, canvas->dc, &transparent->src_area, bbox, dc,
+                                        transparent->true_color);
+    
+            SelectObject(dc, prev_bitmap);
+            DeleteObject(dc);
+            ReleaseMutex(pixman_data->mutex);
+        } else {
+            surface_to_image(surface, &image);
+            gdi_draw_image_transparent(canvas, canvas->dc, &transparent->src_area, bbox, image.pixels,
+                                       image.stride, image.width, image.height,
+                                       transparent->true_color, 0);
+        }
+    
+        pixman_image_unref(surface);
     }
-
-    pixman_image_unref(surface);
 }
 
 static void gdi_draw_bitmap_alpha(HDC dest_dc, const SpiceRect *src, const SpiceRect *dest,
@@ -1208,40 +1267,51 @@ static void gdi_draw_image_alpha(HDC dest_dc, const SpiceRect *src, const SpiceR
 static void gdi_canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlnd* alpha_blend)
 {
     GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
+    GdiCanvas *gdi_surface;
     pixman_image_t *surface;
     GdiImage image;
     PixmanData *pixman_data;
     int use_bitmap_alpha;
 
-    surface = canvas_get_image(&canvas->base, alpha_blend->src_bitmap);
-    use_bitmap_alpha = pixman_image_get_depth(surface) == 32;
-    pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
-
-    Lock lock(*canvas->lock);
-    set_clip(canvas, clip);
-    if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
-        HDC dc;
-        HBITMAP prev_bitmap;
-
-        dc = create_compatible_dc();
-        prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
-        gdi_draw_bitmap_alpha(canvas->dc, &alpha_blend->src_area, bbox, dc, alpha_blend->alpha,
-                              use_bitmap_alpha);
-        SelectObject(dc, prev_bitmap);
-        DeleteObject(dc);
-        ReleaseMutex(pixman_data->mutex);
+    gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, alpha_blend->src_bitmap);
+    if (gdi_surface) {
+        Lock lock(*canvas->lock);
+        set_clip(canvas, clip);
+        use_bitmap_alpha = 0;
+        gdi_draw_bitmap_alpha(canvas->dc, &alpha_blend->src_area, bbox, gdi_surface->dc,
+                              alpha_blend->alpha, use_bitmap_alpha);
     } else {
-        surface_to_image(surface, &image);
-        gdi_draw_image_alpha(canvas->dc, &alpha_blend->src_area, bbox, image.pixels,
-                             image.stride, image.width, image.height,
-                             alpha_blend->alpha, 0, use_bitmap_alpha);
+        surface = canvas_get_image(&canvas->base, alpha_blend->src_bitmap);
+        use_bitmap_alpha = pixman_image_get_depth(surface) == 32;
+        pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
+    
+        Lock lock(*canvas->lock);
+        set_clip(canvas, clip);
+        if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
+            HDC dc;
+            HBITMAP prev_bitmap;
+    
+            dc = create_compatible_dc();
+            prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
+            gdi_draw_bitmap_alpha(canvas->dc, &alpha_blend->src_area, bbox, dc, alpha_blend->alpha,
+                                  use_bitmap_alpha);
+            SelectObject(dc, prev_bitmap);
+            DeleteObject(dc);
+            ReleaseMutex(pixman_data->mutex);
+        } else {
+            surface_to_image(surface, &image);
+            gdi_draw_image_alpha(canvas->dc, &alpha_blend->src_area, bbox, image.pixels,
+                                 image.stride, image.width, image.height,
+                                 alpha_blend->alpha, 0, use_bitmap_alpha);
+        }
+    
+        pixman_image_unref(surface);
     }
-
-    pixman_image_unref(surface);
 }
 
 static void gdi_canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
 {
+    GdiCanvas *gdi_surface;
     GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     pixman_image_t *surface;
     GdiImage image;
@@ -1251,77 +1321,98 @@ static void gdi_canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, S
     HBRUSH hbrush;
     uint8_t rop3;
 
-    surface = canvas_get_image(&canvas->base, opaque->src_bitmap);
-    pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
-    bitmapmask = get_mask_bitmap(canvas, &opaque->mask);
     rop3 = calc_rop3_src_brush(opaque->rop_decriptor);
-    hbrush = get_brush(canvas, &opaque->brush);
-
 
-    Lock lock(*canvas->lock);
-    set_scale_mode(canvas, opaque->scale_mode);
-    set_clip(canvas, clip);
-    prev_hbrush = set_brush(canvas->dc, hbrush, &opaque->brush);
-
-    if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
-        HDC dc;
-        HBITMAP prev_bitmap;
-
-        dc = create_compatible_dc();
-        prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
-        gdi_draw_bitmap(canvas->dc, &opaque->src_area, bbox, dc, &bitmapmask, rop3);
-        SelectObject(dc, prev_bitmap);
-        DeleteObject(dc);
-        ReleaseMutex(pixman_data->mutex);
+    gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, opaque->src_bitmap);
+    if (gdi_surface) {
+        Lock lock(*canvas->lock);
+        bitmapmask = get_mask_bitmap(canvas, &opaque->mask);
+        hbrush = get_brush(canvas, &opaque->brush);
+        set_scale_mode(canvas, opaque->scale_mode);
+        set_clip(canvas, clip);
+        prev_hbrush = set_brush(canvas->dc, hbrush, &opaque->brush);
+        gdi_draw_bitmap(canvas->dc, &opaque->src_area, bbox, gdi_surface->dc, &bitmapmask, rop3);
+        unset_brush(canvas->dc, prev_hbrush);
     } else {
-        surface_to_image(surface, &image);
-        gdi_draw_image_rop3(canvas->dc, &opaque->src_area, bbox, image.pixels,
-                            image.stride, image.width, image.height, &bitmapmask, rop3, 0);
+        surface = canvas_get_image(&canvas->base, opaque->src_bitmap);
+        pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
+    
+        Lock lock(*canvas->lock);
+        bitmapmask = get_mask_bitmap(canvas, &opaque->mask);
+        hbrush = get_brush(canvas, &opaque->brush);
+        set_scale_mode(canvas, opaque->scale_mode);
+        set_clip(canvas, clip);
+        prev_hbrush = set_brush(canvas->dc, hbrush, &opaque->brush);
+    
+        if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
+            HDC dc;
+            HBITMAP prev_bitmap;
+    
+            dc = create_compatible_dc();
+            prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
+            gdi_draw_bitmap(canvas->dc, &opaque->src_area, bbox, dc, &bitmapmask, rop3);
+            SelectObject(dc, prev_bitmap);
+            DeleteObject(dc);
+            ReleaseMutex(pixman_data->mutex);
+        } else {
+            surface_to_image(surface, &image);
+            gdi_draw_image_rop3(canvas->dc, &opaque->src_area, bbox, image.pixels,
+                                image.stride, image.width, image.height, &bitmapmask, rop3, 0);
+        }
+        unset_brush(canvas->dc, prev_hbrush);
+        pixman_image_unref(surface);
     }
 
-    unset_brush(canvas->dc, prev_hbrush);
-
     free_mask(&bitmapmask);
-
-    pixman_image_unref(surface);
 }
 
 static void gdi_canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
 {
+    GdiCanvas *gdi_surface;
     GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     pixman_image_t *surface;
     GdiImage image;
     struct BitmapData bitmapmask;
     PixmanData *pixman_data;
 
-    bitmapmask = get_mask_bitmap(canvas, &blend->mask);
-    surface = canvas_get_image(&canvas->base, blend->src_bitmap);
-    pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
-
-    Lock lock(*canvas->lock);
-    set_scale_mode(canvas, blend->scale_mode);
-    set_clip(canvas, clip);
-
-    if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
-        HDC dc;
-        HBITMAP prev_bitmap;
-
-        dc = create_compatible_dc();
-        prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
-        gdi_draw_bitmap_redrop(canvas->dc, &blend->src_area, bbox, dc,
+    gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, blend->src_bitmap);
+    if (gdi_surface) {
+        Lock lock(*canvas->lock);
+        bitmapmask = get_mask_bitmap(canvas, &blend->mask);
+        set_scale_mode(canvas, blend->scale_mode);
+        set_clip(canvas, clip);
+        gdi_draw_bitmap_redrop(canvas->dc, &blend->src_area, bbox, gdi_surface->dc,
                                &bitmapmask, blend->rop_decriptor, 0);
-        SelectObject(dc, prev_bitmap);
-        DeleteObject(dc);
-        ReleaseMutex(pixman_data->mutex);
-    } else {
-        surface_to_image(surface, &image);
-        gdi_draw_image(canvas->dc, &blend->src_area, bbox, image.pixels, image.stride, image.width,
-                       image.height, &bitmapmask, blend->rop_decriptor, 0);
+    }  else {
+        surface = canvas_get_image(&canvas->base, blend->src_bitmap);
+        pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
+    
+        Lock lock(*canvas->lock);
+        bitmapmask = get_mask_bitmap(canvas, &blend->mask);
+        set_scale_mode(canvas, blend->scale_mode);
+        set_clip(canvas, clip);
+    
+        if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
+            HDC dc;
+            HBITMAP prev_bitmap;
+    
+            dc = create_compatible_dc();
+            prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
+            gdi_draw_bitmap_redrop(canvas->dc, &blend->src_area, bbox, dc,
+                                   &bitmapmask, blend->rop_decriptor, 0);
+            SelectObject(dc, prev_bitmap);
+            DeleteObject(dc);
+            ReleaseMutex(pixman_data->mutex);
+        } else {
+            surface_to_image(surface, &image);
+            gdi_draw_image(canvas->dc, &blend->src_area, bbox, image.pixels, image.stride, image.width,
+                           image.height, &bitmapmask, blend->rop_decriptor, 0);
+        }
+
+        pixman_image_unref(surface);
     }
 
     free_mask(&bitmapmask);
-
-    pixman_image_unref(surface);
 }
 
 static void gdi_canvas_draw_blackness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
@@ -1329,9 +1420,8 @@ static void gdi_canvas_draw_blackness(SpiceCanvas *spice_canvas, SpiceRect *bbox
     GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     struct BitmapData bitmapmask;
 
-    bitmapmask = get_mask_bitmap(canvas, &blackness->mask);
-
     Lock lock(*canvas->lock);
+    bitmapmask = get_mask_bitmap(canvas, &blackness->mask);
     set_clip(canvas, clip);
     gdi_draw_bitmap(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask, 0x0);
 
@@ -1343,9 +1433,8 @@ static void gdi_canvas_draw_invers(SpiceCanvas *spice_canvas, SpiceRect *bbox, S
     GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     struct BitmapData bitmapmask;
 
-    bitmapmask = get_mask_bitmap(canvas, &invers->mask);
-
     Lock lock(*canvas->lock);
+    bitmapmask = get_mask_bitmap(canvas, &invers->mask);
     set_clip(canvas, clip);
     gdi_draw_bitmap(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask, 0x55);
 
@@ -1357,9 +1446,8 @@ static void gdi_canvas_draw_whiteness(SpiceCanvas *spice_canvas, SpiceRect *bbox
     GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     struct BitmapData bitmapmask;
 
-    bitmapmask = get_mask_bitmap(canvas, &whiteness->mask);
-
     Lock lock(*canvas->lock);
+    bitmapmask = get_mask_bitmap(canvas, &whiteness->mask);
     set_clip(canvas, clip);
     gdi_draw_bitmap(canvas->dc, bbox, bbox, canvas->dc, &bitmapmask, 0xff);
 
@@ -1368,6 +1456,7 @@ static void gdi_canvas_draw_whiteness(SpiceCanvas *spice_canvas, SpiceRect *bbox
 
 static void gdi_canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
 {
+    GdiCanvas *gdi_surface;
     GdiCanvas *canvas = (GdiCanvas *)spice_canvas;
     pixman_image_t *surface;
     GdiImage image;
@@ -1376,37 +1465,49 @@ static void gdi_canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, Spi
     HBRUSH hbrush;
     PixmanData *pixman_data;
 
-    hbrush = get_brush(canvas, &rop3->brush);
-    surface = canvas_get_image(&canvas->base, rop3->src_bitmap);
-    pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
-    bitmapmask = get_mask_bitmap(canvas, &rop3->mask);
-
-    Lock lock(*canvas->lock);
-    set_scale_mode(canvas, rop3->scale_mode);
-    set_clip(canvas, clip);
-    prev_hbrush = set_brush(canvas->dc, hbrush, &rop3->brush);
-
-    if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
-        HDC dc;
-        HBITMAP prev_bitmap;
-
-        dc = create_compatible_dc();
-        prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
-        gdi_draw_bitmap(canvas->dc, &rop3->src_area, bbox, dc,
+    gdi_surface = (GdiCanvas *)canvas_get_surface(&canvas->base, rop3->src_bitmap);
+    if (gdi_surface) {
+        Lock lock(*canvas->lock);
+        hbrush = get_brush(canvas, &rop3->brush);
+        bitmapmask = get_mask_bitmap(canvas, &rop3->mask);
+        set_scale_mode(canvas, rop3->scale_mode);
+        set_clip(canvas, clip);
+        prev_hbrush = set_brush(canvas->dc, hbrush, &rop3->brush);
+        gdi_draw_bitmap(canvas->dc, &rop3->src_area, bbox, gdi_surface->dc,
                         &bitmapmask, rop3->rop3);
-        SelectObject(dc, prev_bitmap);
-        DeleteObject(dc);
-        ReleaseMutex(pixman_data->mutex);
+        unset_brush(canvas->dc, prev_hbrush);
     } else {
-        surface_to_image(surface, &image);
-        gdi_draw_image_rop3(canvas->dc, &rop3->src_area, bbox, image.pixels,
-                            image.stride, image.width, image.height, &bitmapmask, rop3->rop3, 0);
+        surface = canvas_get_image(&canvas->base, rop3->src_bitmap);
+        pixman_data = (PixmanData *)pixman_image_get_destroy_data(surface);
+        Lock lock(*canvas->lock);
+        hbrush = get_brush(canvas, &rop3->brush);
+        bitmapmask = get_mask_bitmap(canvas, &rop3->mask);
+        set_scale_mode(canvas, rop3->scale_mode);
+        set_clip(canvas, clip);
+        prev_hbrush = set_brush(canvas->dc, hbrush, &rop3->brush);
+    
+        if (pixman_data && (WaitForSingleObject(pixman_data->mutex, INFINITE) != WAIT_FAILED)) {
+            HDC dc;
+            HBITMAP prev_bitmap;
+    
+            dc = create_compatible_dc();
+            prev_bitmap = (HBITMAP)SelectObject(dc, pixman_data->bitmap);
+            gdi_draw_bitmap(canvas->dc, &rop3->src_area, bbox, dc,
+                            &bitmapmask, rop3->rop3);
+            SelectObject(dc, prev_bitmap);
+            DeleteObject(dc);
+            ReleaseMutex(pixman_data->mutex);
+        } else {
+            surface_to_image(surface, &image);
+            gdi_draw_image_rop3(canvas->dc, &rop3->src_area, bbox, image.pixels,
+                                image.stride, image.width, image.height, &bitmapmask, rop3->rop3, 0);
+        }
+
+        unset_brush(canvas->dc, prev_hbrush);
+        pixman_image_unref(surface);
     }
 
-    unset_brush(canvas->dc, prev_hbrush);
     free_mask(&bitmapmask);
-
-    pixman_image_unref(surface);
 }
 
 static void gdi_canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
@@ -1433,8 +1534,8 @@ static void gdi_canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, Spi
         HBRUSH prev_hbrush;
         HBRUSH hbrush;
 
-        hbrush = get_brush(canvas, &text->back_brush);
         Lock lock(*canvas->lock);
+        hbrush = get_brush(canvas, &text->back_brush);
         prev_hbrush = set_brush(canvas->dc, hbrush, &text->back_brush);
         gdi_draw_bitmap_redrop(canvas->dc, bbox, bbox, canvas->dc, NULL,
                                text->back_mode, 1);
@@ -1724,6 +1825,7 @@ SpiceCanvas *gdi_canvas_create(int width, int height,
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                              , SpiceImageCache *bits_cache
 #endif
+                            , SpiceImageSurfaces *surfaces
                             , SpiceGlzDecoder *glz_decoder
                             )
 {
@@ -1742,6 +1844,7 @@ SpiceCanvas *gdi_canvas_create(int width, int height,
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                                , bits_cache
 #endif
+                               , surfaces
                                , glz_decoder);
     canvas->dc = dc;
     canvas->lock = lock;
diff --git a/common/gdi_canvas.h b/common/gdi_canvas.h
index 8b8f847..73d6978 100644
--- a/common/gdi_canvas.h
+++ b/common/gdi_canvas.h
@@ -37,6 +37,7 @@ SpiceCanvas *gdi_canvas_create(int width, int height,
                                HDC dc, class Mutex *lock, int bits,
                                SpiceImageCache *bits_cache,
                                SpicePaletteCache *palette_cache,
+			       SpiceImageSurfaces *surfaces,
                                SpiceGlzDecoder *glz_decoder);
 
 void gdi_canvas_init();
diff --git a/common/gl_canvas.c b/common/gl_canvas.c
index f69c920..3fd49f2 100644
--- a/common/gl_canvas.c
+++ b/common/gl_canvas.c
@@ -828,6 +828,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, int depth
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                               , SpiceImageCache *bits_cache
 #endif
+                              , SpiceImageSurfaces *surfaces
                               , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                               , SpiceVirtMapping *virt_mapping
@@ -854,6 +855,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, int depth
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                                , bits_cache
 #endif
+                               , surfaces
                                , glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                                , virt_mapping
diff --git a/common/gl_canvas.h b/common/gl_canvas.h
index 29737ec..6d23629 100644
--- a/common/gl_canvas.h
+++ b/common/gl_canvas.h
@@ -28,6 +28,7 @@ SpiceCanvas *gl_canvas_create(int width, int height, int depth
 #elif defined(CAIRO_CANVAS_IMAGE_CACHE)
                            , SpiceImageCache *bits_cache
 #endif
+			   , SpiceImageSurfaces *surfaces
                            , SpiceGlzDecoder *glz_decoder
 #ifndef CAIRO_CANVAS_NO_CHUNKS
                             , SpiceVirtMapping *virt_mapping
diff --git a/server/red_dispatcher.c b/server/red_dispatcher.c
index 17bf24c..88059f6 100644
--- a/server/red_dispatcher.c
+++ b/server/red_dispatcher.c
@@ -522,6 +522,7 @@ RedDispatcher *red_dispatcher_init(QXLInterface *qxl_interface)
     init_data.num_memslots = init_info.num_memslots;
     init_data.num_memslots_groups = init_info.num_memslots_groups;
     init_data.internal_groupslot_id = init_info.internal_groupslot_id;
+    init_data.n_surfaces = init_info.n_surfaces;
 
     num_active_workers = 1;
 
diff --git a/server/red_worker.c b/server/red_worker.c
index 5dec5f6..6d19f35 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -396,6 +396,7 @@ typedef struct ImageItem {
     int height;
     int stride;
     int top_down;
+    int surface_id;
     uint8_t data[0];
 } ImageItem;
 
@@ -460,6 +461,7 @@ typedef struct  __attribute__ ((__packed__)) RedImage {
         SpiceQUICData quic;
         SpiceLZRGBData lz_rgb;
         SpiceLZPLTData lz_plt;
+        SpiceSurface surface;
     };
 } RedImage;
 
@@ -612,6 +614,8 @@ typedef struct GlzSharedDictionary {
     int migrate_freeze;
 } GlzSharedDictionary;
 
+#define NUM_SURFACES 10000
+
 struct DisplayChannel {
     RedChannel base;
 
@@ -639,7 +643,7 @@ struct DisplayChannel {
     Ring glz_drawables_inst_to_free;               // list of instances to be freed
     pthread_mutex_t glz_drawables_inst_to_free_lock;
 
-    uint8_t surface_client_created;
+    uint8_t surface_client_created[NUM_SURFACES];
 
     struct {
         union {
@@ -798,8 +802,14 @@ typedef enum {
     BITMAP_GRADUAL_HIGH,
 } BitmapGradualType;
 
+typedef struct DependItem {
+    Drawable *drawable;
+    RingItem ring_item;
+} DependItem;
+
 struct Drawable {
     uint8_t refs;
+    RingItem surface_list_link;
     RingItem list_link;
     DrawItem tree_item;
     PipeItem pipe_item;
@@ -821,6 +831,10 @@ struct Drawable {
     BitmapGradualType copy_bitmap_graduality;
     uint32_t group_id;
     uint8_t *self_bitmap;
+    DependItem depend_items[3];
+
+    uint8_t *backed_surface_data;
+    DependItem pipe_depend_items[3];
 };
 
 typedef struct _Drawable _Drawable;
@@ -860,7 +874,16 @@ typedef struct DrawContext {
 
 typedef struct RedSurface {
     uint32_t refs;
+    Ring current;
+    Ring current_list;
+    int current_gn;
     DrawContext context;
+
+    Ring depend_on_me;
+
+    //fix me - better handling here
+    QXLReleaseInfo *release_info;
+    uint32_t release_group_id;
 } RedSurface;
 
 #ifdef STREAM_TRACE
@@ -907,10 +930,11 @@ typedef struct RedWorker {
     uint32_t renderers[RED_MAX_RENDERERS];
     uint32_t renderer;
 
-    RedSurface surface;
+    RedSurface surfaces[NUM_SURFACES];
+    uint32_t n_surfaces;
+    SpiceImageSurfaces image_surfaces;
 
     Ring current_list;
-    Ring current;
     uint32_t current_size;
     uint32_t drawable_count;
     uint32_t transparent_count;
@@ -990,14 +1014,14 @@ typedef struct RedWorker {
 pthread_mutex_t avcodec_lock = PTHREAD_MUTEX_INITIALIZER;
 
 static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable);
-static void red_current_flush(RedWorker *worker);
+static void red_current_flush(RedWorker *worker, int surface_id);
 static void display_channel_push(RedWorker *worker);
 #ifdef DRAW_ALL
-#define red_update_area(worker, rect)
+#define red_update_area(worker, rect, surface_id)
 #define red_draw_drawable(worker, item)
 #else
 static void red_draw_drawable(RedWorker *worker, Drawable *item);
-static void red_update_area(RedWorker *worker, const SpiceRect *area);
+static void red_update_area(RedWorker *worker, const SpiceRect *area, int surface_id);
 #endif
 static void red_release_cursor(RedWorker *worker, CursorItem *cursor);
 static inline void release_drawable(RedWorker *worker, Drawable *item);
@@ -1070,6 +1094,25 @@ static void print_compress_stats(DisplayChannel *display_channel)
 
 #endif
 
+static inline int is_primary_surface(RedWorker *worker, uint32_t surface_id)
+{
+    if (surface_id == 0) {
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static inline void __validate_surface(RedWorker *worker, uint32_t surface_id)
+{
+    PANIC_ON(surface_id >= worker->n_surfaces);
+}
+
+static inline void validate_surface(RedWorker *worker, uint32_t surface_id)
+{
+    PANIC_ON(surface_id >= worker->n_surfaces);
+    PANIC_ON(!worker->surfaces[surface_id].context.canvas);
+}
+
 static inline int get_memslot_id(RedWorker *worker, unsigned long addr)
 {
     return addr >> worker->memslot_id_shift;
@@ -1345,11 +1388,45 @@ static void red_pipe_add_type(RedChannel* channel, int pipe_item_type)
     red_pipe_add(channel, item);
 }
 
+static inline void red_create_surface_item(RedWorker *worker, int surface_id);
+static void red_add_surface_image(RedWorker *worker, int surface_id);
+
+static inline void red_handle_drawable_surfaces_client_synced(RedWorker *worker, Drawable *drawable)
+{
+    int x;
+    int surface_id;
+
+    for (x = 0; x < 3; ++x) {
+        surface_id = drawable->qxl_drawable->surfaces_dest[x];
+        if (surface_id != -1) {
+            validate_surface(worker, surface_id);
+            if (worker->display_channel->surface_client_created[surface_id] == TRUE) {
+                continue;
+            }
+            red_create_surface_item(worker, surface_id);
+            red_add_surface_image(worker, surface_id);
+        }
+    }
+
+    surface_id = drawable->qxl_drawable->surface_id;
+    validate_surface(worker, surface_id);
+
+    if (worker->display_channel->surface_client_created[surface_id] == TRUE) {
+        return;
+    }
+
+    red_create_surface_item(worker, surface_id);
+    red_add_surface_image(worker, surface_id);
+}
+
 static inline void red_pipe_add_drawable(RedWorker *worker, Drawable *drawable)
 {
     if (!worker->display_channel) {
         return;
     }
+
+    red_handle_drawable_surfaces_client_synced(worker, drawable);
+
     drawable->refs++;
     red_pipe_add(&worker->display_channel->base, &drawable->pipe_item);
 }
@@ -1369,6 +1446,8 @@ static inline void red_pipe_add_drawable_after(RedWorker *worker, Drawable *draw
     red_pipe_add_after(&worker->display_channel->base, &drawable->pipe_item, &pos_after->pipe_item);
 }
 
+static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id);
+
 static inline void red_pipe_remove_drawable(RedWorker *worker, Drawable *drawable)
 {
     if (ring_item_is_linked(&drawable->pipe_item.link)) {
@@ -1435,8 +1514,6 @@ static void red_pipe_clear(RedChannel *channel)
         case PIPE_ITEM_TYPE_MIGRATE_DATA:
         case PIPE_ITEM_TYPE_INVAL_PALLET_CACHE:
         case PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE:
-        case PIPE_ITEM_TYPE_CREATE_SURFACE:
-        case PIPE_ITEM_TYPE_DESTROY_SURFACE:
             free(item);
             break;
         case PIPE_ITEM_TYPE_IMAGE:
@@ -1453,6 +1530,18 @@ static void red_pipe_clear(RedChannel *channel)
             red_display_release_stream((DisplayChannel *)channel,
                                        SPICE_CONTAINEROF(item, StreamAgent, destroy_item));
             break;
+        case PIPE_ITEM_TYPE_CREATE_SURFACE: {
+            SurfaceCreateItem *surface_create = SPICE_CONTAINEROF(item, SurfaceCreateItem,
+                                                                  pipe_item);
+            free(surface_create);
+            break;
+        }
+        case PIPE_ITEM_TYPE_DESTROY_SURFACE: {
+            SurfaceDestroyItem *surface_destroy = SPICE_CONTAINEROF(item, SurfaceDestroyItem,
+                                                                    pipe_item);
+            free(surface_destroy);
+            break;
+        }
         }
     }
     channel->pipe_size = 0;
@@ -1532,40 +1621,62 @@ static inline void red_destroy_surface_item(RedWorker *worker, uint32_t surface_
         return;
     }
 
+    worker->display_channel->surface_client_created[surface_id] = FALSE;
+
     destroy = get_surface_destroy_item(surface_id);
 
     red_pipe_add(&worker->display_channel->base, &destroy->pipe_item);
 }
 
-static inline void __red_destroy_surface(RedWorker *worker)
+static inline void __red_destroy_surface(RedWorker *worker, uint32_t surface_id)
 {
-    RedSurface *surface = &worker->surface;
+    RedSurface *surface = &worker->surfaces[surface_id];
 
-    if (!--worker->surface.refs) {
+    if (!--surface->refs) {
 #ifdef STREAM_TRACE
         red_reset_stream_trace(worker);
 #endif
         if (surface->context.canvas) {
             surface->context.canvas->ops->destroy(surface->context.canvas);
+            if (surface->release_info) {
+                QXLReleaseInfoExt release_info_ext;
+
+                release_info_ext.group_id = surface->release_group_id;
+                release_info_ext.info = surface->release_info;
+                worker->qxl->release_resource(worker->qxl, release_info_ext);
+            }
+
             surface->context.canvas = NULL;
-            red_destroy_surface_item(worker, 0);
+            red_destroy_surface_item(worker, surface_id);
         }
+
+        PANIC_ON(!ring_is_empty(&surface->depend_on_me));
     }
 }
 
-static inline void red_destroy_surface(RedWorker *worker)
+static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id)
+{
+    validate_surface(worker, surface_id);
+    __red_destroy_surface(worker, surface_id);
+}
+
+static inline void set_surface_release_info(RedWorker *worker, uint32_t surface_id,
+                                            QXLReleaseInfo *release_info, uint32_t group_id)
 {
-    RedSurface *surface = &worker->surface;
+    RedSurface *surface;
 
-    PANIC_ON(!surface->context.canvas);
-    __red_destroy_surface(worker);
+    validate_surface(worker, surface_id);
+    surface = &worker->surfaces[surface_id];
+
+    surface->release_info = release_info;
+    surface->release_group_id = group_id;
 }
 
 static inline void free_qxl_drawable(RedWorker *worker, QXLDrawable *drawable, uint32_t group_id,
                                      uint8_t *self_bitmap)
 {
     QXLReleaseInfoExt release_info_ext;
-    red_destroy_surface(worker);
+    red_destroy_surface(worker, drawable->surface_id);
 
     if (self_bitmap) {
         free(self_bitmap);
@@ -1575,6 +1686,41 @@ static inline void free_qxl_drawable(RedWorker *worker, QXLDrawable *drawable, u
     worker->qxl->release_resource(worker->qxl, release_info_ext);
 }
 
+static void remove_depended_item(DependItem *item)
+{
+    ASSERT(item->drawable);
+    ASSERT(ring_item_is_linked(&item->ring_item));
+    item->drawable = NULL;
+    ring_remove(&item->ring_item);
+}
+
+static inline void red_dec_surfaces_drawable_dependencies(RedWorker *worker, QXLDrawable *drawable)
+{
+    int x;
+    int surface_id;
+
+    for (x = 0; x < 3; ++x) {
+        surface_id = drawable->surfaces_dest[x];
+        if (surface_id == -1) {
+            continue;
+        }
+        red_destroy_surface(worker, surface_id);
+    }
+}
+
+static void remove_drawable_dependencies(RedWorker *worker, Drawable *drawable)
+{
+    int x;
+    int surface_id;
+
+    for (x = 0; x < 3; ++x) {
+        surface_id = drawable->qxl_drawable->surfaces_dest[x];
+        if (surface_id != -1 && drawable->depend_items[x].drawable) {
+            remove_depended_item(&drawable->depend_items[x]);
+        }
+    }
+}
+
 static inline void release_drawable(RedWorker *worker, Drawable *item)
 {
     if (!--item->refs) {
@@ -1588,6 +1734,9 @@ static inline void release_drawable(RedWorker *worker, Drawable *item)
         ASSERT(!item->tree_item.shadow);
         region_destroy(&item->tree_item.base.rgn);
 
+        remove_drawable_dependencies(worker, item);
+        red_dec_surfaces_drawable_dependencies(worker, item->qxl_drawable);
+
         if (item->red_glz_drawable) {
             item->red_glz_drawable->drawable = NULL;
         } else { // no refernce to the qxl drawable left
@@ -1659,9 +1808,29 @@ static inline void red_add_item_trace(RedWorker *worker, Drawable *item)
 
 #endif
 
+static void surface_flush(RedWorker *worker, int surface_id, SpiceRect *rect)
+{
+    red_update_area(worker, rect, surface_id);
+}
+
+static void red_flush_source_surfaces(RedWorker *worker, Drawable *drawable)
+{
+    int x;
+    int surface_id;
+
+    for (x = 0; x < 3; ++x) {
+        surface_id = drawable->qxl_drawable->surfaces_dest[x];
+        if (surface_id != -1 && drawable->depend_items[x].drawable) {
+            remove_depended_item(&drawable->depend_items[x]);
+            surface_flush(worker, surface_id, &drawable->qxl_drawable->surfaces_rects[x]);
+        }
+    }
+}
+
 static inline void current_remove_drawable(RedWorker *worker, Drawable *item)
 {
     worker->drawable_count--;
+
     if (item->tree_item.effect != QXL_EFFECT_OPAQUE) {
         worker->transparent_count--;
     }
@@ -1675,6 +1844,7 @@ static inline void current_remove_drawable(RedWorker *worker, Drawable *item)
     remove_shadow(worker, &item->tree_item);
     ring_remove(&item->tree_item.base.siblings_link);
     ring_remove(&item->list_link);
+    ring_remove(&item->surface_list_link);
     release_drawable(worker, item);
     worker->current_size--;
 }
@@ -1720,9 +1890,9 @@ static inline void current_remove(RedWorker *worker, TreeItem *item)
     }
 }
 
-static void current_tree_for_each(RedWorker *worker, void (*f)(TreeItem *, void *), void * data)
+static void current_tree_for_each(RedWorker *worker, Ring *ring, void (*f)(TreeItem *, void *),
+                                  void * data)
 {
-    Ring *ring = &worker->current;
     RingItem *ring_item;
     Ring *top_ring;
 
@@ -1758,11 +1928,11 @@ static void current_tree_for_each(RedWorker *worker, void (*f)(TreeItem *, void
     }
 }
 
-static void red_current_clear(RedWorker *worker)
+static void red_current_clear(RedWorker *worker, int surface_id)
 {
     RingItem *ring_item;
 
-    while ((ring_item = ring_get_head(&worker->current))) {
+    while ((ring_item = ring_get_head(&worker->surfaces[surface_id].current))) {
         TreeItem *now = SPICE_CONTAINEROF(ring_item, TreeItem, siblings_link);
         current_remove(worker, now);
     }
@@ -1840,19 +2010,19 @@ void __show_current(TreeItem *item, void *data)
     print_base_item("TREE", item);
 }
 
-static void show_current(RedWorker *worker)
+static void show_current(RedWorker *worker, Ring *ring)
 {
-    if (ring_is_empty(&worker->current)) {
+    if (ring_is_empty(ring)) {
         red_printf("TEST: TREE: EMPTY");
         return;
     }
-    current_tree_for_each(worker, __show_current, NULL);
+    current_tree_for_each(worker, ring, __show_current, NULL);
 }
 
 #else
 #define print_rgn(a, b)
 #define print_draw_private(a, b)
-#define show_current(a)
+#define show_current(a, r)
 #define print_shadow_item(a, b)
 #define print_base_item(a, b)
 #endif
@@ -1872,16 +2042,16 @@ static inline Shadow *__find_shadow(TreeItem *item)
     return ((DrawItem *)item)->shadow;
 }
 
-static inline Ring *ring_of(RedWorker *worker, TreeItem *item)
+static inline Ring *ring_of(RedWorker *worker, Ring *ring, TreeItem *item)
 {
-    return (item->container) ? &item->container->items : &worker->current;
+    return (item->container) ? &item->container->items : ring;
 }
 
 static inline int __contained_by(RedWorker *worker, TreeItem *item, Ring *ring)
 {
     ASSERT(item && ring);
     do {
-        Ring *now = ring_of(worker, item);
+        Ring *now = ring_of(worker, ring, item);
         if (now == ring) {
             return TRUE;
         }
@@ -1890,7 +2060,7 @@ static inline int __contained_by(RedWorker *worker, TreeItem *item, Ring *ring)
     return FALSE;
 }
 
-static inline void __exclude_region(RedWorker *worker, TreeItem *item, QRegion *rgn,
+static inline void __exclude_region(RedWorker *worker, Ring *ring, TreeItem *item, QRegion *rgn,
                                     Ring **top_ring, Drawable *frame_candidate)
 {
     QRegion and_rgn;
@@ -1924,7 +2094,7 @@ static inline void __exclude_region(RedWorker *worker, TreeItem *item, QRegion *
                     region_or(rgn, &and_rgn);
                     // in flat representation of current, shadow is always his owner next
                     if (!__contained_by(worker, (TreeItem*)shadow, *top_ring)) {
-                        *top_ring = ring_of(worker, (TreeItem*)shadow);
+                        *top_ring = ring_of(worker, ring, (TreeItem*)shadow);
                     }
                 }
             } else {
@@ -1944,7 +2114,7 @@ static inline void __exclude_region(RedWorker *worker, TreeItem *item, QRegion *
                 if ((shadow = __find_shadow(item))) {
                     region_or(rgn, &shadow->on_hold);
                     if (!__contained_by(worker, (TreeItem*)shadow, *top_ring)) {
-                        *top_ring = ring_of(worker, (TreeItem*)shadow);
+                        *top_ring = ring_of(worker, ring, (TreeItem*)shadow);
                     }
                 }
             }
@@ -1985,7 +2155,7 @@ static void exclude_region(RedWorker *worker, Ring *ring, RingItem *ring_item, Q
 
         if (region_intersects(rgn, &now->rgn)) {
             print_base_item("EXCLUDE2", now);
-            __exclude_region(worker, now, rgn, &top_ring, frame_candidate);
+            __exclude_region(worker, ring, now, rgn, &top_ring, frame_candidate);
             print_base_item("EXCLUDE3", now);
 
             if (region_is_empty(&now->rgn)) {
@@ -2048,7 +2218,7 @@ static void exclude_region(RedWorker *worker, Ring *ring, RingItem *ring_item, Q
 
         if (region_test(rgn, &now->rgn, REGION_TEST_SHARED)) {
             print_base_item("EXCLUDE2", now);
-            __exclude_region(worker, now, rgn, &top_ring);
+            __exclude_region(worker, ring, now, rgn, &top_ring);
             print_base_item("EXCLUDE3", now);
 
             if (region_is_empty(&now->rgn)) {
@@ -2115,8 +2285,14 @@ static inline int is_opaque_item(TreeItem *item)
 
 static inline void __current_add_drawable(RedWorker *worker, Drawable *drawable, RingItem *pos)
 {
+    RedSurface *surface;
+    uint32_t surface_id = drawable->qxl_drawable->surface_id;
+
+    validate_surface(worker, surface_id);
+    surface = &worker->surfaces[surface_id];
     ring_add_after(&drawable->tree_item.base.siblings_link, pos);
     ring_add(&worker->current_list, &drawable->list_link);
+    ring_add(&surface->current_list, &drawable->surface_list_link);
     drawable->refs++;
 }
 
@@ -3163,14 +3339,13 @@ static void red_reset_stream_trace(RedWorker *worker)
 
 #endif
 
-static inline int red_current_add(RedWorker *worker, Drawable *drawable)
+static inline int red_current_add(RedWorker *worker, Ring *ring, Drawable *drawable)
 {
     DrawItem *item = &drawable->tree_item;
 #ifdef RED_WORKER_STAT
     stat_time_t start_time = stat_now();
 #endif
     RingItem *now;
-    Ring *ring = &worker->current;
     QRegion exclude_rgn;
     RingItem *exclude_base = NULL;
 
@@ -3284,10 +3459,10 @@ static inline int red_current_add(RedWorker *worker, Drawable *drawable)
 
 #else
 
-static inline void __handle_remove_shadow(RedWorker *worker, TreeItem *item)
+static inline void __handle_remove_shadow(RedWorker *worker, TreeItem *item, Ring *ring)
 {
     Shadow *shadow;
-    Ring *ring;
+    Ring *_ring;
 
     while (item->type == TREE_ITEM_TYPE_CONTAINER) {
         if (!(item = (TreeItem *)ring_get_tail(&((Container *)item)->items))) {
@@ -3299,19 +3474,18 @@ static inline void __handle_remove_shadow(RedWorker *worker, TreeItem *item)
         return;
     }
     print_base_item("SHADW", &shadow->base);
-    ring = (shadow->base.container) ? &shadow->base.container->items : &worker->current;
-    exclude_region(worker, ring, ring_next(ring, &shadow->base.siblings_link), &shadow->on_hold);
+    _ring = (shadow->base.container) ? &shadow->base.container->items : ring;
+    exclude_region(worker, _ring, ring_next(_ring, &shadow->base.siblings_link), &shadow->on_hold);
     region_clear(&shadow->on_hold);
 }
 
-static inline int red_current_add(RedWorker *worker, Drawable *drawable)
+static inline int red_current_add(RedWorker *worker, Ring *ring, Drawable *drawable)
 {
     DrawItem *item = &drawable->tree_item;
 #ifdef RED_WORKER_STAT
     stat_time_t start_time = stat_now();
 #endif
     RingItem *now;
-    Ring *ring = &worker->current;
 
     print_base_item("ADD", &item->base);
     ASSERT(!region_is_empty(&item->base.rgn));
@@ -3430,12 +3604,11 @@ static inline Shadow *__new_shadow(RedWorker *worker, Drawable *item, SpicePoint
     return shadow;
 }
 
-static inline int red_current_add_with_shadow(RedWorker *worker, Drawable *item, SpicePoint *delta)
+static inline int red_current_add_with_shadow(RedWorker *worker, Ring *ring, Drawable *item, SpicePoint *delta)
 {
 #ifdef RED_WORKER_STAT
     stat_time_t start_time = stat_now();
 #endif
-    Ring *ring;
 
     Shadow *shadow = __new_shadow(worker, item, delta);
     if (!shadow) {
@@ -3444,7 +3617,6 @@ static inline int red_current_add_with_shadow(RedWorker *worker, Drawable *item,
     }
     print_base_item("ADDSHADOW", &item->tree_item.base);
     worker->current_size++;
-    ring = &worker->current;
     // item and his shadow must initially be placed in the same container.
     // for now putting them on root.
 #ifdef STREAM_TRACE
@@ -3518,7 +3690,7 @@ static inline void red_update_streamable(RedWorker *worker, Drawable *drawable,
 
 #endif
 
-static inline int red_current_add_qxl(RedWorker *worker, Drawable *drawable,
+static inline int red_current_add_qxl(RedWorker *worker, Ring *ring, Drawable *drawable,
                                       QXLDrawable *qxl_drawable)
 {
     int ret;
@@ -3531,12 +3703,12 @@ static inline int red_current_add_qxl(RedWorker *worker, Drawable *drawable,
 #endif
         delta.x = qxl_drawable->u.copy_bits.src_pos.x - qxl_drawable->bbox.left;
         delta.y = qxl_drawable->u.copy_bits.src_pos.y - qxl_drawable->bbox.top;
-        ret = red_current_add_with_shadow(worker, drawable, &delta);
+        ret = red_current_add_with_shadow(worker, ring, drawable, &delta);
     } else {
 #ifdef STREAM_TRACE
         red_update_streamable(worker, drawable, qxl_drawable);
 #endif
-        ret = red_current_add(worker, drawable);
+        ret = red_current_add(worker, ring, drawable);
     }
 #ifdef RED_WORKER_STAT
     if ((++worker->add_count % 100) == 0) {
@@ -3565,14 +3737,18 @@ static inline int red_current_add_qxl(RedWorker *worker, Drawable *drawable,
     return ret;
 }
 
-static void red_get_area(RedWorker *worker, const SpiceRect *area, uint8_t *dest, int dest_stride,
-                         int update)
+static void red_get_area(RedWorker *worker, int surface_id, const SpiceRect *area, uint8_t *dest,
+                         int dest_stride, int update)
 {
-    SpiceCanvas *canvas = worker->surface.context.canvas;
+    SpiceCanvas *canvas;
+    RedSurface *surface;
+
+    surface = &worker->surfaces[surface_id];
     if (update) {
-        red_update_area(worker, area);
+        red_update_area(worker, area, surface_id);
     }
 
+    canvas = surface->context.canvas;
     canvas->ops->read_bits(canvas, dest, dest_stride, area);
 }
 
@@ -3583,11 +3759,18 @@ static inline int red_handle_self_bitmap(RedWorker *worker, Drawable *drawable)
     int32_t height;
     uint8_t *dest;
     int dest_stride;
+    uint32_t surface_id;
+    RedSurface *surface;
 
     if (!drawable->qxl_drawable->self_bitmap) {
         return TRUE;
     }
 
+    surface_id = drawable->qxl_drawable->surface_id;
+    validate_surface(worker, surface_id);
+
+    surface = &worker->surfaces[surface_id];
+
     width = drawable->qxl_drawable->bbox.right - drawable->qxl_drawable->bbox.left;
     height = drawable->qxl_drawable->bbox.bottom - drawable->qxl_drawable->bbox.top;
     dest_stride = width * sizeof(uint32_t);
@@ -3599,7 +3782,7 @@ static inline int red_handle_self_bitmap(RedWorker *worker, Drawable *drawable)
     image->descriptor.flags = 0;
 
     QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_RED, ++worker->bits_unique);
-    image->bitmap.flags = QXL_BITMAP_DIRECT | (worker->surface.context.top_down ?
+    image->bitmap.flags = QXL_BITMAP_DIRECT | (surface->context.top_down ?
                                                QXL_BITMAP_TOP_DOWN : 0);
     image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
     image->bitmap.stride = dest_stride;
@@ -3608,7 +3791,8 @@ static inline int red_handle_self_bitmap(RedWorker *worker, Drawable *drawable)
     image->bitmap.data = (QXLPHYSICAL)dest;
     image->bitmap.palette = 0;
 
-    red_get_area(worker, &drawable->qxl_drawable->self_bitmap_area, dest, dest_stride, TRUE);
+    red_get_area(worker, surface_id,
+                 &drawable->qxl_drawable->self_bitmap_area, dest, dest_stride, TRUE);
 
     drawable->self_bitmap = (uint8_t *)image;
     return TRUE;
@@ -3628,6 +3812,7 @@ static void free_one_drawable(RedWorker *worker, int force_glz_free)
     }
     red_draw_drawable(worker, drawable);
     container = drawable->tree_item.base.container;
+
     current_remove_drawable(worker, drawable);
     container_cleanup(worker, container);
 }
@@ -3648,6 +3833,7 @@ static Drawable *get_drawable(RedWorker *worker, uint8_t effect, QXLDrawable *qx
     drawable->tree_item.base.id = ++worker->last_id;
 #endif
     ring_item_init(&drawable->list_link);
+    ring_item_init(&drawable->surface_list_link);
 #ifdef UPDATE_AREA_BY_TREE
     ring_item_init(&drawable->collect_link);
 #endif
@@ -3662,15 +3848,82 @@ static Drawable *get_drawable(RedWorker *worker, uint8_t effect, QXLDrawable *qx
     return drawable;
 }
 
+static inline int red_handle_depends_on_target_surface(RedWorker *worker, uint32_t surface_id)
+{
+    RedSurface *surface;
+    RingItem *ring_item;
+
+    validate_surface(worker, surface_id);
+    surface = &worker->surfaces[surface_id];
+
+    while ((ring_item = ring_get_tail(&surface->depend_on_me))) {
+        Drawable *drawable;
+        DependItem *depended_item = SPICE_CONTAINEROF(ring_item, DependItem, ring_item);
+        drawable = depended_item->drawable;
+        surface_flush(worker, drawable->qxl_drawable->surface_id, &drawable->qxl_drawable->bbox);
+    }
+
+    return TRUE;
+}
+
+static inline void add_to_surface_dependency(RedWorker *worker, int depend_on_surface_id,
+                                             DependItem *depend_item, Drawable *drawable)
+{
+    RedSurface *surface;
+
+    if (depend_on_surface_id == -1) {
+        depend_item->drawable = NULL;
+        return;
+    }
+
+    validate_surface(worker, depend_on_surface_id);
+    surface = &worker->surfaces[depend_on_surface_id];
+
+    depend_item->drawable = drawable;
+    ring_add(&surface->depend_on_me, &depend_item->ring_item);
+}
+
+static inline int red_handle_surfaces_dependencies(RedWorker *worker, Drawable *drawable)
+{
+    int x;
+
+    for (x = 0; x < 3; ++x) {
+        add_to_surface_dependency(worker, drawable->qxl_drawable->surfaces_dest[x],
+                                  &drawable->depend_items[x], drawable);
+    }
+
+    return TRUE;
+}
+
+static inline void red_inc_surfaces_drawable_dependencies(RedWorker *worker, QXLDrawable *drawable)
+{
+    int x;
+    int surface_id;
+    RedSurface *surface;
+
+    for (x = 0; x < 3; ++x) {
+        surface_id = drawable->surfaces_dest[x];
+        if (surface_id == -1) {
+            continue;
+        }
+        validate_surface(worker, surface_id);
+        surface = &worker->surfaces[surface_id];
+        surface->refs++;
+    }
+}
+
 static inline void red_process_drawable(RedWorker *worker, QXLDrawable *drawable, uint32_t group_id)
 {
+    int surface_id;
     Drawable *item = get_drawable(worker, drawable->effect, drawable, group_id);
 
     ASSERT(item);
-    PANIC_ON(!worker->surface.context.canvas);
-    ASSERT(worker->surface.refs != 0);
 
-    worker->surface.refs++;
+    surface_id = drawable->surface_id;
+    validate_surface(worker, surface_id);
+
+    ASSERT(worker->surfaces[surface_id].refs != 0);
+    worker->surfaces[surface_id].refs++;
 
     region_add(&item->tree_item.base.rgn, &drawable->bbox);
 #ifdef PIPE_DEBUG
@@ -3696,6 +3949,8 @@ static inline void red_process_drawable(RedWorker *worker, QXLDrawable *drawable
 #endif
     }
 
+    red_inc_surfaces_drawable_dependencies(worker, drawable);
+
     if (region_is_empty(&item->tree_item.base.rgn)) {
         release_drawable(worker, item);
         return;
@@ -3706,7 +3961,18 @@ static inline void red_process_drawable(RedWorker *worker, QXLDrawable *drawable
         return;
     }
 
-    if (red_current_add_qxl(worker, item, drawable)) {
+    if (!red_handle_depends_on_target_surface(worker, surface_id)) {
+        release_drawable(worker, item);
+        return;
+    }
+
+    if (!red_handle_surfaces_dependencies(worker, item)) {
+        release_drawable(worker, item);
+        return;
+    }
+
+    if (red_current_add_qxl(worker, &worker->surfaces[surface_id].current, item,
+                            drawable)) {
         worker->drawable_count++;
         if (item->tree_item.effect != QXL_EFFECT_OPAQUE) {
             worker->transparent_count++;
@@ -3719,6 +3985,49 @@ static inline void red_process_drawable(RedWorker *worker, QXLDrawable *drawable
     release_drawable(worker, item);
 }
 
+static inline void red_create_surface(RedWorker *worker, uint32_t surface_id,uint32_t width,
+                                      uint32_t height, int32_t stride, uint8_t depth, void *line_0);
+
+static inline void red_process_surface(RedWorker *worker, QXLSurfaceCmd *surface, uint32_t group_id)
+{
+    int surface_id;
+    RedSurface *red_surface;
+    uint8_t *data;
+
+    surface_id = surface->surface_id;
+    __validate_surface(worker, surface_id);
+
+    red_surface = &worker->surfaces[surface_id];
+
+    switch (surface->type) {
+    case QXL_SURFACE_CMD_CREATE: {
+        unsigned long saved_data = (unsigned long)surface->u.surface_create.data;
+        uint32_t height = surface->u.surface_create.height;
+        int32_t stride = surface->u.surface_create.stride;
+        QXLReleaseInfoExt release_info_ext;
+        
+        data = (uint8_t *)get_virt(worker, saved_data, height * abs(stride), group_id);
+        if (stride < 0) {
+            data -= (int32_t)(stride * (height - 1));
+        }
+        red_create_surface(worker, surface_id, surface->u.surface_create.width,
+                           height, stride, surface->u.surface_create.depth, data);
+        release_info_ext.group_id = group_id;
+        release_info_ext.info = &surface->release_info;
+        worker->qxl->release_resource(worker->qxl, release_info_ext);
+        break;
+    }
+    case QXL_SURFACE_CMD_DESTROY:
+        set_surface_release_info(worker, surface_id, &surface->release_info, group_id);
+        red_handle_depends_on_target_surface(worker, surface_id);
+        red_current_clear(worker, surface_id);
+        red_destroy_surface(worker, surface_id);
+        break;
+    default:
+            red_error("unknown surface command");
+    };
+}
+
 static void localize_path(RedWorker *worker, QXLPHYSICAL *in_path, uint32_t group_id)
 {
     QXLPath *path;
@@ -3850,6 +4159,26 @@ static LocalImage *alloc_local_image(RedWorker *worker)
     return &worker->local_images[worker->local_images_pos++];
 }
 
+static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces,
+                                       uint32_t surface_id)
+{
+    RedWorker *worker;
+
+    worker = SPICE_CONTAINEROF(surfaces, RedWorker, image_surfaces);
+    validate_surface(worker, surface_id);
+
+    return worker->surfaces[surface_id].context.canvas;
+}
+
+static void image_surface_init(RedWorker *worker)
+{
+    static SpiceImageSurfacesOps image_surfaces_ops = {
+        image_surfaces_get,
+    };
+
+    worker->image_surfaces.ops = &image_surfaces_ops;
+}
+
 static ImageCacheItem *image_cache_find(ImageCache *cache, uint64_t id)
 {
     ImageCacheItem *item = cache->hash_table[id % IMAGE_CACHE_HASH_SIZE];
@@ -4070,6 +4399,9 @@ static void localize_bitmap(RedWorker *worker, QXLPHYSICAL *in_bitmap, uint32_t
             local_image->bitmap.palette = (SPICE_ADDRESS)shadow_palette;
         }
         break;
+    case SPICE_IMAGE_TYPE_SURFACE: {
+        break;
+    }
     default:
         red_error("invalid image type");
     }
@@ -4095,6 +4427,7 @@ static void unlocalize_bitmap(QXLPHYSICAL *bitmap)
     case SPICE_IMAGE_TYPE_QUIC:
     case SPICE_IMAGE_TYPE_FROM_CACHE:
         *bitmap = 0;
+    case SPICE_IMAGE_TYPE_SURFACE:
         break;
     default:
         red_error("invalid image type %u", image->descriptor.type);
@@ -4154,8 +4487,16 @@ static void unlocalize_attr(SpiceLineAttr *attr)
 
 static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
 {
+    uint32_t surface_id;
+    RedSurface *surface;
+    SpiceCanvas *canvas; 
     SpiceClip clip = drawable->qxl_drawable->clip;
-    SpiceCanvas *canvas = worker->surface.context.canvas;
+
+    surface_id = drawable->qxl_drawable->surface_id;
+    validate_surface(worker, surface_id);
+
+    surface = &worker->surfaces[surface_id];
+    canvas = surface->context.canvas;
 
     worker->local_images_pos = 0;
     image_cache_eaging(&worker->image_cache);
@@ -4295,24 +4636,30 @@ static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable)
 static void red_draw_drawable(RedWorker *worker, Drawable *drawable)
 {
 #ifdef UPDATE_AREA_BY_TREE
-    SpiceCanvas *canvas = worker->surface.context.canvas;
+    SpiceCanvas *canvas;
+
+    canvas = surface->context.canvas;
     //todo: add need top mask flag
     canvas->ops->group_start(canvas,
                              &drawable->tree_item.base.rgn);
 #endif
+    red_flush_source_surfaces(worker, drawable);
     red_draw_qxl_drawable(worker, drawable);
 #ifdef UPDATE_AREA_BY_TREE
     canvas->ops->group_end(canvas);
 #endif
 }
 
-static void validate_area(RedWorker *worker, const SpiceRect *area)
+static void validate_area(RedWorker *worker, const SpiceRect *area, uint32_t surface_id)
 {
-    if (!worker->surface.context.canvas_draws_on_surface) {
-        SpiceCanvas *canvas = worker->surface.context.canvas;
+    RedSurface *surface;
+
+    surface = &worker->surfaces[surface_id];
+    if (!surface->context.canvas_draws_on_surface) {
+        SpiceCanvas *canvas = surface->context.canvas;
         int h;
-        int stride = worker->surface.context.stride;
-        uint8_t *line_0 = worker->surface.context.line_0;
+        int stride = surface->context.stride;
+        uint8_t *line_0 = surface->context.line_0;
 
         if (!(h = area->bottom - area->top)) {
             return;
@@ -4374,15 +4721,20 @@ static inline void __red_collect_for_update(RedWorker *worker, Ring *ring, RingI
     }
 }
 
-static void red_update_area(RedWorker *worker, const SpiceRect *area)
+static void red_update_area(RedWorker *worker, const SpiceRect *area, int surface_id)
 {
-    Ring *ring = &worker->current;
+    RedSurface *surface;
+    Ring *ring;
     RingItem *ring_item;
     Ring items;
     QRegion rgn;
 
+    validate_surface(worker, surface_id);
+    surface = &worker->surfaces[surface_id];
+    ring = &surface->current;
+
     if (!(ring_item = ring_get_head(ring))) {
-        worker->draw_context.validate_area(worker->draw_context.canvas,
+        worker->draw_context.validate_area(surface->context.canvas,
                                            &worker->dev_info.surface0_area, area);
         return;
     }
@@ -4403,23 +4755,34 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area)
         current_remove_drawable(worker, drawable);
         container_cleanup(worker, container);
     }
-    validate_area(worker, area);
+    validate_area(worker, area, surface_id);
 }
 
 #else
 
-static void red_update_area(RedWorker *worker, const SpiceRect *area)
+static void red_update_area(RedWorker *worker, const SpiceRect *area, int surface_id)
 {
-    Ring *ring = &worker->current_list;
-    RingItem *ring_item = ring;
+    RedSurface *surface;
+    Ring *ring;
+    RingItem *ring_item;
     QRegion rgn;
-    Drawable *last = NULL;
+    Drawable *last;
     Drawable *now;
+    int gn;
+
+    validate_surface(worker, surface_id);
+    surface = &worker->surfaces[surface_id];
+
+start_again:
+    last = NULL;
+    gn = ++surface->current_gn;
+    ring = &surface->current_list;
+    ring_item = ring;
 
     region_init(&rgn);
     region_add(&rgn, area);
     while ((ring_item = ring_next(ring, ring_item))) {
-        now = SPICE_CONTAINEROF(ring_item, Drawable, list_link);
+        now = SPICE_CONTAINEROF(ring_item, Drawable, surface_list_link);
         if (region_intersects(&rgn, &now->tree_item.base.rgn)) {
             last = now;
             break;
@@ -4428,21 +4791,26 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area)
     region_destroy(&rgn);
 
     if (!last) {
-        validate_area(worker, area);
+        validate_area(worker, area, surface_id);
         return;
     }
 
     do {
         Container *container;
 
-        ring_item = ring_get_tail(&worker->current_list);
-        now = SPICE_CONTAINEROF(ring_item, Drawable, list_link);
-        red_draw_drawable(worker, now);
+        ring_item = ring_get_tail(&surface->current_list);
+        now = SPICE_CONTAINEROF(ring_item, Drawable, surface_list_link);
+        now->refs++;
         container = now->tree_item.base.container;
         current_remove_drawable(worker, now);
         container_cleanup(worker, container);
+        red_draw_drawable(worker, now);
+        release_drawable(worker, now);
+        if (gn != surface->current_gn) {
+            goto start_again;
+        }
     } while (now != last);
-    validate_area(worker, area);
+    validate_area(worker, area, surface_id);
 }
 
 #endif
@@ -4635,7 +5003,7 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size)
             QXLUpdateCmd *draw_cmd = (QXLUpdateCmd *)get_virt(worker, ext_cmd.cmd.data,
                                                               sizeof(QXLUpdateCmd),
                                                               ext_cmd.group_id);
-            red_update_area(worker, &draw_cmd->area);
+            red_update_area(worker, &draw_cmd->area, draw_cmd->surface_id);
             worker->qxl->notify_update(worker->qxl, draw_cmd->update_id);
             release_info_ext.group_id = ext_cmd.group_id;
             release_info_ext.info = &draw_cmd->release_info;
@@ -4652,6 +5020,12 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size)
             worker->qxl->release_resource(worker->qxl, release_info_ext);
             break;
         }
+        case QXL_CMD_SURFACE: {
+            QXLSurfaceCmd *surface = (QXLSurfaceCmd *)get_virt(worker, ext_cmd.cmd.data,
+                                                               sizeof(QXLSurfaceCmd), ext_cmd.group_id);
+            red_process_surface(worker, surface, ext_cmd.group_id);
+            break;
+        }
         default:
             red_error("bad command type");
         }
@@ -4687,40 +5061,43 @@ static void red_free_some(RedWorker *worker)
     }
 }
 
-static void red_current_flush(RedWorker *worker)
+static void red_current_flush(RedWorker *worker, int surface_id)
 {
     while (!ring_is_empty(&worker->current_list)) {
         free_one_drawable(worker, FALSE);
     }
-    red_current_clear(worker);
+    red_current_clear(worker, surface_id);
 }
 
-static void red_add_screen_image(RedWorker *worker)
+static void red_add_surface_image(RedWorker *worker, int surface_id)
 {
     ImageItem *item;
     int stride;
     SpiceRect area;
-    SpiceCanvas *canvas = worker->surface.context.canvas;
+    SpiceCanvas *canvas = worker->surfaces[surface_id].context.canvas;
 
-    if (!worker->display_channel || !canvas) {
+    if (!worker->display_channel || !worker->surfaces[surface_id].context.canvas) {
         return;
     }
 
-    stride = worker->surface.context.width << 2;
-    item = (ImageItem *)spice_malloc_n_m(worker->surface.context.height, stride, sizeof(ImageItem));
+    stride = abs(worker->surfaces[surface_id].context.stride);
+
+    item = (ImageItem *)spice_malloc_n_m(worker->surfaces[surface_id].context.height, stride,
+                                         sizeof(ImageItem));
 
     red_pipe_item_init(&item->link, PIPE_ITEM_TYPE_IMAGE);
 
     item->refs = 1;
+    item->surface_id = surface_id;
     item->pos.x = item->pos.y = 0;
-    item->width = worker->surface.context.width;
-    item->height = worker->surface.context.height;
+    item->width = worker->surfaces[surface_id].context.width;
+    item->height = worker->surfaces[surface_id].context.height;
     item->stride = stride;
-    item->top_down = worker->surface.context.top_down;
+    item->top_down = worker->surfaces[surface_id].context.top_down;
 
     area.top = area.left = 0;
-    area.right = worker->surface.context.width;
-    area.bottom = worker->surface.context.height;
+    area.right = worker->surfaces[surface_id].context.width;
+    area.bottom = worker->surfaces[surface_id].context.height;
     canvas->ops->read_bits(canvas, item->data, stride, &area);
     red_pipe_add_image_item(worker, item);
     release_image_item(item);
@@ -6047,6 +6424,23 @@ static void fill_bits(DisplayChannel *display_channel, QXLPHYSICAL *in_bitmap, D
     }
 
     switch (qxl_image->descriptor.type) {
+    case SPICE_IMAGE_TYPE_SURFACE: {
+        int surface_id;
+        RedSurface *surface;
+
+        surface_id = qxl_image->surface_image.surface_id;
+        validate_surface(worker, surface_id);
+
+        surface = &worker->surfaces[surface_id];
+        image->descriptor.type = SPICE_IMAGE_TYPE_SURFACE;
+        image->descriptor.flags = 0;
+        image->descriptor.width = surface->context.width;
+        image->descriptor.height = surface->context.height;
+
+        image->surface.surface_id = surface_id;
+        add_buf(channel, BUF_TYPE_RAW, image, sizeof(SpiceSurfaceImage), 0, 0);
+        break;
+    }
     case SPICE_IMAGE_TYPE_BITMAP:
 #ifdef DUMP_BITMAP
         dump_bitmap(display_channel->base.worker, &qxl_image->bitmap, drawable->group_id);
@@ -6214,7 +6608,7 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_FILL:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_FILL;
         fill_base(display_channel, &display_channel->send_data.u.fill.base, item,
-                  sizeof(SpiceMsgDisplayDrawFill), 0);
+                  sizeof(SpiceMsgDisplayDrawFill), drawable->surface_id);
         display_channel->send_data.u.fill.data = drawable->u.fill;
         fill_brush(display_channel, &display_channel->send_data.u.fill.data.brush, item);
         fill_mask(display_channel, &display_channel->send_data.u.fill.data.mask, item);
@@ -6222,7 +6616,7 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_OPAQUE:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_OPAQUE;
         fill_base(display_channel, &display_channel->send_data.u.opaque.base, item,
-                  sizeof(SpiceMsgDisplayDrawOpaque), 0);
+                  sizeof(SpiceMsgDisplayDrawOpaque), drawable->surface_id);
         display_channel->send_data.u.opaque.data = drawable->u.opaque;
         fill_bits(display_channel, &display_channel->send_data.u.opaque.data.src_bitmap, item);
         fill_brush(display_channel, &display_channel->send_data.u.opaque.data.brush, item);
@@ -6231,7 +6625,7 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_COPY:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_COPY;
         fill_base(display_channel, &display_channel->send_data.u.copy.base, item,
-                  sizeof(SpiceMsgDisplayDrawCopy), 0);
+                  sizeof(SpiceMsgDisplayDrawCopy), drawable->surface_id);
         display_channel->send_data.u.copy.data = drawable->u.copy;
         fill_bits(display_channel, &display_channel->send_data.u.copy.data.src_bitmap, item);
         fill_mask(display_channel, &display_channel->send_data.u.copy.data.mask, item);
@@ -6239,27 +6633,27 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_TRANSPARENT:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_TRANSPARENT;
         fill_base(display_channel, &display_channel->send_data.u.transparent.base, item,
-                  sizeof(SpiceMsgDisplayDrawTransparent), 0);
+                  sizeof(SpiceMsgDisplayDrawTransparent), drawable->surface_id);
         display_channel->send_data.u.transparent.data = drawable->u.transparent;
         fill_bits(display_channel, &display_channel->send_data.u.transparent.data.src_bitmap, item);
         break;
     case QXL_DRAW_ALPHA_BLEND:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND;
         fill_base(display_channel, &display_channel->send_data.u.alpha_blend.base, item,
-                  sizeof(SpiceMsgDisplayDrawAlphaBlend), 0);
+                  sizeof(SpiceMsgDisplayDrawAlphaBlend), drawable->surface_id);
         display_channel->send_data.u.alpha_blend.data = drawable->u.alpha_blend;
         fill_bits(display_channel, &display_channel->send_data.u.alpha_blend.data.src_bitmap, item);
         break;
     case QXL_COPY_BITS:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_COPY_BITS;
         fill_base(display_channel, &display_channel->send_data.u.copy_bits.base, item,
-                  sizeof(SpiceMsgDisplayCopyBits), 0);
+                  sizeof(SpiceMsgDisplayCopyBits), drawable->surface_id);
         display_channel->send_data.u.copy_bits.src_pos = drawable->u.copy_bits.src_pos;
         break;
     case QXL_DRAW_BLEND:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_BLEND;
         fill_base(display_channel, &display_channel->send_data.u.blend.base, item,
-                  sizeof(SpiceMsgDisplayDrawBlend), 0);
+                  sizeof(SpiceMsgDisplayDrawBlend), drawable->surface_id);
         display_channel->send_data.u.blend.data = drawable->u.blend;
         fill_bits(display_channel, &display_channel->send_data.u.blend.data.src_bitmap, item);
         fill_mask(display_channel, &display_channel->send_data.u.blend.data.mask, item);
@@ -6267,28 +6661,28 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_BLACKNESS:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_BLACKNESS;
         fill_base(display_channel, &display_channel->send_data.u.blackness.base, item,
-                  sizeof(SpiceMsgDisplayDrawBlackness), 0);
+                  sizeof(SpiceMsgDisplayDrawBlackness), drawable->surface_id);
         display_channel->send_data.u.blackness.data = drawable->u.blackness;
         fill_mask(display_channel, &display_channel->send_data.u.blackness.data.mask, item);
         break;
     case QXL_DRAW_WHITENESS:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_WHITENESS;
         fill_base(display_channel, &display_channel->send_data.u.whiteness.base, item,
-                  sizeof(SpiceMsgDisplayDrawWhiteness), 0);
+                  sizeof(SpiceMsgDisplayDrawWhiteness), drawable->surface_id);
         display_channel->send_data.u.whiteness.data = drawable->u.whiteness;
         fill_mask(display_channel, &display_channel->send_data.u.whiteness.data.mask, item);
         break;
     case QXL_DRAW_INVERS:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_INVERS;
         fill_base(display_channel, &display_channel->send_data.u.invers.base, item,
-                  sizeof(SpiceMsgDisplayDrawInvers), 0);
+                  sizeof(SpiceMsgDisplayDrawInvers), drawable->surface_id);
         display_channel->send_data.u.invers.data = drawable->u.invers;
         fill_mask(display_channel, &display_channel->send_data.u.invers.data.mask, item);
         break;
     case QXL_DRAW_ROP3:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_ROP3;
         fill_base(display_channel, &display_channel->send_data.u.rop3.base, item,
-                  sizeof(SpiceMsgDisplayDrawRop3), 0);
+                  sizeof(SpiceMsgDisplayDrawRop3), drawable->surface_id);
         display_channel->send_data.u.rop3.data = drawable->u.rop3;
         fill_bits(display_channel, &display_channel->send_data.u.rop3.data.src_bitmap, item);
         fill_brush(display_channel, &display_channel->send_data.u.rop3.data.brush, item);
@@ -6297,7 +6691,7 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_STROKE:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_STROKE;
         fill_base(display_channel, &display_channel->send_data.u.stroke.base, item,
-                  sizeof(SpiceMsgDisplayDrawStroke), 0);
+                  sizeof(SpiceMsgDisplayDrawStroke), drawable->surface_id);
         display_channel->send_data.u.stroke.data = drawable->u.stroke;
         fill_path(display_channel, &display_channel->send_data.u.stroke.data.path, item->group_id);
         fill_attr(display_channel, &display_channel->send_data.u.stroke.data.attr, item->group_id);
@@ -6306,7 +6700,7 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_TEXT:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_TEXT;
         fill_base(display_channel, &display_channel->send_data.u.text.base, item,
-                  sizeof(SpiceMsgDisplayDrawText), 0);
+                  sizeof(SpiceMsgDisplayDrawText), drawable->surface_id);
         display_channel->send_data.u.text.data = drawable->u.text;
         fill_brush(display_channel, &display_channel->send_data.u.text.data.fore_brush, item);
         fill_brush(display_channel, &display_channel->send_data.u.text.data.back_brush, item);
@@ -6500,7 +6894,7 @@ static inline void display_begin_send_massage(DisplayChannel *channel, void *ite
 {
     FreeList *free_list = &channel->send_data.free_list;
 
-    if (free_list->res->count) {
+    if (0 && free_list->res->count) {
         int sync_count = 0;
         int sub_index;
         int i;
@@ -6923,7 +7317,7 @@ static void red_send_image(DisplayChannel *display_channel, ImageItem *item)
     channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_COPY;
 
     add_buf(channel, BUF_TYPE_RAW, &display_channel->send_data.u.copy, sizeof(SpiceMsgDisplayDrawCopy), 0, 0);
-    display_channel->send_data.u.copy.base.surface_id = 0;
+    display_channel->send_data.u.copy.base.surface_id = item->surface_id;
     display_channel->send_data.u.copy.base.box.left = item->pos.x;
     display_channel->send_data.u.copy.base.box.top = item->pos.y;
     display_channel->send_data.u.copy.base.box.right = item->pos.x + bitmap.x;
@@ -7172,8 +7566,6 @@ static void red_send_surface_create(DisplayChannel *display, SpiceMsgSurfaceCrea
     add_buf(channel, BUF_TYPE_RAW, &display->send_data.u.surface_create,
             sizeof(SpiceMsgSurfaceCreate), 0, 0);
 
-    display->surface_client_created = TRUE;
-
     red_begin_send_massage(channel, NULL);
 }
 
@@ -7190,7 +7582,6 @@ static void red_send_surface_destroy(DisplayChannel *display, uint32_t surface_i
     add_buf(channel, BUF_TYPE_RAW, &display->send_data.u.surface_destroy,
             sizeof(SpiceMsgSurfaceDestroy), 0, 0);
 
-    display->surface_client_created = FALSE;
     red_begin_send_massage(channel, NULL);
 }
 
@@ -7416,11 +7807,18 @@ static void __show_tree_call(TreeItem *item, void *data)
 
 void red_show_tree(RedWorker *worker)
 {
+    int x;
+
     ShowTreeData show_tree_data;
     show_tree_data.worker = worker;
     show_tree_data.level = 0;
     show_tree_data.container = NULL;
-    current_tree_for_each(worker, __show_tree_call, &show_tree_data);
+    for (x = 0; x < NUM_SURFACES; ++x) {
+        if (worker->surfaces[x].context.canvas) {
+            current_tree_for_each(worker, &worker->surfaces[x].current, __show_tree_call,
+                                  &show_tree_data);
+        }
+    }
 }
 
 static inline int channel_is_connected(RedChannel *channel)
@@ -7491,7 +7889,8 @@ static SpiceCanvas *create_cairo_context(RedWorker *worker, uint32_t width, uint
     if (surface == NULL) {
         red_error("create cairo surface failed");
     }
-    canvas = canvas_create(surface, depth, &worker->image_cache.base, NULL,
+    canvas = canvas_create(surface, depth, &worker->image_cache.base,
+                           &worker->image_surfaces, NULL,
                            &worker->preload_group_virt_mapping);
     pixman_image_unref (surface);
     return canvas;
@@ -7503,7 +7902,8 @@ static SpiceCanvas *create_ogl_context_common(RedWorker *worker, OGLCtx *ctx, ui
     SpiceCanvas *canvas;
 
     oglctx_make_current(ctx);
-    if (!(canvas = gl_canvas_create(width, height, depth, &worker->image_cache.base, NULL,
+    if (!(canvas = gl_canvas_create(width, height, depth, &worker->image_cache.base,
+                                    &worker->image_surfaces, NULL,
                                     &worker->preload_group_virt_mapping))) {
         return NULL;
     }
@@ -7593,34 +7993,46 @@ static SurfaceCreateItem *get_surface_create_item(uint32_t surface_id, uint32_t
     return create;
 }
 
-static inline void red_create_surface_item(RedWorker *worker, RedSurface *surface)
+static inline void __red_create_surface_item(RedWorker *worker, int surface_id, uint32_t flags)
 {
+    RedSurface *surface;
     SurfaceCreateItem *create;
 
-    if (!surface->context.canvas) {
-        return;
-    }
-
     if (!worker->display_channel) {
         return;
     }
 
-    create = get_surface_create_item(0, surface->context.width, surface->context.height,
-                                     surface->context.depth, SPICE_SURFACE_FLAGS_PRIMARY);
+    surface = &worker->surfaces[surface_id];
+
+    create = get_surface_create_item(surface_id, surface->context.width, surface->context.height,
+                                     surface->context.depth, flags);
+
+    worker->display_channel->surface_client_created[surface_id] = TRUE;
+
     red_pipe_add(&worker->display_channel->base, &create->pipe_item);
 }
 
-static inline void red_create_surface(RedWorker *worker, uint32_t surface_id,uint32_t width,
+static inline void red_create_surface_item(RedWorker *worker, int surface_id)
+{
+    if (is_primary_surface(worker, surface_id)) {
+        __red_create_surface_item(worker, surface_id, SPICE_SURFACE_FLAGS_PRIMARY);
+    } else {
+        __red_create_surface_item(worker, surface_id, 0);
+    } 
+}
+
+static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, uint32_t width,
                                       uint32_t height, int32_t stride, uint8_t depth, void *line_0)
 {
     uint32_t i;
-    RedSurface *surface = &worker->surface;
-
+    RedSurface *surface = &worker->surfaces[surface_id];
     if (stride >= 0) {
         PANIC("Untested path stride >= 0");
     }
+    if (depth != 16 && depth != 32) {
+        PANIC("As for now support just 32/16 depth surfaces");
+    }
     PANIC_ON(surface->context.canvas);
-    ASSERT(surface_id == 0);
 
     surface->context.canvas_draws_on_surface = FALSE;
     surface->context.width = width;
@@ -7628,8 +8040,12 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id,uin
     surface->context.depth = depth;
     surface->context.stride = stride;
     surface->context.line_0 = line_0;
+    memset(line_0 + (int32_t)(stride * (height - 1)), 0, height*abs(stride));
+    surface->release_info = NULL;
+    ring_init(&surface->current);
+    ring_init(&surface->current_list);
+    ring_init(&surface->depend_on_me);
     surface->refs = 1;
-
     if (worker->renderer != RED_RENDERER_INVALID) {
         surface->context.canvas = create_canvas_for_surface(worker, surface, worker->renderer,
                                                             width, height, stride,
@@ -7637,7 +8053,8 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id,uin
         if (!surface->context.canvas) {
             PANIC("drawing canvas creating failed - can`t create same type canvas");
         }
-        red_create_surface_item(worker, surface);
+
+        red_create_surface_item(worker, surface_id);
         return;
     }
 
@@ -7645,9 +8062,9 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id,uin
         surface->context.canvas = create_canvas_for_surface(worker, surface, worker->renderers[i],
                                                             width, height, stride,
                                                             surface->context.depth, line_0);
-        if (surface->context.canvas) {
+        if (surface->context.canvas) { //no need canvas check
             worker->renderer = worker->renderers[i];
-            red_create_surface_item(worker, surface);
+            red_create_surface_item(worker, surface_id);
             return;
         }
     }
@@ -7707,7 +8124,7 @@ static void push_new_primary_surface(RedWorker *worker)
     if ((display_channel = worker->display_channel)) {
         red_pipe_add_type(&display_channel->base, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
         if (!display_channel->base.migrate) {
-            red_create_surface_item(worker, &worker->surface);
+            red_create_surface_item(worker, 0);
         }
         display_channel_push(worker);
     }
@@ -7758,10 +8175,10 @@ static void on_new_display_channel(RedWorker *worker)
         return;
     }
     display_channel->base.messages_window = 0;
-    if (worker->surface.context.canvas) {
-        red_current_flush(worker);
+    if (worker->surfaces[0].context.canvas) {
+        red_current_flush(worker, 0);
         push_new_primary_surface(worker);
-        red_add_screen_image(worker);
+        red_add_surface_image(worker, 0);
         if (channel_is_connected(&display_channel->base)) {
             red_pipe_add_verb(&display_channel->base, SPICE_MSG_DISPLAY_MARK);
             red_disply_start_streams(display_channel);
@@ -8359,7 +8776,7 @@ static void on_new_cursor_channel(RedWorker *worker)
 
     channel->base.messages_window = 0;
     red_pipe_add_type(&channel->base, PIPE_ITEM_TYPE_SET_ACK);
-    if (worker->surface.context.canvas && !channel->base.migrate) {
+    if (worker->surfaces[0].context.canvas && !channel->base.migrate) {
         red_pipe_add_type(&worker->cursor_channel->base, PIPE_ITEM_TYPE_CURSOR_INIT);
     }
 }
@@ -8551,12 +8968,17 @@ static void red_wait_outgoiong_item(RedChannel *channel)
 static inline void handle_dev_update(RedWorker *worker)
 {
     RedWorkeMessage message;
+    const SpiceRect *rect;
+    uint32_t *surface_id;
 
-    ASSERT(worker->surface.context.canvas && worker->running);
+    //ASSERT(worker->surfaces[0].context.canvas && worker->running);
 
     flush_display_commands(worker);
 
-    red_update_area(worker, worker->qxl->get_update_area(worker->qxl));
+    worker->qxl->get_update_area(worker->qxl, &rect, &surface_id);
+    ASSERT(worker->running);
+    validate_surface(worker, *surface_id);
+    red_update_area(worker, rect, *surface_id);
     message = RED_WORKER_MESSAGE_READY;
     write_message(worker->channel, &message);
 }
@@ -8596,12 +9018,14 @@ static inline void handle_dev_del_memslot(RedWorker *worker)
     worker->mem_slots[slot_group_id][slot_id].virt_end_addr = 0;
 }
 
-static inline void destroy_surface_wait(RedWorker *worker)
+static inline void destroy_surface_wait(RedWorker *worker, int surface_id)
 {
-    flush_display_commands(worker);
+    if (worker->surfaces[surface_id].context.canvas) {
+        red_handle_depends_on_target_surface(worker, surface_id);
+    }
     red_flush_surface_pipe(worker);
     red_display_clear_glz_drawables(worker->display_channel);
-    red_current_clear(worker);
+    red_current_clear(worker, surface_id);
     red_wait_outgoiong_item((RedChannel *)worker->display_channel);
     if (worker->display_channel) {
         ASSERT(!worker->display_channel->base.send_data.item);
@@ -8617,8 +9041,9 @@ static inline void handle_dev_destroy_surface_wait(RedWorker *worker)
 
     ASSERT(surface_id == 0);
 
-    if (worker->surface.context.canvas) {
-        destroy_surface_wait(worker);
+    flush_display_commands(worker);
+    if (worker->surfaces[0].context.canvas) {
+        destroy_surface_wait(worker, 0);
     }
 
     message = RED_WORKER_MESSAGE_READY;
@@ -8627,10 +9052,22 @@ static inline void handle_dev_destroy_surface_wait(RedWorker *worker)
 
 static inline void handle_dev_destroy_surfaces(RedWorker *worker)
 {
+    int i;
     RedWorkeMessage message;
 
-    destroy_surface_wait(worker);
-    __red_destroy_surface(worker);
+    flush_display_commands(worker);
+    //to handle better
+    if (worker->surfaces[0].context.canvas) {
+        destroy_surface_wait(worker, 0);
+    }
+    for (i = 0; i < NUM_SURFACES; ++i) {
+        if (worker->surfaces[i].context.canvas) {
+            destroy_surface_wait(worker, i);
+            if (worker->surfaces[i].context.canvas) {
+                __red_destroy_surface(worker, i);
+            }
+        }
+    }
     ASSERT(ring_is_empty(&worker->streams));
 
     red_wait_outgoiong_item((RedChannel *)worker->cursor_channel);
@@ -8650,7 +9087,10 @@ static inline void handle_dev_destroy_surfaces(RedWorker *worker)
         }
     }
 
-    ASSERT(!worker->surface.context.canvas);
+    //to handle better
+    for (i = 0; i < NUM_SURFACES; ++i) {
+        ASSERT(!worker->surfaces[i].context.canvas);
+    }
 
     worker->cursor_visible = TRUE;
     worker->cursor_position.x = worker->cursor_position.y = 0;
@@ -8685,7 +9125,6 @@ static inline void handle_dev_create_primary_surface(RedWorker *worker)
                        line_0);
 
     if (worker->display_channel) {
-        red_create_surface_item(worker, &worker->surface);
         red_pipe_add_verb(&worker->display_channel->base, SPICE_MSG_DISPLAY_MARK);
         display_channel_push(worker);
     }
@@ -8721,12 +9160,12 @@ static inline void handle_dev_destroy_primary_surface(RedWorker *worker)
         ASSERT(!worker->cursor_channel->base.send_data.item);
     }
 
-
-    destroy_surface_wait(worker);
-    red_destroy_surface(worker);
+    flush_display_commands(worker);
+    destroy_surface_wait(worker, 0);
+    red_destroy_surface(worker, 0);
     ASSERT(ring_is_empty(&worker->streams));
 
-    ASSERT(!worker->surface.context.canvas);
+    ASSERT(!worker->surfaces[surface_id].context.canvas);
 
     worker->cursor_visible = TRUE;
     worker->cursor_position.x = worker->cursor_position.y = 0;
@@ -8845,17 +9284,23 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
     case RED_WORKER_MESSAGE_LOAD:
         red_printf("load");
         ASSERT(!worker->running);
-        red_add_screen_image(worker);
+        red_add_surface_image(worker, 0);
         red_cursor_load(worker);
         message = RED_WORKER_MESSAGE_READY;
         write_message(worker->channel, &message);
         break;
-    case RED_WORKER_MESSAGE_STOP: {
+        case RED_WORKER_MESSAGE_STOP: {
+        int x;
+
         red_printf("stop");
         ASSERT(worker->running);
         worker->running = FALSE;
         red_display_clear_glz_drawables(worker->display_channel);
-        red_current_flush(worker);
+        for (x = 0; x < NUM_SURFACES; ++x) {
+            if (worker->surfaces->context.canvas) {
+                red_current_flush(worker, x);
+            }
+        }
         red_cursor_flush(worker);
         red_wait_outgoiong_item((RedChannel *)worker->display_channel);
         red_wait_outgoiong_item((RedChannel *)worker->cursor_channel);
@@ -8997,8 +9442,8 @@ static void red_init(RedWorker *worker, WorkerInitData *init_data)
     worker->image_compression = init_data->image_compression;
     worker->streaming_video = init_data->streaming_video;
     ring_init(&worker->current_list);
-    ring_init(&worker->current);
     image_cache_init(&worker->image_cache);
+    image_surface_init(worker);
     drawables_init(worker);
     cursor_items_init(worker);
     red_init_streams(worker);
@@ -9029,6 +9474,8 @@ static void red_init(RedWorker *worker, WorkerInitData *init_data)
     worker->generation_bits = init_data->memslot_gen_bits;
     worker->mem_slot_bits = init_data->memslot_id_bits;
     worker->internal_groupslot_id = init_data->internal_groupslot_id;
+    PANIC_ON(init_data->n_surfaces > NUM_SURFACES);
+    worker->n_surfaces = init_data->n_surfaces;
     red_create_mem_slots(worker);
 
     worker->preload_group_virt_mapping.ops = &preload_group_virt_mapping_ops;
diff --git a/server/red_worker.h b/server/red_worker.h
index 141caee..54577db 100644
--- a/server/red_worker.h
+++ b/server/red_worker.h
@@ -96,6 +96,7 @@ typedef struct WorkerInitData {
     uint8_t memslot_gen_bits;
     uint8_t memslot_id_bits;
     uint8_t internal_groupslot_id;
+    uint32_t n_surfaces;
 } WorkerInitData;
 
 void *red_worker_main(void *arg);
diff --git a/server/vd_interface.h b/server/vd_interface.h
index 674fae0..6d76b9e 100644
--- a/server/vd_interface.h
+++ b/server/vd_interface.h
@@ -153,6 +153,7 @@ typedef struct QXLDevInitInfo {
     uint8_t memslot_id_bits;
     uint32_t qxl_ram_size;
     uint8_t internal_groupslot_id;
+    uint32_t n_surfaces;
 } QXLDevInitInfo;
 
 struct QXLDevMemSlot {
@@ -178,6 +179,8 @@ struct QXLDevSurfaceCreate {
     uint32_t group_id;
 };
 
+struct SpiceRect;
+
 struct QXLInterface {
     VDInterface base;
 
@@ -196,7 +199,7 @@ struct QXLInterface {
     void (*release_resource)(QXLInterface *qxl, struct QXLReleaseInfoExt release_info);
     int (*get_cursor_command)(QXLInterface *qxl, struct QXLCommandExt *cmd);
     int (*req_cursor_notification)(QXLInterface *qxl);
-    const struct SpiceRect *(*get_update_area)(QXLInterface *qxl);
+    void (*get_update_area)(QXLInterface *qxl, const struct SpiceRect **rect, uint32_t **surface_id);
     void (*notify_update)(QXLInterface *qxl, uint32_t update_id);
     void (*set_save_data)(QXLInterface *qxl, void *data, int size);
     void *(*get_save_data)(QXLInterface *qxl);
commit 27a8ec1ef476a13734c33f01653af22cc0432800
Author: Izik Eidus <ieidus at redhat.com>
Date:   Sat Apr 3 05:34:48 2010 +0300

    spice server: surface create/destroy protocol support
    
    Now we can send commands from the server to the client
    to destroy surfaces (right now just the primary surface)
    
    Needed for offscreens support)
    
    Another patch`s on the way.
    
    Signed-off-by: Izik Eidus <ieidus at redhat.com>

diff --git a/client/debug.h b/client/debug.h
index 1c6f6a4..ce8fc98 100644
--- a/client/debug.h
+++ b/client/debug.h
@@ -92,6 +92,11 @@ LOG4CPP_LOGGER("spice")
     ON_PANIC();                             \
 }
 
+#define PANIC_ON(x) if ((x)) {                      \
+    LOG(FATAL, "%s panic %s\n", __FUNCTION__, #x);  \
+    ON_PANIC();                                     \
+}
+
 #define DBGLEVEL 1000
 
 #define DBG(level, format, ...) {               \
diff --git a/client/display_channel.cpp b/client/display_channel.cpp
index f303f57..0d9d20c 100644
--- a/client/display_channel.cpp
+++ b/client/display_channel.cpp
@@ -44,9 +44,9 @@
 
 static Mutex avcodec_mutex;
 
-class SetModeEvent: public SyncEvent {
+class CreatePrimarySurfaceEvent: public SyncEvent {
 public:
-    SetModeEvent(DisplayChannel& channel, int width, int height, int depth)
+   CreatePrimarySurfaceEvent(DisplayChannel& channel, int width, int height, int depth)
         : _channel (channel)
         , _width (width)
         , _height (height)
@@ -57,7 +57,6 @@ public:
     virtual void do_response(AbstractProcessLoop& events_loop)
     {
         Application* app = (Application*)events_loop.get_owner();
-        _channel.destroy_canvas();
         _channel.screen()->lock_size();
         _channel.screen()->resize(_width, _height);
         _channel.create_canvas(app->get_canvas_types(), _width, _height, _depth);
@@ -70,6 +69,22 @@ private:
     int _depth;
 };
 
+class DestroyPrimarySurfaceEvent: public SyncEvent {
+public:
+    DestroyPrimarySurfaceEvent(DisplayChannel& channel)
+        : _channel (channel)
+    {
+    }
+
+    virtual void do_response(AbstractProcessLoop& events_loop)
+    {
+        _channel.destroy_canvas();
+    }
+
+private:
+    DisplayChannel& _channel;
+};
+
 class UnlockScreenEvent: public Event {
 public:
     UnlockScreenEvent(RedScreen* screen)
@@ -658,7 +673,6 @@ DisplayChannel::DisplayChannel(RedClient& client, uint32_t id,
                          sizeof(SpiceMsgDisconnect));
     handler->set_handler(SPICE_MSG_NOTIFY, &DisplayChannel::handle_notify, sizeof(SpiceMsgNotify));
 
-    handler->set_handler(SPICE_MSG_DISPLAY_MODE, &DisplayChannel::handle_mode, sizeof(SpiceMsgDisplayMode));
     handler->set_handler(SPICE_MSG_DISPLAY_MARK, &DisplayChannel::handle_mark, 0);
     handler->set_handler(SPICE_MSG_DISPLAY_RESET, &DisplayChannel::handle_reset, 0);
 
@@ -682,6 +696,11 @@ DisplayChannel::DisplayChannel(RedClient& client, uint32_t id,
     handler->set_handler(SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL,
                          &DisplayChannel::handle_stream_destroy_all, 0);
 
+    handler->set_handler(SPICE_MSG_DISPLAY_SURFACE_CREATE, &DisplayChannel::handle_surface_create,
+                         sizeof(SpiceMsgSurfaceCreate));
+    handler->set_handler(SPICE_MSG_DISPLAY_SURFACE_DESTROY, &DisplayChannel::handle_surface_destroy,
+                         sizeof(SpiceMsgSurfaceDestroy));
+
     get_process_loop().add_trigger(_streams_trigger);
 #ifdef USE_OGL
     get_process_loop().add_trigger(_gl_interrupt_recreate);
@@ -1222,44 +1241,6 @@ void DisplayChannel::create_canvas(const std::vector<int>& canvas_types, int wid
     set_draw_handlers();
 }
 
-void DisplayChannel::handle_mode(RedPeer::InMessage* message)
-{
-    SpiceMsgDisplayMode *mode = (SpiceMsgDisplayMode *)message->data();
-
-    _mark = false;
-    attach_to_screen(get_client().get_application(), get_id());
-    clear_area();
-#ifdef USE_OGL
-    if (screen()) {
-        if (_canvas.get()) {
-            if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
-                screen()->unset_type_gl();
-                screen()->untouch_context();
-            }
-        }
-    }
-#endif
-    AutoRef<SetModeEvent> event(new SetModeEvent(*this, mode->x_res,
-                                                 mode->y_res, mode->bits));
-    get_client().push_event(*event);
-    (*event)->wait();
-    if (!(*event)->success()) {
-        THROW("set mode failed");
-    }
-
-    _x_res = mode->x_res;
-    _y_res = mode->y_res;
-    _depth = mode->bits;
-
-#ifdef USE_OGL
-    if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
-        ((GCanvas *)(_canvas.get()))->touch_context();
-        screen()->set_update_interrupt_trigger(&_interrupt_update);
-        screen()->set_type_gl();
-    }
-#endif
-}
-
 void DisplayChannel::handle_mark(RedPeer::InMessage *message)
 {
     _mark = true;
@@ -1361,6 +1342,7 @@ void DisplayChannel::handle_stream_create(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayStreamCreate* stream_create = (SpiceMsgDisplayStreamCreate*)message->data();
 
+    PANIC_ON(stream_create->surface_id != 0);
     Lock lock(_streams_lock);
     if (_streams.size() <= stream_create->id) {
         _streams.resize(stream_create->id + 1);
@@ -1459,6 +1441,71 @@ void DisplayChannel::handle_stream_destroy_all(RedPeer::InMessage* message)
     destroy_strams();
 }
 
+void DisplayChannel::create_primary_surface(int width, int height, int depth)
+{
+   _mark = false;
+    attach_to_screen(get_client().get_application(), get_id());
+    clear_area();
+
+    AutoRef<CreatePrimarySurfaceEvent> event(new CreatePrimarySurfaceEvent(*this, width, height,
+                                                                           depth));
+    get_client().push_event(*event);
+    (*event)->wait();
+    if (!(*event)->success()) {
+        THROW("Create primary surface failed");
+    }
+
+    _x_res = width;
+    _y_res = height;
+    _depth = depth;
+
+#ifdef USE_OGL
+    if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+        ((GCanvas *)(_canvas.get()))->touch_context();
+        screen()->set_update_interrupt_trigger(&_interrupt_update);
+        screen()->set_type_gl();
+    }
+#endif
+}
+
+void DisplayChannel::destroy_primary_surface()
+{
+#ifdef USE_OGL
+    if (screen()) {
+        if (_canvas.get()) {
+            if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+                screen()->unset_type_gl();
+                screen()->untouch_context();
+            }
+        }
+    }
+#endif
+
+    AutoRef<DestroyPrimarySurfaceEvent> event(new DestroyPrimarySurfaceEvent(*this));
+    get_client().push_event(*event);
+    (*event)->wait();
+    if (!(*event)->success()) {
+        THROW("Destroying primary surface failed");
+    }
+}
+
+void DisplayChannel::handle_surface_create(RedPeer::InMessage* message)
+{
+    SpiceMsgSurfaceCreate* surface_create = (SpiceMsgSurfaceCreate*)message->data();
+    PANIC_ON(surface_create->surface_id != 0);
+    PANIC_ON(surface_create->flags != SPICE_SURFACE_FLAGS_PRIMARY);
+
+    create_primary_surface(surface_create->width, surface_create->height, surface_create->depth);
+}
+
+void DisplayChannel::handle_surface_destroy(RedPeer::InMessage* message)
+{
+    SpiceMsgSurfaceDestroy* surface_destroy = (SpiceMsgSurfaceDestroy*)message->data();
+    PANIC_ON(surface_destroy->surface_id != 0);
+
+    destroy_primary_surface();
+}
+
 #define PRE_DRAW
 #define POST_DRAW
 
@@ -1472,6 +1519,7 @@ void DisplayChannel::handle_stream_destroy_all(RedPeer::InMessage* message)
 void DisplayChannel::handle_copy_bits(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayCopyBits* copy_bits = (SpiceMsgDisplayCopyBits*)message->data();
+    PANIC_ON(copy_bits->base.surface_id != 0);
     PRE_DRAW;
     _canvas->copy_bits(*copy_bits, message->size());
     POST_DRAW;
@@ -1481,72 +1529,84 @@ void DisplayChannel::handle_copy_bits(RedPeer::InMessage* message)
 void DisplayChannel::handle_draw_fill(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayDrawFill* fill = (SpiceMsgDisplayDrawFill*)message->data();
+    PANIC_ON(fill->base.surface_id != 0);
     DRAW(fill);
 }
 
 void DisplayChannel::handle_draw_opaque(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayDrawOpaque* opaque = (SpiceMsgDisplayDrawOpaque*)message->data();
+    PANIC_ON(opaque->base.surface_id != 0);
     DRAW(opaque);
 }
 
 void DisplayChannel::handle_draw_copy(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayDrawCopy* copy = (SpiceMsgDisplayDrawCopy*)message->data();
+    PANIC_ON(copy->base.surface_id != 0);
     DRAW(copy);
 }
 
 void DisplayChannel::handle_draw_blend(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayDrawBlend* blend = (SpiceMsgDisplayDrawBlend*)message->data();
+    PANIC_ON(blend->base.surface_id != 0);
     DRAW(blend);
 }
 
 void DisplayChannel::handle_draw_blackness(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayDrawBlackness* blackness = (SpiceMsgDisplayDrawBlackness*)message->data();
+    PANIC_ON(blackness->base.surface_id != 0);
     DRAW(blackness);
 }
 
 void DisplayChannel::handle_draw_whiteness(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayDrawWhiteness* whiteness = (SpiceMsgDisplayDrawWhiteness*)message->data();
+    PANIC_ON(whiteness->base.surface_id != 0);
     DRAW(whiteness);
 }
 
 void DisplayChannel::handle_draw_invers(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayDrawInvers* invers = (SpiceMsgDisplayDrawInvers*)message->data();
+    PANIC_ON(invers->base.surface_id != 0);
     DRAW(invers);
 }
 
 void DisplayChannel::handle_draw_rop3(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayDrawRop3* rop3 = (SpiceMsgDisplayDrawRop3*)message->data();
+    PANIC_ON(rop3->base.surface_id != 0);
     DRAW(rop3);
 }
 
 void DisplayChannel::handle_draw_stroke(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayDrawStroke* stroke = (SpiceMsgDisplayDrawStroke*)message->data();
+    PANIC_ON(stroke->base.surface_id != 0);
     DRAW(stroke);
 }
 
 void DisplayChannel::handle_draw_text(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayDrawText* text = (SpiceMsgDisplayDrawText*)message->data();
+    PANIC_ON(text->base.surface_id != 0);
     DRAW(text);
 }
 
 void DisplayChannel::handle_draw_transparent(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayDrawTransparent* transparent = (SpiceMsgDisplayDrawTransparent*)message->data();
+    PANIC_ON(transparent->base.surface_id != 0);
     DRAW(transparent);
 }
 
 void DisplayChannel::handle_draw_alpha_blend(RedPeer::InMessage* message)
 {
     SpiceMsgDisplayDrawAlphaBlend* alpha_blend = (SpiceMsgDisplayDrawAlphaBlend*)message->data();
+    PANIC_ON(alpha_blend->base.surface_id != 0);
     DRAW(alpha_blend);
 }
 
diff --git a/client/display_channel.h b/client/display_channel.h
index b2e3791..d603a26 100644
--- a/client/display_channel.h
+++ b/client/display_channel.h
@@ -131,6 +131,9 @@ private:
     void destroy_strams();
     void update_cursor();
 
+    void create_primary_surface(int width, int height, int depth);
+    void destroy_primary_surface();
+
     void handle_mode(RedPeer::InMessage* message);
     void handle_mark(RedPeer::InMessage* message);
     void handle_reset(RedPeer::InMessage* message);
@@ -146,6 +149,9 @@ private:
     void handle_stream_destroy(RedPeer::InMessage* message);
     void handle_stream_destroy_all(RedPeer::InMessage* message);
 
+    void handle_surface_create(RedPeer::InMessage* message);
+    void handle_surface_destroy(RedPeer::InMessage* message);
+
     void handle_draw_fill(RedPeer::InMessage* message);
     void handle_draw_opaque(RedPeer::InMessage* message);
     void handle_draw_copy(RedPeer::InMessage* message);
@@ -208,6 +214,8 @@ private:
     InterruptUpdate _interrupt_update;
 
     friend class SetModeEvent;
+    friend class CreatePrimarySurfaceEvent;
+    friend class DestroyPrimarySurfaceEvent;
     friend class ActivateTimerEvent;
     friend class VideoStream;
     friend class StreamsTrigger;
diff --git a/server/red_worker.c b/server/red_worker.c
index 4608706..5dec5f6 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -221,7 +221,6 @@ typedef struct BufDescriptor {
 
 enum {
     PIPE_ITEM_TYPE_DRAW,
-    PIPE_ITEM_TYPE_MODE,
     PIPE_ITEM_TYPE_INVAL_ONE,
     PIPE_ITEM_TYPE_CURSOR,
     PIPE_ITEM_TYPE_MIGRATE,
@@ -239,6 +238,8 @@ enum {
     PIPE_ITEM_TYPE_PIXMAP_RESET,
     PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE,
     PIPE_ITEM_TYPE_INVAL_PALLET_CACHE,
+    PIPE_ITEM_TYPE_CREATE_SURFACE,
+    PIPE_ITEM_TYPE_DESTROY_SURFACE,
 };
 
 typedef struct PipeItem {
@@ -279,6 +280,16 @@ struct CacheItem {
     uint32_t inval_type;
 };
 
+typedef struct SurfaceCreateItem {
+    SpiceMsgSurfaceCreate surface_create;
+    PipeItem pipe_item;
+} SurfaceCreateItem;
+
+typedef struct SurfaceDestroyItem {
+    SpiceMsgSurfaceDestroy surface_destroy;
+    PipeItem pipe_item;
+} SurfaceDestroyItem;
+
 enum {
     CURSOR_TYPE_INVALID,
     CURSOR_TYPE_DEV,
@@ -628,6 +639,8 @@ struct DisplayChannel {
     Ring glz_drawables_inst_to_free;               // list of instances to be freed
     pthread_mutex_t glz_drawables_inst_to_free_lock;
 
+    uint8_t surface_client_created;
+
     struct {
         union {
             SpiceMsgDisplayDrawFill fill;
@@ -645,6 +658,8 @@ struct DisplayChannel {
             SpiceMsgDisplayDrawText text;
             SpiceMsgDisplayMode mode;
             SpiceMsgDisplayInvalOne inval_one;
+            SpiceMsgSurfaceCreate surface_create;
+            SpiceMsgSurfaceDestroy surface_destroy;
             WaitForChannels wait;
             struct {
                 SpiceMsgDisplayStreamCreate message;
@@ -843,10 +858,10 @@ typedef struct DrawContext {
     void *line_0;
 } DrawContext;
 
-typedef struct Surface {
+typedef struct RedSurface {
     uint32_t refs;
     DrawContext context;
-} Surface;
+} RedSurface;
 
 #ifdef STREAM_TRACE
 typedef struct ItemTrace {
@@ -892,7 +907,7 @@ typedef struct RedWorker {
     uint32_t renderers[RED_MAX_RENDERERS];
     uint32_t renderer;
 
-    Surface surface;
+    RedSurface surface;
 
     Ring current_list;
     Ring current;
@@ -1413,7 +1428,6 @@ static void red_pipe_clear(RedChannel *channel)
         case PIPE_ITEM_TYPE_PIXMAP_RESET:
         case PIPE_ITEM_TYPE_PIXMAP_SYNC:
         case PIPE_ITEM_TYPE_INVAL_ONE:
-        case PIPE_ITEM_TYPE_MODE:
         case PIPE_ITEM_TYPE_MIGRATE:
         case PIPE_ITEM_TYPE_SET_ACK:
         case PIPE_ITEM_TYPE_CURSOR_INIT:
@@ -1421,6 +1435,8 @@ static void red_pipe_clear(RedChannel *channel)
         case PIPE_ITEM_TYPE_MIGRATE_DATA:
         case PIPE_ITEM_TYPE_INVAL_PALLET_CACHE:
         case PIPE_ITEM_TYPE_INVAL_CURSOR_CACHE:
+        case PIPE_ITEM_TYPE_CREATE_SURFACE:
+        case PIPE_ITEM_TYPE_DESTROY_SURFACE:
             free(item);
             break;
         case PIPE_ITEM_TYPE_IMAGE:
@@ -1494,9 +1510,36 @@ static void drawables_init(RedWorker *worker)
 
 static void red_reset_stream_trace(RedWorker *worker);
 
+static SurfaceDestroyItem *get_surface_destroy_item(uint32_t surface_id)
+{
+    SurfaceDestroyItem *destroy;
+
+    destroy = (SurfaceDestroyItem *)malloc(sizeof(SurfaceDestroyItem));
+    PANIC_ON(!destroy);
+
+    destroy->surface_destroy.surface_id = surface_id;
+
+    red_pipe_item_init(&destroy->pipe_item, PIPE_ITEM_TYPE_DESTROY_SURFACE);
+
+    return destroy;
+}
+
+static inline void red_destroy_surface_item(RedWorker *worker, uint32_t surface_id)
+{
+    SurfaceDestroyItem *destroy;
+
+    if (!worker->display_channel) {
+        return;
+    }
+
+    destroy = get_surface_destroy_item(surface_id);
+
+    red_pipe_add(&worker->display_channel->base, &destroy->pipe_item);
+}
+
 static inline void __red_destroy_surface(RedWorker *worker)
 {
-    Surface *surface = &worker->surface;
+    RedSurface *surface = &worker->surface;
 
     if (!--worker->surface.refs) {
 #ifdef STREAM_TRACE
@@ -1505,13 +1548,14 @@ static inline void __red_destroy_surface(RedWorker *worker)
         if (surface->context.canvas) {
             surface->context.canvas->ops->destroy(surface->context.canvas);
             surface->context.canvas = NULL;
+            red_destroy_surface_item(worker, 0);
         }
     }
 }
 
 static inline void red_destroy_surface(RedWorker *worker)
 {
-    Surface *surface = &worker->surface;
+    RedSurface *surface = &worker->surface;
 
     PANIC_ON(!surface->context.canvas);
     __red_destroy_surface(worker);
@@ -4745,10 +4789,11 @@ static inline void fill_rects_clip(RedChannel *channel, QXLPHYSICAL *in_clip, ui
 }
 
 static void fill_base(DisplayChannel *display_channel, SpiceMsgDisplayBase *base, Drawable *drawable,
-                      uint32_t size)
+                      uint32_t size, uint32_t surface_id)
 {
     RedChannel *channel = &display_channel->base;
     add_buf(channel, BUF_TYPE_RAW, base, size, 0, 0);
+    base->surface_id = surface_id;
     base->box = drawable->qxl_drawable->bbox;
     base->clip = drawable->qxl_drawable->clip;
 
@@ -6169,7 +6214,7 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_FILL:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_FILL;
         fill_base(display_channel, &display_channel->send_data.u.fill.base, item,
-                  sizeof(SpiceMsgDisplayDrawFill));
+                  sizeof(SpiceMsgDisplayDrawFill), 0);
         display_channel->send_data.u.fill.data = drawable->u.fill;
         fill_brush(display_channel, &display_channel->send_data.u.fill.data.brush, item);
         fill_mask(display_channel, &display_channel->send_data.u.fill.data.mask, item);
@@ -6177,7 +6222,7 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_OPAQUE:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_OPAQUE;
         fill_base(display_channel, &display_channel->send_data.u.opaque.base, item,
-                  sizeof(SpiceMsgDisplayDrawOpaque));
+                  sizeof(SpiceMsgDisplayDrawOpaque), 0);
         display_channel->send_data.u.opaque.data = drawable->u.opaque;
         fill_bits(display_channel, &display_channel->send_data.u.opaque.data.src_bitmap, item);
         fill_brush(display_channel, &display_channel->send_data.u.opaque.data.brush, item);
@@ -6185,7 +6230,8 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
         break;
     case QXL_DRAW_COPY:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_COPY;
-        fill_base(display_channel, &display_channel->send_data.u.copy.base, item, sizeof(SpiceMsgDisplayDrawCopy));
+        fill_base(display_channel, &display_channel->send_data.u.copy.base, item,
+                  sizeof(SpiceMsgDisplayDrawCopy), 0);
         display_channel->send_data.u.copy.data = drawable->u.copy;
         fill_bits(display_channel, &display_channel->send_data.u.copy.data.src_bitmap, item);
         fill_mask(display_channel, &display_channel->send_data.u.copy.data.mask, item);
@@ -6193,27 +6239,27 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_TRANSPARENT:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_TRANSPARENT;
         fill_base(display_channel, &display_channel->send_data.u.transparent.base, item,
-                  sizeof(SpiceMsgDisplayDrawTransparent));
+                  sizeof(SpiceMsgDisplayDrawTransparent), 0);
         display_channel->send_data.u.transparent.data = drawable->u.transparent;
         fill_bits(display_channel, &display_channel->send_data.u.transparent.data.src_bitmap, item);
         break;
     case QXL_DRAW_ALPHA_BLEND:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND;
         fill_base(display_channel, &display_channel->send_data.u.alpha_blend.base, item,
-                  sizeof(SpiceMsgDisplayDrawAlphaBlend));
+                  sizeof(SpiceMsgDisplayDrawAlphaBlend), 0);
         display_channel->send_data.u.alpha_blend.data = drawable->u.alpha_blend;
         fill_bits(display_channel, &display_channel->send_data.u.alpha_blend.data.src_bitmap, item);
         break;
     case QXL_COPY_BITS:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_COPY_BITS;
         fill_base(display_channel, &display_channel->send_data.u.copy_bits.base, item,
-                  sizeof(SpiceMsgDisplayCopyBits));
+                  sizeof(SpiceMsgDisplayCopyBits), 0);
         display_channel->send_data.u.copy_bits.src_pos = drawable->u.copy_bits.src_pos;
         break;
     case QXL_DRAW_BLEND:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_BLEND;
         fill_base(display_channel, &display_channel->send_data.u.blend.base, item,
-                  sizeof(SpiceMsgDisplayDrawBlend));
+                  sizeof(SpiceMsgDisplayDrawBlend), 0);
         display_channel->send_data.u.blend.data = drawable->u.blend;
         fill_bits(display_channel, &display_channel->send_data.u.blend.data.src_bitmap, item);
         fill_mask(display_channel, &display_channel->send_data.u.blend.data.mask, item);
@@ -6221,28 +6267,28 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_BLACKNESS:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_BLACKNESS;
         fill_base(display_channel, &display_channel->send_data.u.blackness.base, item,
-                  sizeof(SpiceMsgDisplayDrawBlackness));
+                  sizeof(SpiceMsgDisplayDrawBlackness), 0);
         display_channel->send_data.u.blackness.data = drawable->u.blackness;
         fill_mask(display_channel, &display_channel->send_data.u.blackness.data.mask, item);
         break;
     case QXL_DRAW_WHITENESS:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_WHITENESS;
         fill_base(display_channel, &display_channel->send_data.u.whiteness.base, item,
-                  sizeof(SpiceMsgDisplayDrawWhiteness));
+                  sizeof(SpiceMsgDisplayDrawWhiteness), 0);
         display_channel->send_data.u.whiteness.data = drawable->u.whiteness;
         fill_mask(display_channel, &display_channel->send_data.u.whiteness.data.mask, item);
         break;
     case QXL_DRAW_INVERS:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_INVERS;
         fill_base(display_channel, &display_channel->send_data.u.invers.base, item,
-                  sizeof(SpiceMsgDisplayDrawInvers));
+                  sizeof(SpiceMsgDisplayDrawInvers), 0);
         display_channel->send_data.u.invers.data = drawable->u.invers;
         fill_mask(display_channel, &display_channel->send_data.u.invers.data.mask, item);
         break;
     case QXL_DRAW_ROP3:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_ROP3;
         fill_base(display_channel, &display_channel->send_data.u.rop3.base, item,
-                  sizeof(SpiceMsgDisplayDrawRop3));
+                  sizeof(SpiceMsgDisplayDrawRop3), 0);
         display_channel->send_data.u.rop3.data = drawable->u.rop3;
         fill_bits(display_channel, &display_channel->send_data.u.rop3.data.src_bitmap, item);
         fill_brush(display_channel, &display_channel->send_data.u.rop3.data.brush, item);
@@ -6251,7 +6297,7 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
     case QXL_DRAW_STROKE:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_STROKE;
         fill_base(display_channel, &display_channel->send_data.u.stroke.base, item,
-                  sizeof(SpiceMsgDisplayDrawStroke));
+                  sizeof(SpiceMsgDisplayDrawStroke), 0);
         display_channel->send_data.u.stroke.data = drawable->u.stroke;
         fill_path(display_channel, &display_channel->send_data.u.stroke.data.path, item->group_id);
         fill_attr(display_channel, &display_channel->send_data.u.stroke.data.attr, item->group_id);
@@ -6259,7 +6305,8 @@ static inline void red_send_qxl_drawable(RedWorker *worker, DisplayChannel *disp
         break;
     case QXL_DRAW_TEXT:
         channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_TEXT;
-        fill_base(display_channel, &display_channel->send_data.u.text.base, item, sizeof(SpiceMsgDisplayDrawText));
+        fill_base(display_channel, &display_channel->send_data.u.text.base, item,
+                  sizeof(SpiceMsgDisplayDrawText), 0);
         display_channel->send_data.u.text.data = drawable->u.text;
         fill_brush(display_channel, &display_channel->send_data.u.text.data.fore_brush, item);
         fill_brush(display_channel, &display_channel->send_data.u.text.data.back_brush, item);
@@ -6716,29 +6763,6 @@ static inline void send_qxl_drawable(DisplayChannel *display_channel, Drawable *
     red_send_qxl_drawable(display_channel->base.worker, display_channel, item);
 }
 
-static void red_send_mode(DisplayChannel *display_channel)
-{
-    RedChannel *channel;
-    RedWorker *worker;
-
-    ASSERT(display_channel);
-    worker = display_channel->base.worker;
-
-    if (!worker->surface.context.canvas) {
-        return;
-    }
-
-    channel = &display_channel->base;
-    channel->send_data.header.type = SPICE_MSG_DISPLAY_MODE;
-    display_channel->send_data.u.mode.x_res = worker->surface.context.width;
-    display_channel->send_data.u.mode.y_res = worker->surface.context.height;
-    display_channel->send_data.u.mode.bits = worker->surface.context.depth;
-
-    add_buf(channel, BUF_TYPE_RAW, &display_channel->send_data.u.mode, sizeof(SpiceMsgDisplayMode), 0, 0);
-
-    display_begin_send_massage(display_channel, NULL);
-}
-
 static void red_send_set_ack(RedChannel *channel)
 {
     ASSERT(channel);
@@ -6899,6 +6923,7 @@ static void red_send_image(DisplayChannel *display_channel, ImageItem *item)
     channel->send_data.header.type = SPICE_MSG_DISPLAY_DRAW_COPY;
 
     add_buf(channel, BUF_TYPE_RAW, &display_channel->send_data.u.copy, sizeof(SpiceMsgDisplayDrawCopy), 0, 0);
+    display_channel->send_data.u.copy.base.surface_id = 0;
     display_channel->send_data.u.copy.base.box.left = item->pos.x;
     display_channel->send_data.u.copy.base.box.top = item->pos.y;
     display_channel->send_data.u.copy.base.box.right = item->pos.x + bitmap.x;
@@ -6949,6 +6974,7 @@ static void red_display_send_upgrade(DisplayChannel *display_channel, UpgradeIte
     ASSERT(qxl_drawable->u.copy.mask.bitmap == 0);
 
     add_buf(channel, BUF_TYPE_RAW, copy, sizeof(SpiceMsgDisplayDrawCopy), 0, 0);
+    copy->base.surface_id = 0;
     copy->base.box = qxl_drawable->bbox;
     copy->base.clip.type = SPICE_CLIP_TYPE_RECTS;
     copy->base.clip.data = channel->send_data.header.size;
@@ -6969,6 +6995,7 @@ static void red_display_send_stream_start(DisplayChannel *display_channel, Strea
     ASSERT(stream);
     channel->send_data.header.type = SPICE_MSG_DISPLAY_STREAM_CREATE;
     SpiceMsgDisplayStreamCreate *stream_create = &display_channel->send_data.u.stream_create.message;
+    stream_create->surface_id = 0;
     stream_create->id = agent - display_channel->stream_agents;
     stream_create->flags = stream->top_down ? SPICE_STREAM_FLAGS_TOP_DOWN : 0;
     stream_create->codec_type = SPICE_VIDEO_CODEC_TYPE_MJPEG;
@@ -7132,6 +7159,41 @@ static void red_send_cursor(CursorChannel *cursor_channel, CursorItem *cursor)
     red_release_cursor(channel->worker, cursor);
 }
 
+static void red_send_surface_create(DisplayChannel *display, SpiceMsgSurfaceCreate *surface_create)
+{
+    RedChannel *channel;
+
+    ASSERT(display);
+    channel = &display->base;
+
+    channel->send_data.header.type = SPICE_MSG_DISPLAY_SURFACE_CREATE;
+    display->send_data.u.surface_create = *surface_create;
+
+    add_buf(channel, BUF_TYPE_RAW, &display->send_data.u.surface_create,
+            sizeof(SpiceMsgSurfaceCreate), 0, 0);
+
+    display->surface_client_created = TRUE;
+
+    red_begin_send_massage(channel, NULL);
+}
+
+static void red_send_surface_destroy(DisplayChannel *display, uint32_t surface_id)
+{
+    RedChannel *channel;
+
+    ASSERT(display);
+    channel = &display->base;
+
+    channel->send_data.header.type = SPICE_MSG_DISPLAY_SURFACE_DESTROY;
+    display->send_data.u.surface_destroy.surface_id = surface_id;
+
+    add_buf(channel, BUF_TYPE_RAW, &display->send_data.u.surface_destroy,
+            sizeof(SpiceMsgSurfaceDestroy), 0, 0);
+
+    display->surface_client_created = FALSE;
+    red_begin_send_massage(channel, NULL);
+}
+
 static inline PipeItem *red_pipe_get(RedChannel *channel)
 {
     PipeItem *item;
@@ -7195,10 +7257,6 @@ static void display_channel_push(RedWorker *worker)
             display_send_verb(display_channel, ((VerbItem*)pipe_item)->verb);
             free(pipe_item);
             break;
-        case PIPE_ITEM_TYPE_MODE:
-            red_send_mode(display_channel);
-            free(pipe_item);
-            break;
         case PIPE_ITEM_TYPE_MIGRATE:
             red_printf("PIPE_ITEM_TYPE_MIGRATE");
             display_channel_send_migrate(display_channel);
@@ -7229,6 +7287,20 @@ static void display_channel_push(RedWorker *worker)
             red_send_verb((RedChannel *)display_channel, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
             free(pipe_item);
             break;
+        case PIPE_ITEM_TYPE_CREATE_SURFACE: {
+            SurfaceCreateItem *surface_create = SPICE_CONTAINEROF(pipe_item, SurfaceCreateItem,
+                                                                  pipe_item);
+            red_send_surface_create(display_channel, &surface_create->surface_create);
+            free(surface_create);
+            break;
+        }
+        case PIPE_ITEM_TYPE_DESTROY_SURFACE: {
+            SurfaceDestroyItem *surface_destroy = SPICE_CONTAINEROF(pipe_item, SurfaceDestroyItem,
+                                                                    pipe_item);
+            red_send_surface_destroy(display_channel, surface_destroy->surface_destroy.surface_id);
+            free(surface_destroy);
+            break;
+        }
         default:
             red_error("invalid pipe item type");
         }
@@ -7478,7 +7550,7 @@ static SpiceCanvas *create_ogl_pixmap_context(RedWorker *worker, uint32_t width,
     return canvas;
 }
 
-static inline void *create_canvas_for_surface(RedWorker *worker, Surface *surface,
+static inline void *create_canvas_for_surface(RedWorker *worker, RedSurface *surface,
                                               uint32_t renderer, uint32_t width, uint32_t height,
                                               int32_t stride, uint8_t depth, void *line_0)
 {
@@ -7503,16 +7575,52 @@ static inline void *create_canvas_for_surface(RedWorker *worker, Surface *surfac
     };
 }
 
-static inline void red_create_surface(RedWorker *worker, uint32_t width, uint32_t height,
-                                      int32_t stride, uint8_t depth, void *line_0)
+static SurfaceCreateItem *get_surface_create_item(uint32_t surface_id, uint32_t width,
+                                                  uint32_t height, uint8_t depth, uint32_t flags)
+{
+    SurfaceCreateItem *create;
+
+    create = (SurfaceCreateItem *)malloc(sizeof(SurfaceCreateItem));
+    PANIC_ON(!create);
+
+    create->surface_create.surface_id = surface_id;
+    create->surface_create.width = width;
+    create->surface_create.height = height;
+    create->surface_create.flags = flags;
+
+    red_pipe_item_init(&create->pipe_item, PIPE_ITEM_TYPE_CREATE_SURFACE);
+
+    return create;
+}
+
+static inline void red_create_surface_item(RedWorker *worker, RedSurface *surface)
+{
+    SurfaceCreateItem *create;
+
+    if (!surface->context.canvas) {
+        return;
+    }
+
+    if (!worker->display_channel) {
+        return;
+    }
+
+    create = get_surface_create_item(0, surface->context.width, surface->context.height,
+                                     surface->context.depth, SPICE_SURFACE_FLAGS_PRIMARY);
+    red_pipe_add(&worker->display_channel->base, &create->pipe_item);
+}
+
+static inline void red_create_surface(RedWorker *worker, uint32_t surface_id,uint32_t width,
+                                      uint32_t height, int32_t stride, uint8_t depth, void *line_0)
 {
     uint32_t i;
-    Surface *surface = &worker->surface;
+    RedSurface *surface = &worker->surface;
 
     if (stride >= 0) {
         PANIC("Untested path stride >= 0");
     }
     PANIC_ON(surface->context.canvas);
+    ASSERT(surface_id == 0);
 
     surface->context.canvas_draws_on_surface = FALSE;
     surface->context.width = width;
@@ -7529,6 +7637,7 @@ static inline void red_create_surface(RedWorker *worker, uint32_t width, uint32_
         if (!surface->context.canvas) {
             PANIC("drawing canvas creating failed - can`t create same type canvas");
         }
+        red_create_surface_item(worker, surface);
         return;
     }
 
@@ -7538,6 +7647,7 @@ static inline void red_create_surface(RedWorker *worker, uint32_t width, uint32_
                                                             surface->context.depth, line_0);
         if (surface->context.canvas) {
             worker->renderer = worker->renderers[i];
+            red_create_surface_item(worker, surface);
             return;
         }
     }
@@ -7590,14 +7700,14 @@ static inline void red_flush_surface_pipe(RedWorker *worker)
     }
 }
 
-static void push_new_mode(RedWorker *worker)
+static void push_new_primary_surface(RedWorker *worker)
 {
     DisplayChannel *display_channel;
 
     if ((display_channel = worker->display_channel)) {
         red_pipe_add_type(&display_channel->base, PIPE_ITEM_TYPE_INVAL_PALLET_CACHE);
         if (!display_channel->base.migrate) {
-            red_pipe_add_type(&display_channel->base, PIPE_ITEM_TYPE_MODE);
+            red_create_surface_item(worker, &worker->surface);
         }
         display_channel_push(worker);
     }
@@ -7650,7 +7760,7 @@ static void on_new_display_channel(RedWorker *worker)
     display_channel->base.messages_window = 0;
     if (worker->surface.context.canvas) {
         red_current_flush(worker);
-        push_new_mode(worker);
+        push_new_primary_surface(worker);
         red_add_screen_image(worker);
         if (channel_is_connected(&display_channel->base)) {
             red_pipe_add_verb(&display_channel->base, SPICE_MSG_DISPLAY_MARK);
@@ -8571,11 +8681,11 @@ static inline void handle_dev_create_primary_surface(RedWorker *worker)
         line_0 -= (int32_t)(surface.stride * (surface.height -1));
     }
 
-    red_create_surface(worker, surface.width, surface.height, surface.stride, surface.depth,
+    red_create_surface(worker, 0, surface.width, surface.height, surface.stride, surface.depth,
                        line_0);
 
     if (worker->display_channel) {
-        red_pipe_add_type(&worker->display_channel->base, PIPE_ITEM_TYPE_MODE);
+        red_create_surface_item(worker, &worker->surface);
         red_pipe_add_verb(&worker->display_channel->base, SPICE_MSG_DISPLAY_MARK);
         display_channel_push(worker);
     }


More information about the Spice-commits mailing list