[Spice-commits] 17 commits - client/display_channel.cpp server/mjpeg_encoder.c server/mjpeg_encoder.h server/red_worker.c server/tests spice-common

Yonit Halperin yhalperi at kemper.freedesktop.org
Thu May 3 04:12:45 PDT 2012


 client/display_channel.cpp                     |    6 
 server/mjpeg_encoder.c                         |   15 
 server/mjpeg_encoder.h                         |    3 
 server/red_worker.c                            |  399 +++++++++++++++++--------
 server/tests/test_display_base.c               |  180 ++++++++---
 server/tests/test_display_base.h               |   46 ++
 server/tests/test_display_resolution_changes.c |   10 
 server/tests/test_display_streaming.c          |  211 ++++++++++++-
 spice-common                                   |    2 
 9 files changed, 672 insertions(+), 200 deletions(-)

New commits:
commit 4c880348455dbff8c6e1a6b72ee86b923d242a1e
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Wed May 2 14:30:12 2012 +0300

    server/tests/test_display_streaming: include tests for clip and sized frames
    
    CC: Alon Levy <alevy at redhat.com>

diff --git a/server/tests/test_display_streaming.c b/server/tests/test_display_streaming.c
index b4fe013..f395800 100644
--- a/server/tests/test_display_streaming.c
+++ b/server/tests/test_display_streaming.c
@@ -9,40 +9,197 @@
 #include <string.h>
 #include <assert.h>
 #include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
 #include "test_display_base.h"
 
 static int sized;
+static int render_last_frame;
 
-void create_update(Command *command)
+void create_overlay(Command *command , int width, int height)
 {
-    static int count = 0;
-    CommandDrawSolid *cmd = &command->solid;
+    CommandDrawBitmap *cmd = &command->bitmap;
+    uint32_t *dst;
+
     cmd->surface_id = 0;
 
     cmd->bbox.left = 0;
-    cmd->bbox.right = test_get_width();
     cmd->bbox.top = 0;
-    cmd->color = 0xffff00 + ((count * 10) % 256);
-    assert(test_get_height() > 50);
-    cmd->bbox.bottom = test_get_height() - 50;
-    if (count < 20) {
-    } else if (sized && count % 5 == 0) {
-        cmd->bbox.bottom = test_get_height();
-        cmd->color = 0xff;
+    cmd->bbox.right = width;
+    cmd->bbox.bottom = height;
+
+    cmd->num_clip_rects = 0;
+    cmd->bitmap = malloc(width * height * 4 );
+    dst = (uint32_t *)cmd->bitmap;
+    for (int i = 0; i < width * height; i++, dst++) {
+        *dst = 0x8B008B;
     }
+
+}
+
+#define NUM_COMMANDS 2000
+#define SIZED_INTERVAL 100
+#define OVERLAY_FRAME 500
+#define OVERLAY_WIDTH 200
+#define OVERLAY_HEIGHT 200
+
+/*
+ * Create a frame in a stream that displays a row that moves
+ * from the top to the bottom repeatedly.
+ * Upon the OVERLAY_FRAME-th, a drawable is created on top of a part of the stream,
+ * and from then on, all the stream frames has a clipping that keeps this drawable
+ * visible, and in addition a clipping_factor is subtracted from the right limit of their clipping.
+ * If sized=TRUE, a higher frame than the original stream height is created every SIZED_INTERVAL.
+ * The sized frames can be distinguished by a change in the color of the top and bottom limits of the
+ * surface.
+ */
+void create_clipped_frame(Command *command, int clipping_factor)
+{
+    static int count = 0;
+    CommandDrawBitmap *cmd = &command->bitmap;
+    int max_height = test_get_height();
+    int width = test_get_width();
+    int height;
+    int cur_line, end_line;
+    uint32_t *dst;
+
     count++;
-    printf("%d %d\n", count, cmd->bbox.bottom);
+    if (count == NUM_COMMANDS) {
+        count = 0;
+    }
+    if (count == OVERLAY_FRAME) {
+        create_overlay(command, OVERLAY_WIDTH, OVERLAY_HEIGHT);
+        return;
+    }
+
+    cmd->surface_id = 0;
+
+    cmd->bbox.left = 0;
+    cmd->bbox.right = width;
+    assert(max_height > 600);
+    cmd->bbox.top = 50;
+    cmd->bbox.bottom = max_height - 50;
+    height = cmd->bbox.bottom  - cmd->bbox.top;
+    cur_line = (height/30)*(count % 30);
+    end_line = cur_line + (height/30);
+    if (end_line >= height || height - end_line < 8) {
+        end_line = height;
+    }
+
+    if (sized && count % SIZED_INTERVAL == 0) {
+
+        cmd->bbox.top = 0;
+        cmd->bbox.bottom = max_height;
+        height = max_height;
+        cur_line += 50;
+        end_line += 50;
+    }
+
+    cmd->bitmap = malloc(width*height*4);
+    memset(cmd->bitmap, 0xff, width*height*4);
+    dst = (uint32_t *)(cmd->bitmap + cur_line*width*4);
+    for (cur_line; cur_line < end_line; cur_line++) {
+        int col;
+        for (col = 0; col < width; col++, dst++) {
+            *dst = 0x00FF00;
+        }
+    }
+    if (sized && count % SIZED_INTERVAL == 0) {
+        int i;
+        uint32_t color = 0xffffff & rand();
+
+        dst = (uint32_t *)cmd->bitmap;
+
+        for (i = 0; i < 50*width; i++, dst++) {
+            *dst = color;
+        }
+
+        dst = ((uint32_t *)(cmd->bitmap + (height - 50)*4*width));
+
+        for (i = 0; i < 50*width; i++, dst++) {
+            *dst = color;
+        }
+    }
+
+    if (count < OVERLAY_FRAME) {
+        cmd->num_clip_rects = 0;
+    } else {
+        cmd->num_clip_rects = 2;
+        cmd->clip_rects = calloc(sizeof(QXLRect), 2);
+        cmd->clip_rects[0].left = OVERLAY_WIDTH;
+        cmd->clip_rects[0].top = cmd->bbox.top;
+        cmd->clip_rects[0].right = cmd->bbox.right - clipping_factor;
+        cmd->clip_rects[0].bottom = OVERLAY_HEIGHT;
+        cmd->clip_rects[1].left = cmd->bbox.left;
+        cmd->clip_rects[1].top = OVERLAY_HEIGHT;
+        cmd->clip_rects[1].right = cmd->bbox.right - clipping_factor;
+        cmd->clip_rects[1].bottom = cmd->bbox.bottom;
+    }
+}
+
+void create_frame1(Command *command)
+{
+    create_clipped_frame(command, 0);
+}
+
+void create_frame2(Command *command)
+{
+    create_clipped_frame(command, 200);
+}
+
+typedef void (*create_frame_cb)(Command *command);
+
+
+/*
+ * The test contains two types of streams. The first stream doesn't
+ * have a clipping besides the on that the display the overlay drawable.
+ * Expected result: If render_last_frame=false, the last frame should
+ * be sent losslessly. Otherwise, red_update_area should be called, and the
+ * stream is upgraded by a screenshot.
+ *
+ * In the second test, the stream clip changes in the middle (becomes smaller).
+ * Expected result: red_update_area should is, and the
+ * stream is upgraded by a screenshot (including lossy areas that belong to old frames
+ * and were never covered by a lossless drawable).
+ *
+ */
+static void get_stream_commands(Command *commands, int num_commands,
+                                create_frame_cb cb)
+{
+    int i;
+
+    commands[0].command = DESTROY_PRIMARY;
+    commands[1].command = CREATE_PRIMARY;
+    commands[1].create_primary.width = 1280;
+    commands[1].create_primary.height = 1024;
+    commands[num_commands - 1].command = SLEEP;
+    commands[num_commands - 1].sleep.secs = 20;
+
+    for (i = 2; i < num_commands - 1; i++) {
+        commands[i].command = SIMPLE_DRAW_BITMAP;
+        commands[i].cb = cb;
+    }
+    if (render_last_frame) {
+        commands[num_commands - 2].command = SIMPLE_UPDATE;
+    }
 }
 
-static Command commands[] = {
-    {SIMPLE_DRAW_SOLID, create_update},
-};
+static void get_commands(Command **commands, int *num_commands)
+{
+    *num_commands = NUM_COMMANDS * 2;
+    *commands = calloc(sizeof(Command), *num_commands);
+
+    get_stream_commands(*commands, NUM_COMMANDS, create_frame1);
+    get_stream_commands((*commands) + NUM_COMMANDS, NUM_COMMANDS, create_frame2);
+}
 
 SpiceCoreInterface *core;
 SpiceServer *server;
 
 int main(int argc, char **argv)
 {
+    Command *commands;
+    int num_commands;
     int i;
     spice_test_config_parse_args(argc, argv);
     sized = 0;
@@ -50,12 +207,20 @@ int main(int argc, char **argv)
         if (strcmp(argv[i], "sized") == 0) {
             sized = 1;
         }
+        /* render last frame */
+        if (strcmp(argv[i], "render") == 0) {
+            render_last_frame = 1;
+        }
     }
+    srand(time(NULL));
+    // todo: add args list of test numbers with explenations
     core = basic_event_loop_init();
     server = test_init(core);
     spice_server_set_streaming_video(server, SPICE_STREAM_VIDEO_ALL);
     test_add_display_interface(server);
-    test_set_command_list(commands, COUNT(commands));
+    get_commands(&commands, &num_commands);
+    test_set_command_list(commands, num_commands);
     basic_event_loop_mainloop();
+    free(commands);
     return 0;
 }
commit c44726bd5f30c3706432a9711d99166c58208572
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Wed May 2 13:41:10 2012 +0300

    server/tests: use the correct dimensions in SIMPLE_UPDATE

diff --git a/server/tests/test_display_base.c b/server/tests/test_display_base.c
index 1c293f4..f17c05e 100644
--- a/server/tests/test_display_base.c
+++ b/server/tests/test_display_base.c
@@ -506,8 +506,8 @@ static void produce_command(void)
             path_progress(&path);
             break;
         case SIMPLE_UPDATE: {
-            QXLRect rect = {.left = 0, .right = SURF_WIDTH,
-                            .top = 0, .bottom = SURF_HEIGHT};
+            QXLRect rect = {.left = 0, .right = (target_surface == 0 ? test_width : SURF_WIDTH),
+                            .top = 0, .bottom = (target_surface == 0 ? test_height : SURF_HEIGHT)};
             qxl_worker->update_area(qxl_worker, target_surface, &rect, NULL, 0, 1);
             break;
         }
commit a267a4b31516a2347ec2c8413e3f59dcdbd1363a
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Wed May 2 13:39:04 2012 +0300

    server/tests: add SLEEP command to test_display_base

diff --git a/server/tests/test_display_base.c b/server/tests/test_display_base.c
index f412751..1c293f4 100644
--- a/server/tests/test_display_base.c
+++ b/server/tests/test_display_base.c
@@ -498,6 +498,10 @@ static void produce_command(void)
         command->cb(command);
     }
     switch (command->command) {
+        case SLEEP:
+             printf("sleep %u seconds\n", command->sleep.secs);
+             sleep(command->sleep.secs);
+             break;
         case PATH_PROGRESS:
             path_progress(&path);
             break;
diff --git a/server/tests/test_display_base.h b/server/tests/test_display_base.h
index 1421c1c..b2221a2 100644
--- a/server/tests/test_display_base.h
+++ b/server/tests/test_display_base.h
@@ -24,6 +24,7 @@ typedef enum {
     SIMPLE_UPDATE,
     DESTROY_PRIMARY,
     CREATE_PRIMARY,
+    SLEEP
 } CommandType;
 
 typedef struct CommandCreatePrimary {
@@ -45,6 +46,10 @@ typedef struct CommandDrawSolid {
     uint32_t surface_id;
 } CommandDrawSolid;
 
+typedef struct CommandSleep {
+    uint32_t secs;
+} CommandSleep;
+
 typedef struct Command Command;
 
 struct Command {
@@ -55,6 +60,7 @@ struct Command {
         CommandCreatePrimary create_primary;
         CommandDrawBitmap bitmap;
         CommandDrawSolid solid;
+        CommandSleep sleep;
     };
 };
 
commit bccf5bc35a7b96d523b0537be133f0ada8cffa36
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Wed May 2 13:36:27 2012 +0300

    server/tests: add clip to SIMPLE_DRAW_BITMAP

diff --git a/server/tests/test_display_base.c b/server/tests/test_display_base.c
index 86a7e49..f412751 100644
--- a/server/tests/test_display_base.c
+++ b/server/tests/test_display_base.c
@@ -43,6 +43,9 @@ static void test_spice_destroy_update(SimpleSpiceUpdate *update)
     if (!update) {
         return;
     }
+    if (update->drawable.clip.type != SPICE_CLIP_TYPE_NONE) {
+        free((uint8_t*)update->drawable.clip.data);
+    }
     free(update->bitmap);
     free(update);
 }
@@ -143,9 +146,12 @@ static void draw_pos(int t, int *x, int *y)
 #endif
 }
 
-/* bitmap is freeds, so it must be allocated with malloc */
+/* bitmap and rects are freed, so they must be allocated with malloc */
 SimpleSpiceUpdate *test_spice_create_update_from_bitmap(uint32_t surface_id,
-        QXLRect bbox, uint8_t *bitmap)
+                                                        QXLRect bbox,
+                                                        uint8_t *bitmap,
+                                                        uint32_t num_clip_rects,
+                                                        QXLRect *clip_rects)
 {
     SimpleSpiceUpdate *update;
     QXLDrawable *drawable;
@@ -163,7 +169,22 @@ SimpleSpiceUpdate *test_spice_create_update_from_bitmap(uint32_t surface_id,
     drawable->surface_id      = surface_id;
 
     drawable->bbox            = bbox;
-    drawable->clip.type       = SPICE_CLIP_TYPE_NONE;
+    if (num_clip_rects == 0) {
+        drawable->clip.type       = SPICE_CLIP_TYPE_NONE;
+    } else {
+        QXLClipRects *cmd_clip;
+
+        cmd_clip = calloc(sizeof(QXLClipRects) + num_clip_rects*sizeof(QXLRect), 1);
+        cmd_clip->num_rects = num_clip_rects;
+        cmd_clip->chunk.data_size = num_clip_rects*sizeof(QXLRect);
+        cmd_clip->chunk.prev_chunk = cmd_clip->chunk.next_chunk = 0;
+        memcpy(cmd_clip + 1, clip_rects, cmd_clip->chunk.data_size);
+
+        drawable->clip.type = SPICE_CLIP_TYPE_RECTS;
+        drawable->clip.data = (intptr_t)cmd_clip;
+
+        free(clip_rects);
+    }
     drawable->effect          = QXL_EFFECT_OPAQUE;
     simple_set_release_info(&drawable->release_info, (intptr_t)update);
     drawable->type            = QXL_DRAW_COPY;
@@ -209,7 +230,7 @@ static SimpleSpiceUpdate *test_spice_create_update_solid(uint32_t surface_id, QX
         *dst = color;
     }
 
-    return test_spice_create_update_from_bitmap(surface_id, bbox, bitmap);
+    return test_spice_create_update_from_bitmap(surface_id, bbox, bitmap, 0, NULL);
 }
 
 static SimpleSpiceUpdate *test_spice_create_update_draw(uint32_t surface_id, int t)
@@ -249,7 +270,7 @@ static SimpleSpiceUpdate *test_spice_create_update_draw(uint32_t surface_id, int
 
     bbox.left = left; bbox.top = top;
     bbox.right = left + bw; bbox.bottom = top + bh;
-    return test_spice_create_update_from_bitmap(surface_id, bbox, bitmap);
+    return test_spice_create_update_from_bitmap(surface_id, bbox, bitmap, 0, NULL);
 }
 
 static SimpleSpiceUpdate *test_spice_create_update_copy_bits(uint32_t surface_id)
@@ -512,7 +533,8 @@ static void produce_command(void)
                 break;
             case SIMPLE_DRAW_BITMAP:
                 update = test_spice_create_update_from_bitmap(command->bitmap.surface_id,
-                        command->bitmap.bbox, command->bitmap.bitmap);
+                        command->bitmap.bbox, command->bitmap.bitmap,
+                        command->bitmap.num_clip_rects, command->bitmap.clip_rects);
                 break;
             case SIMPLE_DRAW_SOLID:
                 update = test_spice_create_update_solid(command->solid.surface_id,
diff --git a/server/tests/test_display_base.h b/server/tests/test_display_base.h
index 3b24641..1421c1c 100644
--- a/server/tests/test_display_base.h
+++ b/server/tests/test_display_base.h
@@ -35,6 +35,8 @@ typedef struct CommandDrawBitmap {
     QXLRect bbox;
     uint8_t *bitmap;
     uint32_t surface_id;
+    uint32_t num_clip_rects;
+    QXLRect *clip_rects;
 } CommandDrawBitmap;
 
 typedef struct CommandDrawSolid {
commit 3373cda43476d24022bff769f4b7991eb61520cb
Author: Alon Levy <alevy at redhat.com>
Date:   Thu Apr 19 13:48:43 2012 +0300

    server/tests/test_display_streaming: update to create sized frames

diff --git a/server/tests/test_display_streaming.c b/server/tests/test_display_streaming.c
index 1b81d76..b4fe013 100644
--- a/server/tests/test_display_streaming.c
+++ b/server/tests/test_display_streaming.c
@@ -5,14 +5,37 @@
  */
 
 #include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
 #include "test_display_base.h"
 
-int simple_commands[] = {
-    SIMPLE_DRAW,
-    SIMPLE_UPDATE,
-    PATH_PROGRESS,
-    SIMPLE_CREATE_SURFACE,
-    SIMPLE_DESTROY_SURFACE,
+static int sized;
+
+void create_update(Command *command)
+{
+    static int count = 0;
+    CommandDrawSolid *cmd = &command->solid;
+    cmd->surface_id = 0;
+
+    cmd->bbox.left = 0;
+    cmd->bbox.right = test_get_width();
+    cmd->bbox.top = 0;
+    cmd->color = 0xffff00 + ((count * 10) % 256);
+    assert(test_get_height() > 50);
+    cmd->bbox.bottom = test_get_height() - 50;
+    if (count < 20) {
+    } else if (sized && count % 5 == 0) {
+        cmd->bbox.bottom = test_get_height();
+        cmd->color = 0xff;
+    }
+    count++;
+    printf("%d %d\n", count, cmd->bbox.bottom);
+}
+
+static Command commands[] = {
+    {SIMPLE_DRAW_SOLID, create_update},
 };
 
 SpiceCoreInterface *core;
@@ -20,12 +43,19 @@ SpiceServer *server;
 
 int main(int argc, char **argv)
 {
+    int i;
     spice_test_config_parse_args(argc, argv);
+    sized = 0;
+    for (i = 1 ; i < argc; ++i) {
+        if (strcmp(argv[i], "sized") == 0) {
+            sized = 1;
+        }
+    }
     core = basic_event_loop_init();
     server = test_init(core);
     spice_server_set_streaming_video(server, SPICE_STREAM_VIDEO_ALL);
     test_add_display_interface(server);
-    test_set_simple_command_list(simple_commands, COUNT(simple_commands));
+    test_set_command_list(commands, COUNT(commands));
     basic_event_loop_mainloop();
     return 0;
 }
commit 8778b8eb2c026dcbeef96e28ca64c29b4f0f8888
Author: Alon Levy <alevy at redhat.com>
Date:   Thu Apr 19 13:48:42 2012 +0300

    server/tests: add SIMPLE_DRAW_SOLID and SIMPLE_DRAW_BITMAP

diff --git a/server/tests/test_display_base.c b/server/tests/test_display_base.c
index 49b7973..86a7e49 100644
--- a/server/tests/test_display_base.c
+++ b/server/tests/test_display_base.c
@@ -191,6 +191,27 @@ SimpleSpiceUpdate *test_spice_create_update_from_bitmap(uint32_t surface_id,
     return update;
 }
 
+static SimpleSpiceUpdate *test_spice_create_update_solid(uint32_t surface_id, QXLRect bbox, uint32_t color)
+{
+    uint8_t *bitmap;
+    uint32_t *dst;
+    uint32_t bw;
+    uint32_t bh;
+    int i;
+
+    bw = bbox.right - bbox.left;
+    bh = bbox.bottom - bbox.top;
+
+    bitmap = malloc(bw * bh * 4);
+    dst = (uint32_t *)bitmap;
+
+    for (i = 0 ; i < bh * bw ; ++i, ++dst) {
+        *dst = color;
+    }
+
+    return test_spice_create_update_from_bitmap(surface_id, bbox, bitmap);
+}
+
 static SimpleSpiceUpdate *test_spice_create_update_draw(uint32_t surface_id, int t)
 {
     int top, left;
@@ -468,6 +489,8 @@ static void produce_command(void)
 
         /* Drawing commands, they all push a command to the command ring */
         case SIMPLE_COPY_BITS:
+        case SIMPLE_DRAW_SOLID:
+        case SIMPLE_DRAW_BITMAP:
         case SIMPLE_DRAW: {
             SimpleSpiceUpdate *update;
 
@@ -481,12 +504,20 @@ static void produce_command(void)
             }
 
             switch (command->command) {
-                case SIMPLE_COPY_BITS:
-                    update = test_spice_create_update_copy_bits(0);
-                    break;
-                case SIMPLE_DRAW:
-                    update = test_spice_create_update_draw(0, path.t);
-                    break;
+            case SIMPLE_COPY_BITS:
+                update = test_spice_create_update_copy_bits(0);
+                break;
+            case SIMPLE_DRAW:
+                update = test_spice_create_update_draw(0, path.t);
+                break;
+            case SIMPLE_DRAW_BITMAP:
+                update = test_spice_create_update_from_bitmap(command->bitmap.surface_id,
+                        command->bitmap.bbox, command->bitmap.bitmap);
+                break;
+            case SIMPLE_DRAW_SOLID:
+                update = test_spice_create_update_solid(command->solid.surface_id,
+                        command->solid.bbox, command->solid.color);
+                break;
             }
             push_command(&update->ext);
             break;
diff --git a/server/tests/test_display_base.h b/server/tests/test_display_base.h
index b769721..3b24641 100644
--- a/server/tests/test_display_base.h
+++ b/server/tests/test_display_base.h
@@ -17,6 +17,8 @@ typedef enum {
     PATH_PROGRESS,
     SIMPLE_CREATE_SURFACE,
     SIMPLE_DRAW,
+    SIMPLE_DRAW_BITMAP,
+    SIMPLE_DRAW_SOLID,
     SIMPLE_COPY_BITS,
     SIMPLE_DESTROY_SURFACE,
     SIMPLE_UPDATE,
commit 76fd64ad96aed9d7e567eb5e447a5d83181743f2
Author: Alon Levy <alevy at redhat.com>
Date:   Thu Apr 19 13:48:41 2012 +0300

    server/tests: refactor Command

diff --git a/server/tests/test_display_base.c b/server/tests/test_display_base.c
index 4211f29..49b7973 100644
--- a/server/tests/test_display_base.c
+++ b/server/tests/test_display_base.c
@@ -453,7 +453,7 @@ static void produce_command(void)
 
     command = &g_commands[cmd_index];
     if (command->cb) {
-        command->cb(command->cb_opaque, &command->arg1, &command->arg2);
+        command->cb(command);
     }
     switch (command->command) {
         case PATH_PROGRESS:
@@ -511,15 +511,13 @@ static void produce_command(void)
             break;
         }
 
-        case DESTROY_PRIMARY: {
+        case DESTROY_PRIMARY:
             qxl_worker->destroy_primary_surface(qxl_worker, 0);
             break;
-        }
 
-        case CREATE_PRIMARY: {
-            create_primary_surface(qxl_worker, command->arg1, command->arg2);
+        case CREATE_PRIMARY:
+            create_primary_surface(qxl_worker, command->create_primary.width, command->create_primary.height);
             break;
-        }
     }
     cmd_index = (cmd_index + 1) % g_num_commands;
 }
diff --git a/server/tests/test_display_base.h b/server/tests/test_display_base.h
index 6922d9b..b769721 100644
--- a/server/tests/test_display_base.h
+++ b/server/tests/test_display_base.h
@@ -23,13 +23,36 @@ typedef enum {
     DESTROY_PRIMARY,
     CREATE_PRIMARY,
 } CommandType;
-typedef struct Command {
+
+typedef struct CommandCreatePrimary {
+    uint32_t width;
+    uint32_t height;
+} CommandCreatePrimary;
+
+typedef struct CommandDrawBitmap {
+    QXLRect bbox;
+    uint8_t *bitmap;
+    uint32_t surface_id;
+} CommandDrawBitmap;
+
+typedef struct CommandDrawSolid {
+    QXLRect bbox;
+    uint32_t color;
+    uint32_t surface_id;
+} CommandDrawSolid;
+
+typedef struct Command Command;
+
+struct Command {
     CommandType command;
-    uint64_t arg1;
-    uint64_t arg2;
-    void (*cb)(void *cb_opaque, uint64_t *arg1, uint64_t *arg2);
+    void (*cb)(Command *command);
     void *cb_opaque;
-} Command;
+    union {
+        CommandCreatePrimary create_primary;
+        CommandDrawBitmap bitmap;
+        CommandDrawSolid solid;
+    };
+};
 
 void test_set_simple_command_list(int *command, int num_commands);
 void test_set_command_list(Command *command, int num_commands);
diff --git a/server/tests/test_display_resolution_changes.c b/server/tests/test_display_resolution_changes.c
index ef26ee1..4a66a75 100644
--- a/server/tests/test_display_resolution_changes.c
+++ b/server/tests/test_display_resolution_changes.c
@@ -23,7 +23,7 @@ void pinger(void *opaque)
     core->timer_start(ping_timer, ping_ms);
 }
 
-void set_primary_params(void *cb_opaque, uint64_t *arg1, uint64_t *arg2)
+void set_primary_params(Command *command)
 {
 #if 0
     static int toggle = 0;
@@ -39,14 +39,14 @@ void set_primary_params(void *cb_opaque, uint64_t *arg1, uint64_t *arg2)
 #endif
     static int count = 0;
 
-    *arg1 = 800 + sin((float)count / 6) * 200;
-    *arg2 = 600 + cos((float)count / 6) * 200;
+    command->create_primary.width = 800 + sin((float)count / 6) * 200;
+    command->create_primary.height = 600 + cos((float)count / 6) * 200;
     count++;
 }
 
 static Command commands[] = {
-    {DESTROY_PRIMARY, 0, 0, NULL, NULL},
-    {CREATE_PRIMARY, 0, 0, set_primary_params, NULL},
+    {DESTROY_PRIMARY, NULL},
+    {CREATE_PRIMARY, set_primary_params},
 };
 
 int main(void)
commit 703f32cda91db552167324c542e19ef720c474dc
Author: Alon Levy <alevy at redhat.com>
Date:   Thu Apr 19 13:48:40 2012 +0300

    server/tests: add test_get_width/test_get_height

diff --git a/server/tests/test_display_base.c b/server/tests/test_display_base.c
index 3e051b6..4211f29 100644
--- a/server/tests/test_display_base.c
+++ b/server/tests/test_display_base.c
@@ -47,6 +47,9 @@ static void test_spice_destroy_update(SimpleSpiceUpdate *update)
     free(update);
 }
 
+static uint32_t test_width;
+static uint32_t test_height;
+
 #define DEFAULT_WIDTH 640
 #define DEFAULT_HEIGHT 320
 
@@ -320,9 +323,22 @@ static void create_primary_surface(QXLWorker *worker, uint32_t width,
     surface.mem        = (uint64_t)&g_primary_surface;
     surface.group_id   = MEM_SLOT_GROUP_ID;
 
+    test_width = width;
+    test_height = height;
+
     qxl_worker->create_primary_surface(qxl_worker, 0, &surface);
 }
 
+uint32_t test_get_width(void)
+{
+    return test_width;
+}
+
+uint32_t test_get_height(void)
+{
+    return test_height;
+}
+
 QXLDevMemSlot slot = {
 .slot_group_id = MEM_SLOT_GROUP_ID,
 .slot_id = 0,
diff --git a/server/tests/test_display_base.h b/server/tests/test_display_base.h
index fa9fd18..6922d9b 100644
--- a/server/tests/test_display_base.h
+++ b/server/tests/test_display_base.h
@@ -36,6 +36,9 @@ void test_set_command_list(Command *command, int num_commands);
 void test_add_display_interface(SpiceServer *server);
 SpiceServer* test_init(SpiceCoreInterface* core);
 
+uint32_t test_get_width(void);
+uint32_t test_get_height(void);
+
 void spice_test_config_parse_args(int argc, char **argv);
 
 #endif /* __TEST_DISPLAY_BASE_H__ */
commit 2f02599e6a8572a153087f49e8d4f3ac737e4eaf
Author: Alon Levy <alevy at redhat.com>
Date:   Thu Apr 19 13:48:38 2012 +0300

    server/tests: refactor test_display_base

diff --git a/server/tests/test_display_base.c b/server/tests/test_display_base.c
index 3e2ba6c..3e051b6 100644
--- a/server/tests/test_display_base.c
+++ b/server/tests/test_display_base.c
@@ -140,45 +140,23 @@ static void draw_pos(int t, int *x, int *y)
 #endif
 }
 
-static SimpleSpiceUpdate *test_spice_create_update_draw(uint32_t surface_id, int t)
+/* bitmap is freeds, so it must be allocated with malloc */
+SimpleSpiceUpdate *test_spice_create_update_from_bitmap(uint32_t surface_id,
+        QXLRect bbox, uint8_t *bitmap)
 {
     SimpleSpiceUpdate *update;
     QXLDrawable *drawable;
     QXLImage *image;
-    int top, left;
-    draw_pos(t, &left, &top);
-    QXLRect bbox = {
-        .top = top,
-        .left = left,
-    };
-    uint8_t *dst;
-    int bw, bh;
-    int i;
+    uint32_t bw, bh;
 
-    if ((t % angle_parts) == 0) {
-        c_i++;
-    }
-
-    if(surface_id != 0) {
-        color = (color + 1) % 2;
-    } else {
-        color = surface_id;
-    }
-
-    unique++;
+    bh = bbox.bottom - bbox.top;
+    bw = bbox.right - bbox.left;
 
     update   = calloc(sizeof(*update), 1);
+    update->bitmap = bitmap;
     drawable = &update->drawable;
     image    = &update->image;
 
-    bw       = g_primary_width/SINGLE_PART;
-    bh       = 48;
-
-    bbox.right = bbox.left + bw;
-    bbox.bottom = bbox.top + bh;
-    update->bitmap = malloc(bw * bh * 4);
-    //printf("allocated %p, %p\n", update, update->bitmap);
-
     drawable->surface_id      = surface_id;
 
     drawable->bbox            = bbox;
@@ -201,11 +179,43 @@ static SimpleSpiceUpdate *test_spice_create_update_draw(uint32_t surface_id, int
     image->bitmap.stride     = bw * 4;
     image->descriptor.width  = image->bitmap.x = bw;
     image->descriptor.height = image->bitmap.y = bh;
-    image->bitmap.data = (intptr_t)(update->bitmap);
+    image->bitmap.data = (intptr_t)bitmap;
     image->bitmap.palette = 0;
     image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
 
-    dst = update->bitmap;
+    set_cmd(&update->ext, QXL_CMD_DRAW, (intptr_t)drawable);
+
+    return update;
+}
+
+static SimpleSpiceUpdate *test_spice_create_update_draw(uint32_t surface_id, int t)
+{
+    int top, left;
+    uint8_t *dst;
+    uint8_t *bitmap;
+    int bw, bh;
+    int i;
+    QXLRect bbox;
+
+    draw_pos(t, &left, &top);
+    if ((t % angle_parts) == 0) {
+        c_i++;
+    }
+
+    if (surface_id != 0) {
+        color = (color + 1) % 2;
+    } else {
+        color = surface_id;
+    }
+
+    unique++;
+
+    bw       = g_primary_width/SINGLE_PART;
+    bh       = 48;
+
+    bitmap = dst = malloc(bw * bh * 4);
+    //printf("allocated %p\n", dst);
+
     for (i = 0 ; i < bh * bw ; ++i, dst+=4) {
         *dst = (color+i % 255);
         *(dst+((1+c_i)%3)) = 255 - color;
@@ -213,9 +223,9 @@ static SimpleSpiceUpdate *test_spice_create_update_draw(uint32_t surface_id, int
         *(dst+((3+c_i)%3)) = 0;
     }
 
-    set_cmd(&update->ext, QXL_CMD_DRAW, (intptr_t)drawable);
-
-    return update;
+    bbox.left = left; bbox.top = top;
+    bbox.right = left + bw; bbox.bottom = top + bh;
+    return test_spice_create_update_from_bitmap(surface_id, bbox, bitmap);
 }
 
 static SimpleSpiceUpdate *test_spice_create_update_copy_bits(uint32_t surface_id)
@@ -440,6 +450,7 @@ static void produce_command(void)
             break;
         }
 
+        /* Drawing commands, they all push a command to the command ring */
         case SIMPLE_COPY_BITS:
         case SIMPLE_DRAW: {
             SimpleSpiceUpdate *update;
commit 8c9114f9f4245a7ed6c9a9c5be7da81bea4bcf5a
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Mon Apr 30 09:14:24 2012 +0300

    server/red_worker/video: don't override the clip in areas that belong only to sized frames

diff --git a/server/red_worker.c b/server/red_worker.c
index 6926210..473d0d6 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -2454,31 +2454,6 @@ static StreamClipItem *__new_stream_clip(DisplayChannelClient* dcc, StreamAgent
     return item;
 }
 
-static void push_stream_clip_by_drawable(DisplayChannelClient* dcc, StreamAgent *agent,
-                                         Drawable *drawable)
-{
-    StreamClipItem *item = __new_stream_clip(dcc, agent);
-    int n_rects;
-
-    if (!item) {
-        spice_critical("alloc failed");
-    }
-
-    if (drawable->red_drawable->clip.type == SPICE_CLIP_TYPE_NONE) {
-        item->rects = NULL;
-        item->clip_type = SPICE_CLIP_TYPE_NONE;
-        item->rects = NULL;
-    } else {
-        item->clip_type = SPICE_CLIP_TYPE_RECTS;
-        n_rects = pixman_region32_n_rects(&drawable->tree_item.base.rgn);
-
-        item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect), sizeof(SpiceClipRects));
-        item->rects->num_rects = n_rects;
-        region_ret_rects(&drawable->tree_item.base.rgn, item->rects->rects, n_rects);
-    }
-    red_channel_client_pipe_add(&dcc->common.base, (PipeItem *)item);
-}
-
 static void push_stream_clip(DisplayChannelClient* dcc, StreamAgent *agent)
 {
     StreamClipItem *item = __new_stream_clip(dcc, agent);
@@ -2523,13 +2498,20 @@ static void red_attach_stream(RedWorker *worker, Drawable *drawable, Stream *str
     stream->last_time = drawable->creation_time;
 
     WORKER_FOREACH_DCC(worker, item, dcc) {
-        StreamAgent *agent = &dcc->stream_agents[get_stream_id(worker, stream)];
+        StreamAgent *agent;
+        QRegion clip_in_draw_dest;
 
+        agent = &dcc->stream_agents[get_stream_id(worker, stream)];
         region_or(&agent->vis_region, &drawable->tree_item.base.rgn);
-        if (!region_is_equal(&agent->clip, &drawable->tree_item.base.rgn)) {
-            region_destroy(&agent->clip);
-            region_clone(&agent->clip, &drawable->tree_item.base.rgn);
-            push_stream_clip_by_drawable(dcc, agent, drawable);
+
+        region_init(&clip_in_draw_dest);
+        region_add(&clip_in_draw_dest, &drawable->red_drawable->bbox);
+        region_and(&clip_in_draw_dest, &agent->clip);
+
+        if (!region_is_equal(&clip_in_draw_dest, &drawable->tree_item.base.rgn)) {
+            region_remove(&agent->clip, &drawable->red_drawable->bbox);
+            region_or(&agent->clip, &drawable->tree_item.base.rgn);
+            push_stream_clip(dcc, agent);
         }
     }
 }
commit 318dce4abbdaf48817a48a3c6d5972e4223dff06
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Apr 1 08:32:14 2012 +0300

    server/red_worker.c/video: add support for frames of different sizes
    
    rhbz #813826
    
    When playing a youtube video on Windows guest, the driver sometimes(**) sends
    images which contain the video frames, but also other parts of the
    screen (e.g., the youtube process bar). In order to prevent glitches, we send these
    images as part of the stream, using SPICE_MSG_DISPLAY_STREAM_DATA_SIZED.
    
    (**) It happens regularly with the you tube html5 player. With flash,
    it occurs when moving the cursor in the player area.

diff --git a/server/mjpeg_encoder.c b/server/mjpeg_encoder.c
index 4692315..9c3d079 100644
--- a/server/mjpeg_encoder.c
+++ b/server/mjpeg_encoder.c
@@ -25,8 +25,6 @@
 #include <jpeglib.h>
 
 struct MJpegEncoder {
-    int width;
-    int height;
     uint8_t *row;
     int first_frame;
     int quality;
@@ -38,15 +36,13 @@ struct MJpegEncoder {
     void (*pixel_converter)(uint8_t *src, uint8_t *dest);
 };
 
-MJpegEncoder *mjpeg_encoder_new(int width, int height)
+MJpegEncoder *mjpeg_encoder_new(void)
 {
     MJpegEncoder *enc;
 
     enc = spice_new0(MJpegEncoder, 1);
 
     enc->first_frame = TRUE;
-    enc->width = width;
-    enc->height = height;
     enc->quality = 70;
     enc->cinfo.err = jpeg_std_error(&enc->jerr);
     jpeg_create_compress(&enc->cinfo);
@@ -200,6 +196,7 @@ spice_jpeg_mem_dest(j_compress_ptr cinfo,
 /* end of code from libjpeg */
 
 int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,
+                              int width, int height,
                               uint8_t **dest, size_t *dest_len)
 {
     encoder->cinfo.in_color_space   = JCS_RGB;
@@ -233,9 +230,9 @@ int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,
     }
 
     if ((encoder->pixel_converter != NULL) && (encoder->row == NULL)) {
-        unsigned int stride = encoder->width * 3;
+        unsigned int stride = width * 3;
         /* check for integer overflow */
-        if (stride < encoder->width) {
+        if (stride < width) {
             return FALSE;
         }
         encoder->row = spice_malloc(stride);
@@ -243,8 +240,8 @@ int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,
 
     spice_jpeg_mem_dest(&encoder->cinfo, dest, dest_len);
 
-    encoder->cinfo.image_width      = encoder->width;
-    encoder->cinfo.image_height     = encoder->height;
+    encoder->cinfo.image_width      = width;
+    encoder->cinfo.image_height     = height;
     jpeg_set_defaults(&encoder->cinfo);
     encoder->cinfo.dct_method       = JDCT_IFAST;
     jpeg_set_quality(&encoder->cinfo, encoder->quality, TRUE);
diff --git a/server/mjpeg_encoder.h b/server/mjpeg_encoder.h
index c43827f..91297d0 100644
--- a/server/mjpeg_encoder.h
+++ b/server/mjpeg_encoder.h
@@ -23,11 +23,12 @@
 
 typedef struct MJpegEncoder MJpegEncoder;
 
-MJpegEncoder *mjpeg_encoder_new(int width, int height);
+MJpegEncoder *mjpeg_encoder_new(void);
 void mjpeg_encoder_destroy(MJpegEncoder *encoder);
 
 uint8_t mjpeg_encoder_get_bytes_per_pixel(MJpegEncoder *encoder);
 int mjpeg_encoder_start_frame(MJpegEncoder *encoder, SpiceBitmapFmt format,
+                              int width, int height,
                               uint8_t **dest, size_t *dest_len);
 int mjpeg_encoder_encode_scanline(MJpegEncoder *encoder, uint8_t *src_pixels,
                                   size_t image_width);
diff --git a/server/red_worker.c b/server/red_worker.c
index 823c25b..6926210 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -374,6 +374,12 @@ typedef struct ImageItem {
 
 typedef struct Drawable Drawable;
 
+enum {
+    STREAM_FRAME_NONE,
+    STREAM_FRAME_NATIVE,
+    STREAM_FRAME_CONTAINER,
+};
+
 typedef struct Stream Stream;
 struct Stream {
     uint8_t refs;
@@ -792,6 +798,7 @@ struct Drawable {
     int gradual_frames_count;
     int last_gradual_frame;
     Stream *stream;
+    Stream *sized_stream;
     int streamable;
     BitmapGradualType copy_bitmap_graduality;
     uint32_t group_id;
@@ -994,7 +1001,7 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area, int surfac
 static void red_release_cursor(RedWorker *worker, CursorItem *cursor);
 static inline void release_drawable(RedWorker *worker, Drawable *item);
 static void red_display_release_stream(RedWorker *worker, StreamAgent *agent);
-static inline void red_detach_stream(RedWorker *worker, Stream *stream);
+static inline void red_detach_stream(RedWorker *worker, Stream *stream, int detach_sized);
 static void red_stop_stream(RedWorker *worker, Stream *stream);
 static inline void red_stream_maintenance(RedWorker *worker, Drawable *candidate, Drawable *sect);
 static inline void display_begin_send_message(RedChannelClient *rcc);
@@ -1747,7 +1754,7 @@ static inline void release_drawable(RedWorker *worker, Drawable *drawable)
         spice_assert(ring_is_empty(&drawable->pipes));
 
         if (drawable->stream) {
-            red_detach_stream(worker, drawable->stream);
+            red_detach_stream(worker, drawable->stream, TRUE);
         }
         region_destroy(&drawable->tree_item.base.rgn);
 
@@ -2424,11 +2431,14 @@ static void red_release_stream(RedWorker *worker, Stream *stream)
     }
 }
 
-static inline void red_detach_stream(RedWorker *worker, Stream *stream)
+static inline void red_detach_stream(RedWorker *worker, Stream *stream, int detach_sized)
 {
     spice_assert(stream->current && stream->current->stream);
     spice_assert(stream->current->stream == stream);
     stream->current->stream = NULL;
+    if (detach_sized) {
+        stream->current->sized_stream = NULL;
+    }
     stream->current = NULL;
 }
 
@@ -2531,6 +2541,7 @@ static void red_stop_stream(RedWorker *worker, Stream *stream)
 
     spice_assert(ring_item_is_linked(&stream->link));
     spice_assert(!stream->current);
+    spice_debug("stream %d", get_stream_id(worker, stream));
     WORKER_FOREACH_DCC(worker, item, dcc) {
         StreamAgent *stream_agent;
         stream_agent = &dcc->stream_agents[get_stream_id(worker, stream)];
@@ -2586,11 +2597,13 @@ static inline void red_display_detach_stream_gracefully(DisplayChannelClient *dc
         /* (1) The caller should detach the drawable from the stream. This will
          * lead to sending the drawable losslessly, as an ordinary drawable. */
         if (red_display_drawable_is_in_pipe(dcc, stream->current)) {
-            spice_debug("stream %d: upgrade by linked drawable. box ==>", stream_id);
+            spice_debug("stream %d: upgrade by linked drawable. sized %d, box ==>",
+                        stream_id, stream->current->sized_stream != NULL);
             rect_debug(&stream->current->red_drawable->bbox);
             return;
         }
-        spice_debug("stream %d: upgrade by drawable. box ==>", stream_id);
+        spice_debug("stream %d: upgrade by drawable. sized %d, box ==>",
+                    stream_id, stream->current->sized_stream != NULL);
         rect_debug(&stream->current->red_drawable->bbox);
         rcc = &dcc->common.base;
         channel = rcc->channel;
@@ -2629,7 +2642,7 @@ static inline void red_detach_stream_gracefully(RedWorker *worker, Stream *strea
         red_display_detach_stream_gracefully(dcc, stream);
     }
     if (stream->current) {
-        red_detach_stream(worker, stream);
+        red_detach_stream(worker, stream, TRUE);
     }
 }
 
@@ -2653,14 +2666,15 @@ static void red_detach_streams_behind(RedWorker *worker, QRegion *region)
             if (region_intersects(&agent->vis_region, region)) {
                 red_display_detach_stream_gracefully(dcc, stream);
                 detach_stream = 1;
+                spice_debug("stream %d", get_stream_id(worker, stream));
             }
         }
         if (detach_stream && stream->current) {
-            red_detach_stream(worker, stream);
+            red_detach_stream(worker, stream, TRUE);
         } else if (!has_clients) {
             if (stream->current &&
                 region_intersects(&stream->current->tree_item.base.rgn, region)) {
-                red_detach_stream(worker, stream);
+                red_detach_stream(worker, stream, TRUE);
             }
         }
     }
@@ -2840,7 +2854,7 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
     stream_width = src_rect->right - src_rect->left;
     stream_height = src_rect->bottom - src_rect->top;
 
-    stream->mjpeg_encoder = mjpeg_encoder_new(stream_width, stream_height);
+    stream->mjpeg_encoder = mjpeg_encoder_new();
 
     ring_add(&worker->streams, &stream->link);
     stream->current = drawable;
@@ -2858,7 +2872,9 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
         red_display_create_stream(dcc, stream);
     }
     worker->stream_count++;
-
+    spice_debug("stream %d %dx%d (%d, %d) (%d, %d)", (int)(stream - worker->streams_buf), stream->width,
+    stream->height, stream->dest_area.left, stream->dest_area.top,
+    stream->dest_area.right, stream->dest_area.bottom);
     return;
 }
 
@@ -2919,34 +2935,64 @@ static inline int __red_is_next_stream_frame(RedWorker *worker,
                                              const int other_src_height,
                                              const SpiceRect *other_dest,
                                              const red_time_t other_time,
-                                             const Stream *stream)
+                                             const Stream *stream,
+                                             int container_candidate_allowed)
 {
     RedDrawable *red_drawable;
+    int is_frame_container = FALSE;
 
     if (candidate->creation_time - other_time >
             (stream ? RED_STREAM_CONTINUS_MAX_DELTA : RED_STREAM_DETACTION_MAX_DELTA)) {
-        return FALSE;
+        return STREAM_FRAME_NONE;
     }
 
     red_drawable = candidate->red_drawable;
+    if (!container_candidate_allowed) {
+        SpiceRect* candidate_src;
 
-    if (!rect_is_equal(&red_drawable->bbox, other_dest)) {
-        return FALSE;
-    }
+        if (!rect_is_equal(&red_drawable->bbox, other_dest)) {
+            return STREAM_FRAME_NONE;
+        }
 
-    SpiceRect* candidate_src = &red_drawable->u.copy.src_area;
-    if (candidate_src->right - candidate_src->left != other_src_width ||
-        candidate_src->bottom - candidate_src->top != other_src_height) {
-        return FALSE;
+        candidate_src = &red_drawable->u.copy.src_area;
+        if (candidate_src->right - candidate_src->left != other_src_width ||
+            candidate_src->bottom - candidate_src->top != other_src_height) {
+            return STREAM_FRAME_NONE;
+        }
+    } else {
+        if (rect_contains(&red_drawable->bbox, other_dest)) {
+            int candidate_area = rect_get_area(&red_drawable->bbox);
+            int other_area = rect_get_area(other_dest);
+            /* do not stream drawables that are significantly
+             * bigger than the original frame */
+            if (candidate_area > 2 * other_area) {
+                spice_debug("too big candidate:");
+                spice_debug("prev box ==>");
+                rect_debug(other_dest);
+                spice_debug("new box ==>");
+                rect_debug(&red_drawable->bbox);
+                return STREAM_FRAME_NONE;
+            }
+
+            if (candidate_area > other_area) {
+                is_frame_container = TRUE;
+            }
+        } else {
+            return STREAM_FRAME_NONE;
+        }
     }
 
     if (stream) {
         SpiceBitmap *bitmap = &red_drawable->u.copy.src_bitmap->u.bitmap;
         if (stream->top_down != !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
-            return FALSE;
+            return STREAM_FRAME_NONE;
         }
     }
-    return TRUE;
+    if (is_frame_container) {
+        return STREAM_FRAME_CONTAINER;
+    } else {
+        return STREAM_FRAME_NATIVE;
+    }
 }
 
 static inline int red_is_next_stream_frame(RedWorker *worker, const Drawable *candidate,
@@ -2960,7 +3006,8 @@ static inline int red_is_next_stream_frame(RedWorker *worker, const Drawable *ca
     return __red_is_next_stream_frame(worker, candidate, prev_src->right - prev_src->left,
                                       prev_src->bottom - prev_src->top,
                                       &prev->red_drawable->bbox, prev->creation_time,
-                                      prev->stream);
+                                      prev->stream,
+                                      FALSE);
 }
 
 static void reset_rate(DisplayChannelClient *dcc, StreamAgent *stream_agent)
@@ -3102,20 +3149,31 @@ static inline void red_stream_maintenance(RedWorker *worker, Drawable *candidate
         return;
     }
 
-    if (!red_is_next_stream_frame(worker, candidate, prev)) {
-        return;
-    }
-
     if ((stream = prev->stream)) {
-        pre_stream_item_swap(worker, stream);
-        red_detach_stream(worker, stream);
-        prev->streamable = FALSE; //prevent item trace
-        red_attach_stream(worker, candidate, stream);
+        int is_next_frame = __red_is_next_stream_frame(worker,
+                                                       candidate,
+                                                       stream->width,
+                                                       stream->height,
+                                                       &stream->dest_area,
+                                                       stream->last_time,
+                                                       stream,
+                                                       TRUE);
+        if (is_next_frame != STREAM_FRAME_NONE) {
+            pre_stream_item_swap(worker, stream);
+            red_detach_stream(worker, stream, FALSE);
+            prev->streamable = FALSE; //prevent item trace
+            red_attach_stream(worker, candidate, stream);
+            if (is_next_frame == STREAM_FRAME_CONTAINER) {
+                candidate->sized_stream = stream;
+            }
+        }
     } else {
-        red_stream_add_frame(worker, candidate,
-                             prev->frames_count,
-                             prev->gradual_frames_count,
-                             prev->last_gradual_frame);
+        if (red_is_next_stream_frame(worker, candidate, prev) != STREAM_FRAME_NONE) {
+            red_stream_add_frame(worker, candidate,
+                                 prev->frames_count,
+                                 prev->gradual_frames_count,
+                                 prev->last_gradual_frame);
+        }
     }
 }
 
@@ -3243,14 +3301,24 @@ static inline void red_use_stream_trace(RedWorker *worker, Drawable *drawable)
 
     while (item) {
         Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
-        if (!stream->current && __red_is_next_stream_frame(worker,
-                                                           drawable,
-                                                           stream->width,
-                                                           stream->height,
-                                                           &stream->dest_area,
-                                                           stream->last_time,
-                                                           stream)) {
+        int is_next_frame = __red_is_next_stream_frame(worker,
+                                                       drawable,
+                                                       stream->width,
+                                                       stream->height,
+                                                       &stream->dest_area,
+                                                       stream->last_time,
+                                                       stream,
+                                                       TRUE);
+        if (is_next_frame != STREAM_FRAME_NONE) {
+            if (stream->current) {
+                stream->current->streamable = FALSE; //prevent item trace
+                pre_stream_item_swap(worker, stream);
+                red_detach_stream(worker, stream, FALSE);
+            }
             red_attach_stream(worker, drawable, stream);
+            if (is_next_frame == STREAM_FRAME_CONTAINER) {
+                drawable->sized_stream = stream;
+            }
             return;
         }
         item = ring_next(ring, item);
@@ -3260,7 +3328,8 @@ static inline void red_use_stream_trace(RedWorker *worker, Drawable *drawable)
     trace_end = trace + NUM_TRACE_ITEMS;
     for (; trace < trace_end; trace++) {
         if (__red_is_next_stream_frame(worker, drawable, trace->width, trace->height,
-                                       &trace->dest_area, trace->time, NULL)) {
+                                       &trace->dest_area, trace->time, NULL, FALSE) !=
+                                       STREAM_FRAME_NONE) {
             if (red_stream_add_frame(worker, drawable,
                                      trace->frames_count,
                                      trace->gradual_frames_count,
@@ -8097,8 +8166,12 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
     SpiceImage *image;
     RedWorker *worker = dcc->common.worker;
     int n;
+    int width, height;
 
-    spice_assert(stream);
+    if (!stream) {
+        spice_assert(drawable->sized_stream);
+        stream = drawable->sized_stream;
+    }
     spice_assert(drawable->red_drawable->type == QXL_DRAW_COPY);
 
     worker = display_channel->common.worker;
@@ -8108,6 +8181,20 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
         return FALSE;
     }
 
+    if (drawable->sized_stream) {
+        if (red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_SIZED_STREAM)) {
+            SpiceRect *src_rect = &drawable->red_drawable->u.copy.src_area;
+
+            width = src_rect->right - src_rect->left;
+            height = src_rect->bottom - src_rect->top;
+        } else {
+            return FALSE;
+        }
+    } else {
+        width = stream->width;
+        height = stream->height;
+    }
+
     StreamAgent *agent = &dcc->stream_agents[get_stream_id(worker, stream)];
     uint64_t time_now = red_now();
     size_t outbuf_size;
@@ -8118,6 +8205,7 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
 
     outbuf_size = dcc->send_data.stream_outbuf_size;
     if (!mjpeg_encoder_start_frame(stream->mjpeg_encoder, image->u.bitmap.format,
+                                   width, height,
                                    &dcc->send_data.stream_outbuf,
                                    &outbuf_size)) {
         return FALSE;
@@ -8129,14 +8217,32 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
     n = mjpeg_encoder_end_frame(stream->mjpeg_encoder);
     dcc->send_data.stream_outbuf_size = outbuf_size;
 
-    red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA, NULL);
+    if (!drawable->sized_stream) {
+        SpiceMsgDisplayStreamData stream_data;
+
+        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA, NULL);
 
-    SpiceMsgDisplayStreamData stream_data;
+        stream_data.base.id = get_stream_id(worker, stream);
+        stream_data.base.multi_media_time = drawable->red_drawable->mm_time;
+        stream_data.data_size = n;
 
-    stream_data.base.id = get_stream_id(worker, stream);
-    stream_data.base.multi_media_time = drawable->red_drawable->mm_time;
-    stream_data.data_size = n;
-    spice_marshall_msg_display_stream_data(base_marshaller, &stream_data);
+        spice_marshall_msg_display_stream_data(base_marshaller, &stream_data);
+    } else {
+        SpiceMsgDisplayStreamDataSized stream_data;
+
+        red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA_SIZED, NULL);
+
+        stream_data.base.id = get_stream_id(worker, stream);
+        stream_data.base.multi_media_time = drawable->red_drawable->mm_time;
+        stream_data.data_size = n;
+        stream_data.width = width;
+        stream_data.height = height;
+        stream_data.dest = drawable->red_drawable->bbox;
+
+        spice_debug("stream %d: sized frame: dest ==> ", stream_data.base.id);
+        rect_debug(&stream_data.dest);
+        spice_marshall_msg_display_stream_data_sized(base_marshaller, &stream_data);
+    }
     spice_marshaller_add_ref(base_marshaller,
                              dcc->send_data.stream_outbuf, n);
     agent->last_send_time = time_now;
@@ -8150,7 +8256,9 @@ static inline void marshall_qxl_drawable(RedChannelClient *rcc,
     DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
 
     spice_assert(display_channel && rcc);
-    if (item->stream && red_marshall_stream_data(rcc, m, item)) {
+    /* allow sized frames to be streamed, even if they where replaced by another frame, since
+     * newer frames might not cover sized frames completely if they are bigger */
+    if ((item->stream || item->sized_stream) && red_marshall_stream_data(rcc, m, item)) {
         return;
     }
     if (!display_channel->enable_jpeg)
@@ -8400,7 +8508,6 @@ static void red_display_marshall_upgrade(RedChannelClient *rcc, SpiceMarshaller
     SpiceMarshaller *src_bitmap_out, *mask_bitmap_out;
 
     spice_assert(rcc && rcc->channel && item && item->drawable);
-
     red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_COPY, &item->base);
 
     red_drawable = item->drawable->red_drawable;
commit 0f1fb0ecc2a2f5a3086b7697e26228c16b97693e
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Wed May 2 14:59:19 2012 +0300

    Update the spice-common submodule
    
    spice-common changes: STREAM_DATA_SIZED message was added in order to support
    video streams with frames that their size is different from the initial size
    that the stream was created with.
    
    This patch also includes server and client adjustments to the new
    SpiceMsgDisplayStreamData.

diff --git a/client/display_channel.cpp b/client/display_channel.cpp
index ebeacd2..17bdf6a 100644
--- a/client/display_channel.cpp
+++ b/client/display_channel.cpp
@@ -1419,7 +1419,7 @@ void DisplayChannel::handle_stream_data(RedPeer::InMessage* message)
     SpiceMsgDisplayStreamData* stream_data = (SpiceMsgDisplayStreamData*)message->data();
     VideoStream* stream;
 
-    if (stream_data->id >= _streams.size() || !(stream = _streams[stream_data->id])) {
+    if (stream_data->base.id >= _streams.size() || !(stream = _streams[stream_data->base.id])) {
         THROW("invalid stream");
     }
 
@@ -1427,7 +1427,9 @@ void DisplayChannel::handle_stream_data(RedPeer::InMessage* message)
         THROW("access violation");
     }
 
-    stream->push_data(stream_data->multi_media_time, stream_data->data_size, stream_data->data);
+    stream->push_data(stream_data->base.multi_media_time,
+                      stream_data->data_size,
+                      stream_data->data);
 }
 
 void DisplayChannel::handle_stream_clip(RedPeer::InMessage* message)
diff --git a/server/red_worker.c b/server/red_worker.c
index 85ad7b8..823c25b 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -8133,8 +8133,8 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
 
     SpiceMsgDisplayStreamData stream_data;
 
-    stream_data.id = get_stream_id(worker, stream);
-    stream_data.multi_media_time = drawable->red_drawable->mm_time;
+    stream_data.base.id = get_stream_id(worker, stream);
+    stream_data.base.multi_media_time = drawable->red_drawable->mm_time;
     stream_data.data_size = n;
     spice_marshall_msg_display_stream_data(base_marshaller, &stream_data);
     spice_marshaller_add_ref(base_marshaller,
diff --git a/spice-common b/spice-common
index 178c7ea..22fc0b0 160000
--- a/spice-common
+++ b/spice-common
@@ -1 +1 @@
-Subproject commit 178c7eaff6fa45b9051bb4d3cf90f45ea9319f83
+Subproject commit 22fc0b0145876b90385c1c88923bcd72a6380812
commit 21b857f3ce568735a8c7951652f3e412d69b6df8
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Mon Apr 30 09:09:57 2012 +0300

    server/red_worker/video: don't detach a drawable from a stream due to it being rendered.
    
    The previous patch took care that streams will be upgraded by a surface
    screenshot and not the last frame, if necessary. Thus, there is no
    more a reason for detaching drawables from streams when they are
    rendered. Moreover, detaching such drawables can cause glitches, in
    case they are still in the pipe, and red_update_area is called
    frequently and leads to stream frames rendering.

diff --git a/server/red_worker.c b/server/red_worker.c
index dc66fc1..85ad7b8 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -1743,9 +1743,12 @@ static inline void release_drawable(RedWorker *worker, Drawable *drawable)
     RingItem *item, *next;
 
     if (!--drawable->refs) {
-        spice_assert(!drawable->stream);
         spice_assert(!drawable->tree_item.shadow);
         spice_assert(ring_is_empty(&drawable->pipes));
+
+        if (drawable->stream) {
+            red_detach_stream(worker, drawable->stream);
+        }
         region_destroy(&drawable->tree_item.base.rgn);
 
         remove_drawable_dependencies(worker, drawable);
@@ -1846,9 +1849,7 @@ static inline void current_remove_drawable(RedWorker *worker, Drawable *item)
     if (item->tree_item.effect != QXL_EFFECT_OPAQUE) {
         worker->transparent_count--;
     }
-    if (item->stream) {
-        red_detach_stream(worker, item->stream);
-    } else {
+    if (!item->stream) {
         red_add_item_trace(worker, item);
     }
     remove_shadow(worker, &item->tree_item);
@@ -2741,9 +2742,7 @@ static inline void red_handle_streams_timout(RedWorker *worker)
         Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
         item = ring_next(ring, item);
         if (now >= (stream->last_time + RED_STREAM_TIMOUT)) {
-            if (stream->current) {
-                red_detach_stream_gracefully(worker, stream);
-            }
+            red_detach_stream_gracefully(worker, stream);
             red_stop_stream(worker, stream);
         }
     }
@@ -4504,7 +4503,8 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area, int surfac
 #ifdef ACYCLIC_SURFACE_DEBUG
     int gn;
 #endif
-
+    spice_debug("surface %d: area ==>", surface_id);
+    rect_debug(area);
     surface = &worker->surfaces[surface_id];
 
     last = NULL;
commit 81fe00b08ad4f8abe848f9840c4879e8c1e645c1
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Apr 29 12:40:25 2012 +0300

    server/red_worker/video: upgrade stream by a screenshot instead of the last frame, if needed
    
    Upgrading a stream: When the stream's visible region is bigger than the one of the last
    frame, we will send an updated screenshot of the visible region, instead
    of sending the last frame losslessly.

diff --git a/server/red_worker.c b/server/red_worker.c
index d95a9bb..dc66fc1 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -1006,6 +1006,8 @@ static void display_channel_push_release(DisplayChannelClient *dcc, uint8_t type
 static void red_display_release_stream_clip(RedWorker *worker, StreamClipItem *item);
 static int red_display_free_some_independent_glz_drawables(DisplayChannelClient *dcc);
 static void red_display_free_glz_drawable(DisplayChannelClient *dcc, RedGlzDrawable *drawable);
+static ImageItem *red_add_surface_area_image(DisplayChannelClient *dcc, int surface_id,
+                                             SpiceRect *area, PipeItem *pos, int can_lossy);
 static void reset_rate(DisplayChannelClient *dcc, StreamAgent *stream_agent);
 static BitmapGradualType _get_bitmap_graduality_level(RedWorker *worker, SpiceBitmap *bitmap,
                                                       uint32_t group_id);
@@ -2541,25 +2543,54 @@ static void red_stop_stream(RedWorker *worker, Stream *stream)
     red_release_stream(worker, stream);
 }
 
-static int drawable_is_linked(Drawable *drawable)
+static int red_display_drawable_is_in_pipe(DisplayChannelClient *dcc, Drawable *drawable)
 {
-    return !ring_is_empty(&drawable->pipes);
+    DrawablePipeItem *dpi;
+    RingItem *dpi_link;
+
+    DRAWABLE_FOREACH_DPI(drawable, dpi_link, dpi) {
+        if (dpi->dcc == dcc) {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
 }
 
-static inline void red_detach_stream_gracefully(RedWorker *worker, Stream *stream)
+/*
+ * after red_display_detach_stream_gracefully is called for all the display channel clients,
+ * red_detach_stream should be called. See comment (1).
+ */
+static inline void red_display_detach_stream_gracefully(DisplayChannelClient *dcc, Stream *stream)
 {
-    RedChannel *channel;
-    RingItem *item;
-    RedChannelClient *rcc;
-    DisplayChannelClient *dcc;
+    int stream_id = get_stream_id(dcc->common.worker, stream);
+    StreamAgent *agent = &dcc->stream_agents[stream_id];
 
-    spice_assert(stream->current);
-    WORKER_FOREACH_DCC(worker, item, dcc) {
+    /* stopping the client from playing older frames at once*/
+    region_clear(&agent->clip);
+    push_stream_clip(dcc, agent);
+
+    if (region_is_empty(&agent->vis_region)) {
+        spice_debug("stream %d: vis region empty", stream_id);
+        return;
+    }
+
+    if (stream->current &&
+        region_contains(&stream->current->tree_item.base.rgn, &agent->vis_region)) {
+        RedChannel *channel;
+        RedChannelClient *rcc;
         UpgradeItem *upgrade_item;
         int n_rects;
-        if (drawable_is_linked(stream->current)) {
-            continue;
+
+        /* (1) The caller should detach the drawable from the stream. This will
+         * lead to sending the drawable losslessly, as an ordinary drawable. */
+        if (red_display_drawable_is_in_pipe(dcc, stream->current)) {
+            spice_debug("stream %d: upgrade by linked drawable. box ==>", stream_id);
+            rect_debug(&stream->current->red_drawable->bbox);
+            return;
         }
+        spice_debug("stream %d: upgrade by drawable. box ==>", stream_id);
+        rect_debug(&stream->current->red_drawable->bbox);
         rcc = &dcc->common.base;
         channel = rcc->channel;
         upgrade_item = spice_new(UpgradeItem, 1);
@@ -2574,8 +2605,31 @@ static inline void red_detach_stream_gracefully(RedWorker *worker, Stream *strea
         region_ret_rects(&upgrade_item->drawable->tree_item.base.rgn,
                          upgrade_item->rects->rects, n_rects);
         red_channel_client_pipe_add(rcc, &upgrade_item->base);
+
+    } else {
+        SpiceRect upgrade_area;
+
+        region_extents(&agent->vis_region, &upgrade_area);
+        spice_debug("stream %d: upgrade by screenshot. has current %d. box ==>",
+                    stream_id, stream->current != NULL);
+        rect_debug(&upgrade_area);
+        red_update_area(dcc->common.worker, &upgrade_area, 0);
+        red_add_surface_area_image(dcc, 0, &upgrade_area, NULL, FALSE);
+    }
+
+}
+
+static inline void red_detach_stream_gracefully(RedWorker *worker, Stream *stream)
+{
+    RingItem *item;
+    DisplayChannelClient *dcc;
+
+    WORKER_FOREACH_DCC(worker, item, dcc) {
+        red_display_detach_stream_gracefully(dcc, stream);
+    }
+    if (stream->current) {
+        red_detach_stream(worker, stream);
     }
-    red_detach_stream(worker, stream);
 }
 
 // region should be a primary surface region
@@ -2594,24 +2648,20 @@ static void red_detach_streams_behind(RedWorker *worker, QRegion *region)
 
         WORKER_FOREACH_DCC(worker, dcc_ring_item, dcc) {
             StreamAgent *agent = &dcc->stream_agents[get_stream_id(worker, stream)];
+
             if (region_intersects(&agent->vis_region, region)) {
-                /* hiding the stream at the client side at once */
-                region_clear(&agent->clip);
-                push_stream_clip(dcc, agent);
+                red_display_detach_stream_gracefully(dcc, stream);
                 detach_stream = 1;
             }
         }
-        if (!has_clients) {
+        if (detach_stream && stream->current) {
+            red_detach_stream(worker, stream);
+        } else if (!has_clients) {
             if (stream->current &&
                 region_intersects(&stream->current->tree_item.base.rgn, region)) {
                 red_detach_stream(worker, stream);
             }
         }
-        if (detach_stream) {
-            if (stream->current) {
-                red_detach_stream_gracefully(worker, stream);
-            }
-        }
     }
 }
 
commit 80f0865b44f903c8b009471377dfbe86a79ece06
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Apr 29 11:55:01 2012 +0300

    server/red_worker/video: maintain visible region and clip region for streams
    
    Differentiate between the clipping of the video stream, and the region
    that currently displays fragments of the video stream (henceforth,
    vis_region). The latter equals or contains the former one. For example,
    let c1 be the clip area at time t1, and c2 be the clip area at time t2,
    where t1 < t2. If c1 contains c2, and at least part of c1/c2, hasn't been
    covered by a non-video images, vis_region will contain c2, and also the part
    of c1/c2 that still displays fragments of the video.
    When we consider if a stream should be "upgraded" (1), due to its area
    being used by a rendering operation, or due to stopping the video, we
    should take into account the vis_region, and not the clip region (next
    patch: not upgrade by the last frame, but rather by vis_region).
    This fix will be more necessary when sized frames are introduced (see the
    following patches). Then, the vis_region might be larger
    than the last frame, and contain it, more frequently than before.
    
    (1) "upgrading a stream" stands for sending its last frame losslessly. Or more
    precisely, lossless resending of all the currently displayed lossy areas, that were
    sent as part of the stream.

diff --git a/server/red_worker.c b/server/red_worker.c
index 9456a55..d95a9bb 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -390,7 +390,15 @@ struct Stream {
 };
 
 typedef struct StreamAgent {
-    QRegion vis_region;
+    QRegion vis_region; /* the part of the surface area that is currently occupied by video
+                           fragments */
+    QRegion clip;       /* the current video clipping. It can be different from vis_region:
+                           for example, let c1 be the clip area at time t1, and c2
+                           be the clip area at time t2, where t1 < t2. If c1 contains c2, and
+                           at least part of c1/c2, hasn't been covered by a non-video images,
+                           vis_region will contain c2 and also the part of c1/c2 that still
+                           displays fragments of the video */
+
     PipeItem create_item;
     PipeItem destroy_item;
     Stream *stream;
@@ -2468,11 +2476,11 @@ static void push_stream_clip(DisplayChannelClient* dcc, StreamAgent *agent)
     }
     item->clip_type = SPICE_CLIP_TYPE_RECTS;
 
-    n_rects = pixman_region32_n_rects(&agent->vis_region);
+    n_rects = pixman_region32_n_rects(&agent->clip);
 
     item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect), sizeof(SpiceClipRects));
     item->rects->num_rects = n_rects;
-    region_ret_rects(&agent->vis_region, item->rects->rects, n_rects);
+    region_ret_rects(&agent->clip, item->rects->rects, n_rects);
     red_channel_client_pipe_add(&dcc->common.base, (PipeItem *)item);
 }
 
@@ -2493,7 +2501,6 @@ static inline int get_stream_id(RedWorker *worker, Stream *stream)
 static void red_attach_stream(RedWorker *worker, Drawable *drawable, Stream *stream)
 {
     DisplayChannelClient *dcc;
-    StreamAgent *agent;
     RingItem *item;
 
     spice_assert(!drawable->stream && !stream->current);
@@ -2503,10 +2510,12 @@ static void red_attach_stream(RedWorker *worker, Drawable *drawable, Stream *str
     stream->last_time = drawable->creation_time;
 
     WORKER_FOREACH_DCC(worker, item, dcc) {
-        agent = &dcc->stream_agents[get_stream_id(worker, stream)];
-        if (!region_is_equal(&agent->vis_region, &drawable->tree_item.base.rgn)) {
-            region_destroy(&agent->vis_region);
-            region_clone(&agent->vis_region, &drawable->tree_item.base.rgn);
+        StreamAgent *agent = &dcc->stream_agents[get_stream_id(worker, stream)];
+
+        region_or(&agent->vis_region, &drawable->tree_item.base.rgn);
+        if (!region_is_equal(&agent->clip, &drawable->tree_item.base.rgn)) {
+            region_destroy(&agent->clip);
+            region_clone(&agent->clip, &drawable->tree_item.base.rgn);
             push_stream_clip_by_drawable(dcc, agent, drawable);
         }
     }
@@ -2523,6 +2532,7 @@ static void red_stop_stream(RedWorker *worker, Stream *stream)
         StreamAgent *stream_agent;
         stream_agent = &dcc->stream_agents[get_stream_id(worker, stream)];
         region_clear(&stream_agent->vis_region);
+        region_clear(&stream_agent->clip);
         spice_assert(!pipe_item_is_linked(&stream_agent->destroy_item));
         stream->refs++;
         red_channel_client_pipe_add(&dcc->common.base, &stream_agent->destroy_item);
@@ -2585,7 +2595,8 @@ static void red_detach_streams_behind(RedWorker *worker, QRegion *region)
         WORKER_FOREACH_DCC(worker, dcc_ring_item, dcc) {
             StreamAgent *agent = &dcc->stream_agents[get_stream_id(worker, stream)];
             if (region_intersects(&agent->vis_region, region)) {
-                region_clear(&agent->vis_region);
+                /* hiding the stream at the client side at once */
+                region_clear(&agent->clip);
                 push_stream_clip(dcc, agent);
                 detach_stream = 1;
             }
@@ -2604,7 +2615,7 @@ static void red_detach_streams_behind(RedWorker *worker, QRegion *region)
     }
 }
 
-static void red_streams_update_clip(RedWorker *worker, Drawable *drawable)
+static void red_streams_update_visible_region(RedWorker *worker, Drawable *drawable)
 {
     Ring *ring;
     RingItem *item;
@@ -2637,6 +2648,7 @@ static void red_streams_update_clip(RedWorker *worker, Drawable *drawable)
 
             if (region_intersects(&agent->vis_region, &drawable->tree_item.base.rgn)) {
                 region_exclude(&agent->vis_region, &drawable->tree_item.base.rgn);
+                region_exclude(&agent->clip, &drawable->tree_item.base.rgn);
                 push_stream_clip(dcc, agent);
             }
         }
@@ -2747,6 +2759,7 @@ static void red_display_create_stream(DisplayChannelClient *dcc, Stream *stream)
     if (stream->current) {
         agent->frames = 1;
         region_clone(&agent->vis_region, &stream->current->tree_item.base.rgn);
+        region_clone(&agent->clip, &agent->vis_region);
     } else {
         agent->frames = 0;
     }
@@ -2821,6 +2834,7 @@ static void red_display_client_init_streams(DisplayChannelClient *dcc)
         StreamAgent *agent = &dcc->stream_agents[i];
         agent->stream = &worker->streams_buf[i];
         region_init(&agent->vis_region);
+        region_init(&agent->clip);
         red_channel_pipe_item_init(channel, &agent->create_item, PIPE_ITEM_TYPE_STREAM_CREATE);
         red_channel_pipe_item_init(channel, &agent->destroy_item, PIPE_ITEM_TYPE_STREAM_DESTROY);
     }
@@ -2833,6 +2847,7 @@ static void red_display_destroy_streams(DisplayChannelClient *dcc)
     for (i = 0; i < NUM_STREAMS; i++) {
         StreamAgent *agent = &dcc->stream_agents[i];
         region_destroy(&agent->vis_region);
+        region_destroy(&agent->clip);
     }
 }
 
@@ -3327,7 +3342,7 @@ static inline int red_current_add(RedWorker *worker, Ring *ring, Drawable *drawa
         region_or(&exclude_rgn, &item->base.rgn);
         exclude_region(worker, ring, exclude_base, &exclude_rgn, NULL, drawable);
         red_use_stream_trace(worker, drawable);
-        red_streams_update_clip(worker, drawable);
+        red_streams_update_visible_region(worker, drawable);
     } else {
         if (drawable->surface_id == 0) {
             red_detach_streams_behind(worker, &drawable->tree_item.base.rgn);
@@ -3397,7 +3412,7 @@ static inline int red_current_add_with_shadow(RedWorker *worker, Ring *ring, Dra
         region_clone(&exclude_rgn, &item->tree_item.base.rgn);
         exclude_region(worker, ring, &shadow->base.siblings_link, &exclude_rgn, NULL, NULL);
         region_destroy(&exclude_rgn);
-        red_streams_update_clip(worker, item);
+        red_streams_update_visible_region(worker, item);
     } else {
         if (item->surface_id == 0) {
             red_detach_streams_behind(worker, &item->tree_item.base.rgn);
commit 3ccc9de184f9cb499a4f887779c9fb9bffba3210
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Wed May 2 10:47:00 2012 +0300

    server/red_worker: add get_stream_id

diff --git a/server/red_worker.c b/server/red_worker.c
index e341c00..9456a55 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -2485,6 +2485,11 @@ static void red_display_release_stream_clip(RedWorker *worker, StreamClipItem *i
     }
 }
 
+static inline int get_stream_id(RedWorker *worker, Stream *stream)
+{
+    return (int)(stream - worker->streams_buf);
+}
+
 static void red_attach_stream(RedWorker *worker, Drawable *drawable, Stream *stream)
 {
     DisplayChannelClient *dcc;
@@ -2498,7 +2503,7 @@ static void red_attach_stream(RedWorker *worker, Drawable *drawable, Stream *str
     stream->last_time = drawable->creation_time;
 
     WORKER_FOREACH_DCC(worker, item, dcc) {
-        agent = &dcc->stream_agents[stream - worker->streams_buf];
+        agent = &dcc->stream_agents[get_stream_id(worker, stream)];
         if (!region_is_equal(&agent->vis_region, &drawable->tree_item.base.rgn)) {
             region_destroy(&agent->vis_region);
             region_clone(&agent->vis_region, &drawable->tree_item.base.rgn);
@@ -2516,7 +2521,7 @@ static void red_stop_stream(RedWorker *worker, Stream *stream)
     spice_assert(!stream->current);
     WORKER_FOREACH_DCC(worker, item, dcc) {
         StreamAgent *stream_agent;
-        stream_agent = &dcc->stream_agents[stream - worker->streams_buf];
+        stream_agent = &dcc->stream_agents[get_stream_id(worker, stream)];
         region_clear(&stream_agent->vis_region);
         spice_assert(!pipe_item_is_linked(&stream_agent->destroy_item));
         stream->refs++;
@@ -2578,7 +2583,7 @@ static void red_detach_streams_behind(RedWorker *worker, QRegion *region)
         item = ring_next(ring, item);
 
         WORKER_FOREACH_DCC(worker, dcc_ring_item, dcc) {
-            StreamAgent *agent = &dcc->stream_agents[stream - worker->streams_buf];
+            StreamAgent *agent = &dcc->stream_agents[get_stream_id(worker, stream)];
             if (region_intersects(&agent->vis_region, region)) {
                 region_clear(&agent->vis_region);
                 push_stream_clip(dcc, agent);
@@ -2628,7 +2633,7 @@ static void red_streams_update_clip(RedWorker *worker, Drawable *drawable)
         }
 
         WORKER_FOREACH_DCC(worker, dcc_ring_item, dcc) {
-            agent = &dcc->stream_agents[stream - worker->streams_buf];
+            agent = &dcc->stream_agents[get_stream_id(worker, stream)];
 
             if (region_intersects(&agent->vis_region, &drawable->tree_item.base.rgn)) {
                 region_exclude(&agent->vis_region, &drawable->tree_item.base.rgn);
@@ -2735,7 +2740,7 @@ static int get_minimal_bit_rate(RedWorker *worker, int width, int height)
 
 static void red_display_create_stream(DisplayChannelClient *dcc, Stream *stream)
 {
-    StreamAgent *agent = &dcc->stream_agents[stream - dcc->common.worker->streams_buf];
+    StreamAgent *agent = &dcc->stream_agents[get_stream_id(dcc->common.worker, stream)];
 
     stream->refs++;
     spice_assert(region_is_empty(&agent->vis_region));
@@ -2927,7 +2932,7 @@ static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream)
         return;
     }
 
-    index = stream - worker->streams_buf;
+    index = get_stream_id(worker, stream);
     DRAWABLE_FOREACH_DPI(stream->current, ring_item, dpi) {
         dcc = dpi->dcc;
         if (!display_channel_client_is_low_bandwidth(dcc)) {
@@ -8038,7 +8043,7 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
         return FALSE;
     }
 
-    StreamAgent *agent = &dcc->stream_agents[stream - worker->streams_buf];
+    StreamAgent *agent = &dcc->stream_agents[get_stream_id(worker, stream)];
     uint64_t time_now = red_now();
     size_t outbuf_size;
     if (time_now - agent->last_send_time < (1000 * 1000 * 1000) / agent->fps) {
@@ -8063,7 +8068,7 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
 
     SpiceMsgDisplayStreamData stream_data;
 
-    stream_data.id = stream - worker->streams_buf;
+    stream_data.id = get_stream_id(worker, stream);
     stream_data.multi_media_time = drawable->red_drawable->mm_time;
     stream_data.data_size = n;
     spice_marshall_msg_display_stream_data(base_marshaller, &stream_data);
@@ -8363,7 +8368,7 @@ static void red_display_marshall_stream_start(RedChannelClient *rcc,
     SpiceClipRects clip_rects;
 
     stream_create.surface_id = 0;
-    stream_create.id = agent - dcc->stream_agents;
+    stream_create.id = get_stream_id(dcc->common.worker, stream);
     stream_create.flags = stream->top_down ? SPICE_STREAM_FLAGS_TOP_DOWN : 0;
     stream_create.codec_type = SPICE_VIDEO_CODEC_TYPE_MJPEG;
 
@@ -8399,7 +8404,7 @@ static void red_display_marshall_stream_clip(RedChannelClient *rcc,
     red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CLIP, &item->base);
     SpiceMsgDisplayStreamClip stream_clip;
 
-    stream_clip.id = agent - dcc->stream_agents;
+    stream_clip.id = get_stream_id(dcc->common.worker, agent->stream);
     stream_clip.clip.type = item->clip_type;
     stream_clip.clip.rects = item->rects;
 
@@ -8413,7 +8418,7 @@ static void red_display_marshall_stream_end(RedChannelClient *rcc,
     SpiceMsgDisplayStreamDestroy destroy;
 
     red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY, NULL);
-    destroy.id = agent - dcc->stream_agents;
+    destroy.id = get_stream_id(dcc->common.worker, agent->stream);
 
     spice_marshall_msg_display_stream_destroy(base_marshaller, &destroy);
 }
commit 892462658b8082632e2bd899f55b02f4f4580aeb
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Mon Apr 30 15:15:33 2012 +0300

    Update the spice-common submodule
    
    We need some rect/region getters methods that were added

diff --git a/spice-common b/spice-common
index f37ba0d..178c7ea 160000
--- a/spice-common
+++ b/spice-common
@@ -1 +1 @@
-Subproject commit f37ba0d032ed91fb3b624883a4309a9870a130dd
+Subproject commit 178c7eaff6fa45b9051bb4d3cf90f45ea9319f83


More information about the Spice-commits mailing list