1 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3    Copyright (C) 2009-2015 Red Hat, Inc.
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <config.h>
19 
20 #include <common/utils.h>
21 #include "dcc-private.h"
22 #include "display-channel.h"
23 #include "display-channel-private.h"
24 #include "red-client.h"
25 #include "main-channel-client.h"
26 #include <spice-server-enums.h>
27 
28 #define DISPLAY_CLIENT_SHORT_TIMEOUT 15000000000ULL //nano
29 #define DISPLAY_FREE_LIST_DEFAULT_SIZE 128
30 
31 static void dcc_init_stream_agents(DisplayChannelClient *dcc);
32 
DisplayChannelClient(DisplayChannel * display,RedClient * client,RedStream * stream,RedChannelCapabilities * caps,uint32_t id,SpiceImageCompression image_compression,spice_wan_compression_t jpeg_state,spice_wan_compression_t zlib_glz_state)33 DisplayChannelClient::DisplayChannelClient(DisplayChannel *display,
34                          RedClient *client, RedStream *stream,
35                          RedChannelCapabilities *caps,
36                          uint32_t id,
37                          SpiceImageCompression image_compression,
38                          spice_wan_compression_t jpeg_state,
39                          spice_wan_compression_t zlib_glz_state):
40     CommonGraphicsChannelClient(display, client, stream, caps, true),
41     priv(new DisplayChannelClientPrivate)
42 {
43 
44     // XXX from display_channel_client_init, put somewhere else
45     ring_init(&priv->palette_cache_lru);
46     // todo: tune quality according to bandwidth
47     priv->encoders.jpeg_quality = 85;
48 
49     priv->send_data.free_list.res = (SpiceResourceList*)
50         g_malloc(sizeof(SpiceResourceList) +
51                  DISPLAY_FREE_LIST_DEFAULT_SIZE * sizeof(SpiceResourceID));
52     priv->send_data.free_list.res_size = DISPLAY_FREE_LIST_DEFAULT_SIZE;
53 
54 
55     priv->image_compression = image_compression;
56     priv->jpeg_state = jpeg_state;
57     priv->zlib_glz_state = zlib_glz_state;
58 
59 
60     priv->id = id;
61 
62     image_encoders_init(&priv->encoders, &DCC_TO_DC(this)->priv->encoder_shared_data);
63 
64     dcc_init_stream_agents(this);
65 }
66 
~DisplayChannelClient()67 DisplayChannelClient::~DisplayChannelClient()
68 {
69     g_clear_pointer(&priv->preferred_video_codecs, g_array_unref);
70     g_clear_pointer(&priv->client_preferred_video_codecs, g_array_unref);
71 }
72 
RedSurfaceCreateItem(uint32_t surface_id,uint32_t width,uint32_t height,uint32_t format,uint32_t flags)73 RedSurfaceCreateItem::RedSurfaceCreateItem(uint32_t surface_id,
74                                            uint32_t width,
75                                            uint32_t height,
76                                            uint32_t format,
77                                            uint32_t flags)
78 {
79     surface_create.surface_id = surface_id;
80     surface_create.width = width;
81     surface_create.height = height;
82     surface_create.flags = flags;
83     surface_create.format = format;
84 }
85 
dcc_drawable_is_in_pipe(DisplayChannelClient * dcc,Drawable * drawable)86 bool dcc_drawable_is_in_pipe(DisplayChannelClient *dcc, Drawable *drawable)
87 {
88     RedDrawablePipeItem *dpi;
89     GList *l;
90 
91     for (l = drawable->pipes; l != nullptr; l = l->next) {
92         dpi = (RedDrawablePipeItem *) l->data;
93         if (dpi->dcc == dcc) {
94             return TRUE;
95         }
96     }
97 
98     return FALSE;
99 }
100 
101 /*
102  * Return: TRUE if wait_if_used == FALSE, or otherwise, if all of the pipe items that
103  * are related to the surface have been cleared (or sent) from the pipe.
104  */
dcc_clear_surface_drawables_from_pipe(DisplayChannelClient * dcc,int surface_id,int wait_if_used)105 bool dcc_clear_surface_drawables_from_pipe(DisplayChannelClient *dcc, int surface_id,
106                                            int wait_if_used)
107 {
108     int x;
109 
110     spice_return_val_if_fail(dcc != nullptr, TRUE);
111     /* removing the newest drawables that their destination is surface_id and
112        no other drawable depends on them */
113 
114     auto &pipe = dcc->get_pipe();
115     for (auto l = pipe.begin(); l != pipe.end(); ) {
116         Drawable *drawable;
117         RedDrawablePipeItem *dpi = nullptr;
118         int depend_found = FALSE;
119         RedPipeItem *item = l->get();
120         auto item_pos = l;
121 
122         ++l;
123         if (item->type == RED_PIPE_ITEM_TYPE_DRAW) {
124             dpi = static_cast<RedDrawablePipeItem*>(item);
125             drawable = dpi->drawable;
126         } else if (item->type == RED_PIPE_ITEM_TYPE_UPGRADE) {
127             drawable = static_cast<RedUpgradeItem*>(item)->drawable;
128         } else {
129             continue;
130         }
131 
132         if (drawable->surface_id == surface_id) {
133             l = pipe.erase(item_pos);
134             continue;
135         }
136 
137         for (x = 0; x < 3; ++x) {
138             if (drawable->surface_deps[x] == surface_id) {
139                 depend_found = TRUE;
140                 break;
141             }
142         }
143 
144         if (depend_found) {
145             spice_debug("surface %d dependent item found %p, %p", surface_id, drawable, item);
146             if (!wait_if_used) {
147                 return TRUE;
148             }
149             return dcc->wait_pipe_item_sent(item_pos, COMMON_CLIENT_TIMEOUT);
150         }
151     }
152 
153     if (!wait_if_used) {
154         return TRUE;
155     }
156 
157     /*
158      * in case that the pipe didn't contain any item that is dependent on the surface, but
159      * there is one during sending. Use a shorter timeout, since it is just one item
160      */
161     return dcc->wait_outgoing_item(DISPLAY_CLIENT_SHORT_TIMEOUT);
162 }
163 
dcc_create_surface(DisplayChannelClient * dcc,int surface_id)164 void dcc_create_surface(DisplayChannelClient *dcc, int surface_id)
165 {
166     DisplayChannel *display;
167     RedSurface *surface;
168     uint32_t flags;
169 
170     if (!dcc) {
171         return;
172     }
173 
174     display = DCC_TO_DC(dcc);
175     flags = is_primary_surface(display, surface_id) ? SPICE_SURFACE_FLAGS_PRIMARY : 0;
176 
177     /* don't send redundant create surface commands to client */
178     if (display->get_during_target_migrate() ||
179         dcc->priv->surface_client_created[surface_id]) {
180         return;
181     }
182     surface = &display->priv->surfaces[surface_id];
183     auto create = red::make_shared<RedSurfaceCreateItem>(surface_id, surface->context.width,
184                                                          surface->context.height,
185                                                          surface->context.format, flags);
186     dcc->priv->surface_client_created[surface_id] = TRUE;
187     dcc->pipe_add(create);
188 }
189 
190 // adding the pipe item after pos. If pos == NULL, adding to head.
191 void
dcc_add_surface_area_image(DisplayChannelClient * dcc,int surface_id,SpiceRect * area,RedChannelClient::Pipe::iterator pipe_item_pos,int can_lossy)192 dcc_add_surface_area_image(DisplayChannelClient *dcc, int surface_id,
193                            SpiceRect *area, RedChannelClient::Pipe::iterator pipe_item_pos,
194                            int can_lossy)
195 {
196     DisplayChannel *display = DCC_TO_DC(dcc);
197     RedSurface *surface = &display->priv->surfaces[surface_id];
198     SpiceCanvas *canvas = surface->context.canvas;
199     int stride;
200     int width;
201     int height;
202     int bpp;
203     int all_set;
204 
205     spice_assert(area);
206 
207     width = area->right - area->left;
208     height = area->bottom - area->top;
209     bpp = SPICE_SURFACE_FMT_DEPTH(surface->context.format) / 8;
210     stride = width * bpp;
211 
212     red::shared_ptr<RedImageItem> item(new (height * stride) RedImageItem());
213 
214     item->surface_id = surface_id;
215     item->image_format =
216         spice_bitmap_from_surface_type(surface->context.format);
217     item->image_flags = 0;
218     item->pos.x = area->left;
219     item->pos.y = area->top;
220     item->width = width;
221     item->height = height;
222     item->stride = stride;
223     item->top_down = surface->context.top_down;
224     item->can_lossy = can_lossy;
225 
226     canvas->ops->read_bits(canvas, item->data, stride, area);
227 
228     /* For 32bit non-primary surfaces we need to keep any non-zero
229        high bytes as the surface may be used as source to an alpha_blend */
230     if (!is_primary_surface(display, surface_id) &&
231         item->image_format == SPICE_BITMAP_FMT_32BIT &&
232         rgb32_data_has_alpha(item->width, item->height, item->stride, item->data, &all_set)) {
233         if (all_set) {
234             item->image_flags |= SPICE_IMAGE_FLAGS_HIGH_BITS_SET;
235         } else {
236             item->image_format = SPICE_BITMAP_FMT_RGBA;
237         }
238     }
239 
240     if (pipe_item_pos != dcc->get_pipe().end()) {
241         dcc->pipe_add_after_pos(item, pipe_item_pos);
242     } else {
243         dcc->pipe_add(item);
244     }
245 }
246 
dcc_push_surface_image(DisplayChannelClient * dcc,int surface_id)247 void dcc_push_surface_image(DisplayChannelClient *dcc, int surface_id)
248 {
249     DisplayChannel *display;
250     SpiceRect area;
251     RedSurface *surface;
252 
253     if (!dcc) {
254         return;
255     }
256 
257     display = DCC_TO_DC(dcc);
258     surface = &display->priv->surfaces[surface_id];
259     if (!surface->context.canvas) {
260         return;
261     }
262     area.top = area.left = 0;
263     area.right = surface->context.width;
264     area.bottom = surface->context.height;
265 
266     /* not allowing lossy compression because probably, especially if it is a primary surface,
267        it combines both "picture-like" areas with areas that are more "artificial"*/
268     dcc_add_surface_area_image(dcc, surface_id, &area, dcc->get_pipe().end(), FALSE);
269 }
270 
add_drawable_surface_images(DisplayChannelClient * dcc,Drawable * drawable)271 static void add_drawable_surface_images(DisplayChannelClient *dcc, Drawable *drawable)
272 {
273     DisplayChannel *display = DCC_TO_DC(dcc);
274     int x;
275 
276     for (x = 0; x < 3; ++x) {
277         int surface_id;
278 
279         surface_id = drawable->surface_deps[x];
280         if (surface_id != -1) {
281             if (dcc->priv->surface_client_created[surface_id]) {
282                 continue;
283             }
284             dcc_create_surface(dcc, surface_id);
285             display_channel_current_flush(display, surface_id);
286             dcc_push_surface_image(dcc, surface_id);
287         }
288     }
289 
290     if (dcc->priv->surface_client_created[drawable->surface_id]) {
291         return;
292     }
293 
294     dcc_create_surface(dcc, drawable->surface_id);
295     display_channel_current_flush(display, drawable->surface_id);
296     dcc_push_surface_image(dcc, drawable->surface_id);
297 }
298 
RedDrawablePipeItem(DisplayChannelClient * init_dcc,Drawable * init_drawable)299 RedDrawablePipeItem::RedDrawablePipeItem(DisplayChannelClient *init_dcc, Drawable *init_drawable):
300     drawable(init_drawable),
301     dcc(init_dcc)
302 {
303     drawable->pipes = g_list_prepend(drawable->pipes, this);
304     drawable->refs++;
305 }
306 
~RedDrawablePipeItem()307 RedDrawablePipeItem::~RedDrawablePipeItem()
308 {
309     drawable->pipes = g_list_remove(drawable->pipes, this);
310     drawable_unref(drawable);
311 }
312 
dcc_prepend_drawable(DisplayChannelClient * dcc,Drawable * drawable)313 void dcc_prepend_drawable(DisplayChannelClient *dcc, Drawable *drawable)
314 {
315     auto dpi = red::make_shared<RedDrawablePipeItem>(dcc, drawable);
316 
317     add_drawable_surface_images(dcc, drawable);
318     dcc->pipe_add(dpi);
319 }
320 
dcc_append_drawable(DisplayChannelClient * dcc,Drawable * drawable)321 void dcc_append_drawable(DisplayChannelClient *dcc, Drawable *drawable)
322 {
323     auto dpi = red::make_shared<RedDrawablePipeItem>(dcc, drawable);
324 
325     add_drawable_surface_images(dcc, drawable);
326     dcc->pipe_add_tail(dpi);
327 }
328 
dcc_add_drawable_after(DisplayChannelClient * dcc,Drawable * drawable,RedPipeItem * pos)329 void dcc_add_drawable_after(DisplayChannelClient *dcc, Drawable *drawable, RedPipeItem *pos)
330 {
331     auto dpi = red::make_shared<RedDrawablePipeItem>(dcc, drawable);
332 
333     add_drawable_surface_images(dcc, drawable);
334     dcc->pipe_add_after(dpi, pos);
335 }
336 
dcc_init_stream_agents(DisplayChannelClient * dcc)337 static void dcc_init_stream_agents(DisplayChannelClient *dcc)
338 {
339     int i;
340     DisplayChannel *display = DCC_TO_DC(dcc);
341 
342     for (i = 0; i < NUM_STREAMS; i++) {
343         VideoStreamAgent *agent = &dcc->priv->stream_agents[i];
344         agent->stream = display_channel_get_nth_video_stream(display, i);
345         region_init(&agent->vis_region);
346         region_init(&agent->clip);
347     }
348 }
349 
dcc_new(DisplayChannel * display,RedClient * client,RedStream * stream,int mig_target,RedChannelCapabilities * caps,SpiceImageCompression image_compression,spice_wan_compression_t jpeg_state,spice_wan_compression_t zlib_glz_state)350 DisplayChannelClient *dcc_new(DisplayChannel *display,
351                               RedClient *client, RedStream *stream,
352                               int mig_target,
353                               RedChannelCapabilities *caps,
354                               SpiceImageCompression image_compression,
355                               spice_wan_compression_t jpeg_state,
356                               spice_wan_compression_t zlib_glz_state)
357 
358 {
359     auto dcc =
360         red::make_shared<DisplayChannelClient>(display, client, stream, caps,
361                                                display->priv->qxl->id, image_compression,
362                                                jpeg_state, zlib_glz_state);
363     if (!dcc->init()) {
364         return nullptr;
365     }
366     spice_debug("New display (client %p) dcc %p stream %p", client, dcc.get(), stream);
367     display->set_during_target_migrate(mig_target);
368 
369     return dcc.get();
370 }
371 
dcc_create_all_streams(DisplayChannelClient * dcc)372 static void dcc_create_all_streams(DisplayChannelClient *dcc)
373 {
374     Ring *ring = &DCC_TO_DC(dcc)->priv->streams;
375     RingItem *item = ring;
376 
377     while ((item = ring_next(ring, item))) {
378         VideoStream *stream = SPICE_CONTAINEROF(item, VideoStream, link);
379         dcc_create_stream(dcc, stream);
380     }
381 }
382 
383 /* TODO: this function is evil^Wsynchronous, fix */
display_channel_client_wait_for_init(DisplayChannelClient * dcc)384 static bool display_channel_client_wait_for_init(DisplayChannelClient *dcc)
385 {
386     dcc->priv->expect_init = TRUE;
387     uint64_t end_time = spice_get_monotonic_time_ns() + COMMON_CLIENT_TIMEOUT;
388     for (;;) {
389         dcc->receive();
390         if (!dcc->is_connected()) {
391             break;
392         }
393         if (dcc->priv->pixmap_cache && dcc->priv->encoders.glz_dict) {
394             dcc->priv->pixmap_cache_generation = dcc->priv->pixmap_cache->generation;
395             /* TODO: move common.id? if it's used for a per client structure.. */
396             spice_debug("creating encoder with id == %d", dcc->priv->id);
397             if (!image_encoders_glz_create(&dcc->priv->encoders, dcc->priv->id)) {
398                 spice_critical("create global lz failed");
399             }
400             return TRUE;
401         }
402         if (spice_get_monotonic_time_ns() > end_time) {
403             spice_warning("timeout");
404             dcc->disconnect();
405             break;
406         }
407         usleep(DISPLAY_CLIENT_RETRY_INTERVAL);
408     }
409     return FALSE;
410 }
411 
dcc_start(DisplayChannelClient * dcc)412 void dcc_start(DisplayChannelClient *dcc)
413 {
414     DisplayChannel *display = DCC_TO_DC(dcc);
415 
416     dcc->push_set_ack();
417 
418     if (dcc->is_waiting_for_migrate_data())
419         return;
420 
421     if (!display_channel_client_wait_for_init(dcc))
422         return;
423 
424     red::shared_ptr<DisplayChannelClient> self(dcc);
425     dcc->ack_zero_messages_window();
426     if (display->priv->surfaces[0].context.canvas) {
427         display_channel_current_flush(display, 0);
428         dcc->pipe_add_type(RED_PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE);
429         dcc_create_surface(dcc, 0);
430         dcc_push_surface_image(dcc, 0);
431         dcc_push_monitors_config(dcc);
432         dcc->pipe_add_empty_msg(SPICE_MSG_DISPLAY_MARK);
433         dcc_create_all_streams(dcc);
434     }
435 
436     if (red_stream_is_plain_unix(dcc->get_stream()) &&
437         dcc->test_remote_cap(SPICE_DISPLAY_CAP_GL_SCANOUT)) {
438         dcc->pipe_add(dcc_gl_scanout_item_new(dcc, nullptr, 0));
439         dcc_push_monitors_config(dcc);
440     }
441 }
442 
dcc_destroy_stream_agents(DisplayChannelClient * dcc)443 static void dcc_destroy_stream_agents(DisplayChannelClient *dcc)
444 {
445     int i;
446 
447     for (i = 0; i < NUM_STREAMS; i++) {
448         VideoStreamAgent *agent = &dcc->priv->stream_agents[i];
449         region_destroy(&agent->vis_region);
450         region_destroy(&agent->clip);
451         if (agent->video_encoder) {
452             agent->video_encoder->destroy(agent->video_encoder);
453             agent->video_encoder = nullptr;
454         }
455     }
456 }
457 
dcc_stop(DisplayChannelClient * dcc)458 static void dcc_stop(DisplayChannelClient *dcc)
459 {
460     DisplayChannel *dc = DCC_TO_DC(dcc);
461 
462     pixmap_cache_unref(dcc->priv->pixmap_cache);
463     dcc->priv->pixmap_cache = nullptr;
464     dcc_palette_cache_reset(dcc);
465     g_free(dcc->priv->send_data.free_list.res);
466     dcc_destroy_stream_agents(dcc);
467     image_encoders_free(&dcc->priv->encoders);
468 
469     if (dcc->priv->gl_draw_ongoing) {
470         display_channel_gl_draw_done(dc);
471     }
472 }
473 
dcc_video_stream_agent_clip(DisplayChannelClient * dcc,VideoStreamAgent * agent)474 void dcc_video_stream_agent_clip(DisplayChannelClient* dcc, VideoStreamAgent *agent)
475 {
476     auto item = red::make_shared<VideoStreamClipItem>(agent);
477 
478     dcc->pipe_add(item);
479 }
480 
~RedMonitorsConfigItem()481 RedMonitorsConfigItem::~RedMonitorsConfigItem()
482 {
483     monitors_config_unref(monitors_config);
484 }
485 
RedMonitorsConfigItem(MonitorsConfig * init_monitors_config)486 RedMonitorsConfigItem::RedMonitorsConfigItem(MonitorsConfig *init_monitors_config)
487 {
488     monitors_config = monitors_config_ref(init_monitors_config);
489 }
490 
dcc_push_monitors_config(DisplayChannelClient * dcc)491 void dcc_push_monitors_config(DisplayChannelClient *dcc)
492 {
493     DisplayChannel *dc = DCC_TO_DC(dcc);
494     MonitorsConfig *monitors_config = dc->priv->monitors_config;
495 
496     if (monitors_config == nullptr) {
497         spice_warning("monitors_config is NULL");
498         return;
499     }
500 
501     if (!dcc->test_remote_cap(SPICE_DISPLAY_CAP_MONITORS_CONFIG)) {
502         return;
503     }
504 
505     auto mci = red::make_shared<RedMonitorsConfigItem>(monitors_config);
506     dcc->pipe_add(mci);
507 }
508 
RedSurfaceDestroyItem(uint32_t surface_id)509 RedSurfaceDestroyItem::RedSurfaceDestroyItem(uint32_t surface_id)
510 {
511     surface_destroy.surface_id = surface_id;
512 }
513 
dcc_gl_scanout_item_new(RedChannelClient * rcc,void * data,int num)514 RedPipeItemPtr dcc_gl_scanout_item_new(RedChannelClient *rcc, void *data, int num)
515 {
516     /* FIXME: on !unix peer, start streaming with a video codec */
517     if (!red_stream_is_plain_unix(rcc->get_stream()) ||
518         !rcc->test_remote_cap(SPICE_DISPLAY_CAP_GL_SCANOUT)) {
519         red_channel_warning(rcc->get_channel(),
520                             "FIXME: client does not support GL scanout");
521         rcc->disconnect();
522         return RedPipeItemPtr();
523     }
524 
525     return red::make_shared<RedGlScanoutUnixItem>();
526 }
527 
528 XXX_CAST(RedChannelClient, DisplayChannelClient, DISPLAY_CHANNEL_CLIENT);
529 
dcc_gl_draw_item_new(RedChannelClient * rcc,void * data,int num)530 RedPipeItemPtr dcc_gl_draw_item_new(RedChannelClient *rcc, void *data, int num)
531 {
532     DisplayChannelClient *dcc = DISPLAY_CHANNEL_CLIENT(rcc);
533     auto draw = (const SpiceMsgDisplayGlDraw *) data;
534 
535     if (!red_stream_is_plain_unix(rcc->get_stream()) ||
536         !rcc->test_remote_cap(SPICE_DISPLAY_CAP_GL_SCANOUT)) {
537         red_channel_warning(rcc->get_channel(),
538                             "FIXME: client does not support GL scanout");
539         rcc->disconnect();
540         return RedPipeItemPtr();
541     }
542 
543     dcc->priv->gl_draw_ongoing = TRUE;
544     auto item = red::make_shared<RedGlDrawItem>();
545     item->draw = *draw;
546 
547     return item;
548 }
549 
dcc_destroy_surface(DisplayChannelClient * dcc,uint32_t surface_id)550 void dcc_destroy_surface(DisplayChannelClient *dcc, uint32_t surface_id)
551 {
552     DisplayChannel *display;
553 
554     if (!dcc) {
555         return;
556     }
557 
558     display = DCC_TO_DC(dcc);
559 
560     if (display->get_during_target_migrate() ||
561         !dcc->priv->surface_client_created[surface_id]) {
562         return;
563     }
564 
565     dcc->priv->surface_client_created[surface_id] = FALSE;
566     auto destroy = red::make_shared<RedSurfaceDestroyItem>(surface_id);
567     dcc->pipe_add(destroy);
568 }
569 
570 #define MIN_DIMENSION_TO_QUIC 3
571 /**
572  * quic doesn't handle:
573  *       (1) palette
574  */
can_quic_compress(SpiceBitmap * bitmap)575 static bool can_quic_compress(SpiceBitmap *bitmap)
576 {
577     return !bitmap_fmt_is_plt(bitmap->format) &&
578             bitmap->x >= MIN_DIMENSION_TO_QUIC && bitmap->y >= MIN_DIMENSION_TO_QUIC;
579 }
580 /**
581  * lz/glz doesn't handle:
582  *       (1) bitmaps with strides that are larger than the width of the image in bytes
583  *       (2) unstable bitmaps
584  */
can_lz_compress(SpiceBitmap * bitmap)585 static bool can_lz_compress(SpiceBitmap *bitmap)
586 {
587     return !bitmap_has_extra_stride(bitmap) &&
588            !(bitmap->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE);
589 }
590 
591 #define MIN_SIZE_TO_COMPRESS 54
get_compression_for_bitmap(SpiceBitmap * bitmap,SpiceImageCompression preferred_compression,Drawable * drawable)592 static SpiceImageCompression get_compression_for_bitmap(SpiceBitmap *bitmap,
593                                                         SpiceImageCompression preferred_compression,
594                                                         Drawable *drawable)
595 {
596     if (bitmap->y * bitmap->stride < MIN_SIZE_TO_COMPRESS) { // TODO: change the size cond
597         return SPICE_IMAGE_COMPRESSION_OFF;
598     }
599     if (preferred_compression == SPICE_IMAGE_COMPRESSION_OFF) {
600         return SPICE_IMAGE_COMPRESSION_OFF;
601     }
602     if (preferred_compression == SPICE_IMAGE_COMPRESSION_QUIC) {
603         if (can_quic_compress(bitmap)) {
604             return SPICE_IMAGE_COMPRESSION_QUIC;
605         }
606         return SPICE_IMAGE_COMPRESSION_OFF;
607     }
608 
609     if (preferred_compression == SPICE_IMAGE_COMPRESSION_AUTO_GLZ ||
610         preferred_compression == SPICE_IMAGE_COMPRESSION_AUTO_LZ) {
611         if (can_quic_compress(bitmap)) {
612             if (drawable == nullptr ||
613                 drawable->copy_bitmap_graduality == BITMAP_GRADUAL_INVALID) {
614                 if (bitmap_fmt_has_graduality(bitmap->format) &&
615                     bitmap_get_graduality_level(bitmap) == BITMAP_GRADUAL_HIGH) {
616                     return SPICE_IMAGE_COMPRESSION_QUIC;
617                 }
618             } else if (drawable->copy_bitmap_graduality == BITMAP_GRADUAL_HIGH) {
619                 return SPICE_IMAGE_COMPRESSION_QUIC;
620             }
621             if (!can_lz_compress(bitmap)) {
622                 return SPICE_IMAGE_COMPRESSION_QUIC;
623             }
624         }
625         if (preferred_compression == SPICE_IMAGE_COMPRESSION_AUTO_LZ) {
626             preferred_compression = SPICE_IMAGE_COMPRESSION_LZ;
627         } else {
628             preferred_compression = SPICE_IMAGE_COMPRESSION_GLZ;
629         }
630     }
631 
632     if (preferred_compression == SPICE_IMAGE_COMPRESSION_GLZ) {
633         if (drawable == nullptr || !bitmap_fmt_has_graduality(bitmap->format)) {
634             preferred_compression = SPICE_IMAGE_COMPRESSION_LZ;
635         }
636     }
637 
638     if (preferred_compression == SPICE_IMAGE_COMPRESSION_LZ4) {
639         if (!bitmap_fmt_is_rgb(bitmap->format)) {
640             preferred_compression = SPICE_IMAGE_COMPRESSION_LZ;
641         }
642     }
643 
644     if (preferred_compression == SPICE_IMAGE_COMPRESSION_LZ ||
645         preferred_compression == SPICE_IMAGE_COMPRESSION_LZ4 ||
646         preferred_compression == SPICE_IMAGE_COMPRESSION_GLZ) {
647         if (can_lz_compress(bitmap)) {
648             return preferred_compression;
649         }
650         return SPICE_IMAGE_COMPRESSION_OFF;
651     }
652 
653     return SPICE_IMAGE_COMPRESSION_INVALID;
654 }
655 
dcc_compress_image(DisplayChannelClient * dcc,SpiceImage * dest,SpiceBitmap * src,Drawable * drawable,int can_lossy,compress_send_data_t * o_comp_data)656 int dcc_compress_image(DisplayChannelClient *dcc,
657                        SpiceImage *dest, SpiceBitmap *src, Drawable *drawable,
658                        int can_lossy,
659                        compress_send_data_t* o_comp_data)
660 {
661     DisplayChannel *display_channel = DCC_TO_DC(dcc);
662     SpiceImageCompression image_compression;
663     stat_start_time_t start_time;
664     int success = FALSE;
665 
666     stat_start_time_init(&start_time, &display_channel->priv->encoder_shared_data.off_stat);
667 
668     image_compression = get_compression_for_bitmap(src, dcc->priv->image_compression, drawable);
669     switch (image_compression) {
670     case SPICE_IMAGE_COMPRESSION_OFF:
671         break;
672     case SPICE_IMAGE_COMPRESSION_QUIC:
673         if (can_lossy && display_channel->priv->enable_jpeg &&
674             (src->format != SPICE_BITMAP_FMT_RGBA || !bitmap_has_extra_stride(src))) {
675             success = image_encoders_compress_jpeg(&dcc->priv->encoders, dest, src, o_comp_data);
676             break;
677         }
678         success = image_encoders_compress_quic(&dcc->priv->encoders, dest, src, o_comp_data);
679         break;
680     case SPICE_IMAGE_COMPRESSION_GLZ:
681         success = image_encoders_compress_glz(&dcc->priv->encoders, dest, src,
682                                               drawable->red_drawable, &drawable->glz_retention,
683                                               o_comp_data,
684                                               display_channel->priv->enable_zlib_glz_wrap);
685         if (success) {
686             break;
687         }
688         goto lz_compress;
689 #ifdef USE_LZ4
690     case SPICE_IMAGE_COMPRESSION_LZ4:
691         if (dcc->test_remote_cap(SPICE_DISPLAY_CAP_LZ4_COMPRESSION)) {
692             success = image_encoders_compress_lz4(&dcc->priv->encoders, dest, src, o_comp_data);
693             break;
694         }
695 #endif
696         /* fall through */
697     case SPICE_IMAGE_COMPRESSION_LZ:
698 lz_compress:
699         success = image_encoders_compress_lz(&dcc->priv->encoders, dest, src, o_comp_data);
700         if (success && !bitmap_fmt_is_rgb(src->format)) {
701             dcc_palette_cache_palette(dcc, dest->u.lz_plt.palette, &(dest->u.lz_plt.flags));
702         }
703         break;
704     default:
705         spice_error("invalid image compression type %u", image_compression);
706     }
707 
708     if (!success) {
709         uint64_t image_size = src->stride * (uint64_t)src->y;
710         stat_compress_add(&display_channel->priv->encoder_shared_data.off_stat, start_time, image_size, image_size);
711     }
712 
713     return success;
714 }
715 
716 #define CLIENT_PALETTE_CACHE
717 #include "cache-item.tmpl.cpp"
718 #undef CLIENT_PALETTE_CACHE
719 
dcc_palette_cache_palette(DisplayChannelClient * dcc,SpicePalette * palette,uint8_t * flags)720 void dcc_palette_cache_palette(DisplayChannelClient *dcc, SpicePalette *palette,
721                                uint8_t *flags)
722 {
723     if (palette == nullptr) {
724         return;
725     }
726     if (palette->unique) {
727         if (red_palette_cache_find(dcc, palette->unique)) {
728             *flags |= SPICE_BITMAP_FLAGS_PAL_FROM_CACHE;
729             return;
730         }
731         if (red_palette_cache_add(dcc, palette->unique, 1)) {
732             *flags |= SPICE_BITMAP_FLAGS_PAL_CACHE_ME;
733         }
734     }
735 }
736 
dcc_palette_cache_reset(DisplayChannelClient * dcc)737 void dcc_palette_cache_reset(DisplayChannelClient *dcc)
738 {
739     red_palette_cache_reset(dcc, CLIENT_PALETTE_CACHE_SIZE);
740 }
741 
dcc_push_release(DisplayChannelClient * dcc,uint8_t type,uint64_t id,uint64_t * sync_data)742 static void dcc_push_release(DisplayChannelClient *dcc, uint8_t type, uint64_t id,
743                              uint64_t* sync_data)
744 {
745     FreeList *free_list = &dcc->priv->send_data.free_list;
746     int i;
747 
748     for (i = 0; i < MAX_CACHE_CLIENTS; i++) {
749         free_list->sync[i] = MAX(free_list->sync[i], sync_data[i]);
750     }
751 
752     if (free_list->res->count == free_list->res_size) {
753         free_list->res = (SpiceResourceList*) g_realloc(free_list->res,
754                                    sizeof(*free_list->res) +
755                                    free_list->res_size * sizeof(SpiceResourceID) * 2);
756         free_list->res_size *= 2;
757     }
758     free_list->res->resources[free_list->res->count].type = type;
759     free_list->res->resources[free_list->res->count++].id = id;
760 }
761 
dcc_pixmap_cache_unlocked_add(DisplayChannelClient * dcc,uint64_t id,uint32_t size,int lossy)762 bool dcc_pixmap_cache_unlocked_add(DisplayChannelClient *dcc, uint64_t id,
763                                    uint32_t size, int lossy)
764 {
765     PixmapCache *cache = dcc->priv->pixmap_cache;
766     NewCacheItem *item;
767     uint64_t serial;
768     int key;
769 
770     spice_assert(size > 0);
771 
772     item = g_new(NewCacheItem, 1);
773     serial = dcc->get_message_serial();
774 
775     if (cache->generation != dcc->priv->pixmap_cache_generation) {
776         if (!dcc->priv->pending_pixmaps_sync) {
777             dcc->pipe_add_type(RED_PIPE_ITEM_TYPE_PIXMAP_SYNC);
778             dcc->priv->pending_pixmaps_sync = TRUE;
779         }
780         g_free(item);
781         return FALSE;
782     }
783 
784     cache->available -= size;
785     while (cache->available < 0) {
786         NewCacheItem *tail;
787         NewCacheItem **now;
788 
789         SPICE_VERIFY(SPICE_OFFSETOF(NewCacheItem, lru_link) == 0);
790         if (!(tail = SPICE_CONTAINEROF(ring_get_tail(&cache->lru), NewCacheItem, lru_link)) ||
791                                                      tail->sync[dcc->priv->id] == serial) {
792             cache->available += size;
793             g_free(item);
794             return FALSE;
795         }
796 
797         now = &cache->hash_table[BITS_CACHE_HASH_KEY(tail->id)];
798         for (;;) {
799             spice_assert(*now);
800             if (*now == tail) {
801                 *now = tail->next;
802                 break;
803             }
804             now = &(*now)->next;
805         }
806         ring_remove(&tail->lru_link);
807         cache->items--;
808         cache->available += tail->size;
809         cache->sync[dcc->priv->id] = serial;
810         dcc_push_release(dcc, SPICE_RES_TYPE_PIXMAP, tail->id, tail->sync);
811         g_free(tail);
812     }
813     ++cache->items;
814     item->next = cache->hash_table[(key = BITS_CACHE_HASH_KEY(id))];
815     cache->hash_table[key] = item;
816     ring_item_init(&item->lru_link);
817     ring_add(&cache->lru, &item->lru_link);
818     item->id = id;
819     item->size = size;
820     item->lossy = lossy;
821     memset(item->sync, 0, sizeof(item->sync));
822     item->sync[dcc->priv->id] = serial;
823     cache->sync[dcc->priv->id] = serial;
824     return TRUE;
825 }
826 
dcc_handle_init(DisplayChannelClient * dcc,SpiceMsgcDisplayInit * init)827 static bool dcc_handle_init(DisplayChannelClient *dcc, SpiceMsgcDisplayInit *init)
828 {
829     gboolean success;
830     RedClient *client = dcc->get_client();
831 
832     spice_return_val_if_fail(dcc->priv->expect_init, FALSE);
833     dcc->priv->expect_init = FALSE;
834 
835     spice_return_val_if_fail(!dcc->priv->pixmap_cache, FALSE);
836     dcc->priv->pixmap_cache = pixmap_cache_get(client,
837                                                init->pixmap_cache_id,
838                                                init->pixmap_cache_size);
839     spice_return_val_if_fail(dcc->priv->pixmap_cache, FALSE);
840 
841     success = image_encoders_get_glz_dictionary(&dcc->priv->encoders,
842                                                 client,
843                                                 init->glz_dictionary_id,
844                                                 init->glz_dictionary_window_size);
845     spice_return_val_if_fail(success, FALSE);
846 
847     return TRUE;
848 }
849 
dcc_handle_stream_report(DisplayChannelClient * dcc,SpiceMsgcDisplayStreamReport * report)850 static bool dcc_handle_stream_report(DisplayChannelClient *dcc,
851                                      SpiceMsgcDisplayStreamReport *report)
852 {
853     VideoStreamAgent *agent;
854 
855     if (report->stream_id >= NUM_STREAMS) {
856         spice_warning("stream_report: invalid stream id %u",
857                       report->stream_id);
858         return FALSE;
859     }
860 
861     agent = &dcc->priv->stream_agents[report->stream_id];
862     if (!agent->video_encoder) {
863         spice_debug("stream_report: no encoder for stream id %u. "
864                    "The stream has probably been destroyed",
865                    report->stream_id);
866         return TRUE;
867     }
868 
869     if (report->num_frames == 0 && report->num_drops == UINT32_MAX) {
870         spice_warning("stream_report: the client does not support stream %u",
871                       report->stream_id);
872         /* Stop streaming the video so the client can see it */
873         agent->video_encoder->destroy(agent->video_encoder);
874         agent->video_encoder = nullptr;
875         return TRUE;
876     }
877 
878     if (report->unique_id != agent->report_id) {
879         spice_warning("stream_report: unique id mismatch: local (%u) != msg (%u) "
880                       "The old stream was probably replaced by a new one",
881                       agent->report_id, report->unique_id);
882         return TRUE;
883     }
884 
885     agent->video_encoder->client_stream_report(agent->video_encoder,
886                                                report->num_frames,
887                                                report->num_drops,
888                                                report->start_frame_mm_time,
889                                                report->end_frame_mm_time,
890                                                report->last_frame_delay,
891                                                report->audio_delay);
892     return TRUE;
893 }
894 
dcc_handle_preferred_compression(DisplayChannelClient * dcc,SpiceMsgcDisplayPreferredCompression * pc)895 static bool dcc_handle_preferred_compression(DisplayChannelClient *dcc,
896                                              SpiceMsgcDisplayPreferredCompression *pc)
897 {
898     switch (pc->image_compression) {
899     case SPICE_IMAGE_COMPRESSION_AUTO_LZ:
900     case SPICE_IMAGE_COMPRESSION_AUTO_GLZ:
901     case SPICE_IMAGE_COMPRESSION_QUIC:
902 #ifdef USE_LZ4
903     case SPICE_IMAGE_COMPRESSION_LZ4:
904 #endif
905     case SPICE_IMAGE_COMPRESSION_LZ:
906     case SPICE_IMAGE_COMPRESSION_GLZ:
907     case SPICE_IMAGE_COMPRESSION_OFF:
908         dcc->priv->image_compression = (SpiceImageCompression) pc->image_compression;
909         break;
910     default:
911         spice_warning("preferred-compression: unsupported image compression setting");
912     }
913     g_debug("Setting preferred compression to %s",
914             spice_image_compression_t_get_nick(dcc->priv->image_compression));
915     return TRUE;
916 }
917 
918 /* TODO: Client preference should only be considered when host has video-codecs
919  * with the same priority value. At the moment, the video-codec GArray will be
920  * sorted following only the client's preference (@user_data)
921  *
922  * example:
923  * host encoding preference: gstreamer:mjpeg;gstreamer:vp8;gstreamer:h264
924  * client decoding preference: h264, vp9, mjpeg
925  * result: gstreamer:h264;gstreamer:mjpeg;gstreamer:vp8
926  */
sort_video_codecs_by_client_preference(gconstpointer a_pointer,gconstpointer b_pointer,gpointer user_data)927 static gint sort_video_codecs_by_client_preference(gconstpointer a_pointer,
928                                                    gconstpointer b_pointer,
929                                                    gpointer user_data)
930 {
931     auto a = (const RedVideoCodec *) a_pointer;
932     auto b = (const RedVideoCodec *) b_pointer;
933     auto client_pref = (GArray *) user_data;
934 
935     return (g_array_index(client_pref, gint, a->type) -
936             g_array_index(client_pref, gint, b->type));
937 }
938 
dcc_update_preferred_video_codecs(DisplayChannelClient * dcc)939 static void dcc_update_preferred_video_codecs(DisplayChannelClient *dcc)
940 {
941     GArray *video_codecs, *server_codecs;
942     char *codecs_str;
943 
944     server_codecs = display_channel_get_video_codecs(DCC_TO_DC(dcc));
945     spice_return_if_fail(server_codecs != nullptr);
946 
947     /* Copy current host preference */
948     video_codecs = g_array_sized_new(FALSE, FALSE, sizeof(RedVideoCodec), server_codecs->len);
949     g_array_append_vals(video_codecs, server_codecs->data, server_codecs->len);
950 
951     /* Sort the copy of current host preference based on client's preference */
952     g_array_sort_with_data(video_codecs, sort_video_codecs_by_client_preference,
953                            dcc->priv->client_preferred_video_codecs);
954     g_clear_pointer(&dcc->priv->preferred_video_codecs, g_array_unref);
955     dcc->priv->preferred_video_codecs = video_codecs;
956 
957     codecs_str = video_codecs_to_string(video_codecs, " ");
958     spice_debug("Preferred video-codecs: %s", codecs_str);
959     g_free(codecs_str);
960 }
961 
dcc_video_codecs_update(DisplayChannelClient * dcc)962 void dcc_video_codecs_update(DisplayChannelClient *dcc)
963 {
964     /* Only worry about video-codecs update if client has sent
965      * SPICE_MSGC_DISPLAY_PREFERRED_VIDEO_CODEC_TYPE */
966     if (dcc->priv->client_preferred_video_codecs == nullptr) {
967         return;
968     }
969 
970     /* New host preference */
971     dcc_update_preferred_video_codecs(dcc);
972 }
973 
dcc_handle_preferred_video_codec_type(DisplayChannelClient * dcc,SpiceMsgcDisplayPreferredVideoCodecType * msg)974 static int dcc_handle_preferred_video_codec_type(DisplayChannelClient *dcc,
975                                                  SpiceMsgcDisplayPreferredVideoCodecType *msg)
976 {
977     g_return_val_if_fail(msg->num_of_codecs > 0, TRUE);
978 
979     g_clear_pointer(&dcc->priv->client_preferred_video_codecs, g_array_unref);
980     dcc->priv->client_preferred_video_codecs = video_stream_parse_preferred_codecs(msg);
981 
982     /* New client preference */
983     dcc_update_preferred_video_codecs(dcc);
984     video_stream_detach_and_stop(DCC_TO_DC(dcc));
985 
986     return TRUE;
987 }
988 
dcc_get_preferred_video_codecs_for_encoding(DisplayChannelClient * dcc)989 GArray *dcc_get_preferred_video_codecs_for_encoding(DisplayChannelClient *dcc)
990 {
991     if (dcc->priv->preferred_video_codecs != nullptr) {
992         return dcc->priv->preferred_video_codecs;
993     }
994     return display_channel_get_video_codecs(DCC_TO_DC(dcc));
995 }
996 
dcc_handle_gl_draw_done(DisplayChannelClient * dcc)997 static bool dcc_handle_gl_draw_done(DisplayChannelClient *dcc)
998 {
999     DisplayChannel *display = DCC_TO_DC(dcc);
1000 
1001     if (G_UNLIKELY(!dcc->priv->gl_draw_ongoing)) {
1002         g_warning("unexpected DRAW_DONE received\n");
1003         /* close client connection */
1004         return FALSE;
1005     }
1006 
1007     dcc->priv->gl_draw_ongoing = FALSE;
1008     display_channel_gl_draw_done(display);
1009 
1010     return TRUE;
1011 }
1012 
handle_message(uint16_t type,uint32_t size,void * msg)1013 bool DisplayChannelClient::handle_message(uint16_t type, uint32_t size, void *msg)
1014 {
1015     switch (type) {
1016     case SPICE_MSGC_DISPLAY_INIT:
1017         return dcc_handle_init(this, (SpiceMsgcDisplayInit *)msg);
1018     case SPICE_MSGC_DISPLAY_STREAM_REPORT:
1019         return dcc_handle_stream_report(this, (SpiceMsgcDisplayStreamReport *)msg);
1020     case SPICE_MSGC_DISPLAY_PREFERRED_COMPRESSION:
1021         return dcc_handle_preferred_compression(this,
1022             (SpiceMsgcDisplayPreferredCompression *)msg);
1023     case SPICE_MSGC_DISPLAY_GL_DRAW_DONE:
1024         return dcc_handle_gl_draw_done(this);
1025     case SPICE_MSGC_DISPLAY_PREFERRED_VIDEO_CODEC_TYPE:
1026         return dcc_handle_preferred_video_codec_type(this,
1027             (SpiceMsgcDisplayPreferredVideoCodecType *)msg);
1028     default:
1029         return RedChannelClient::handle_message(type, size, msg);
1030     }
1031 }
1032 
dcc_handle_migrate_glz_dictionary(DisplayChannelClient * dcc,SpiceMigrateDataDisplay * migrate)1033 static int dcc_handle_migrate_glz_dictionary(DisplayChannelClient *dcc,
1034                                              SpiceMigrateDataDisplay *migrate)
1035 {
1036     GlzEncDictRestoreData glz_dict_data = migrate->glz_dict_data;
1037     return image_encoders_restore_glz_dictionary(&dcc->priv->encoders,
1038                                                  dcc->get_client(),
1039                                                  migrate->glz_dict_id,
1040                                                  &glz_dict_data);
1041 }
1042 
restore_surface(DisplayChannelClient * dcc,uint32_t surface_id)1043 static bool restore_surface(DisplayChannelClient *dcc, uint32_t surface_id)
1044 {
1045     /* we don't process commands till we receive the migration data, thus,
1046      * we should have not sent any surface to the client. */
1047     if (dcc->priv->surface_client_created[surface_id]) {
1048         spice_warning("surface %u is already marked as client_created", surface_id);
1049         return FALSE;
1050     }
1051     dcc->priv->surface_client_created[surface_id] = TRUE;
1052     return TRUE;
1053 }
1054 
restore_surfaces_lossless(DisplayChannelClient * dcc,MigrateDisplaySurfacesAtClientLossless * mig_surfaces)1055 static bool restore_surfaces_lossless(DisplayChannelClient *dcc,
1056                                       MigrateDisplaySurfacesAtClientLossless *mig_surfaces)
1057 {
1058     uint32_t i;
1059 
1060     spice_debug("trace");
1061     for (i = 0; i < mig_surfaces->num_surfaces; i++) {
1062         uint32_t surface_id = mig_surfaces->surfaces[i].id;
1063 
1064         if (!restore_surface(dcc, surface_id))
1065             return FALSE;
1066     }
1067     return TRUE;
1068 }
1069 
restore_surfaces_lossy(DisplayChannelClient * dcc,MigrateDisplaySurfacesAtClientLossy * mig_surfaces)1070 static bool restore_surfaces_lossy(DisplayChannelClient *dcc,
1071                                    MigrateDisplaySurfacesAtClientLossy *mig_surfaces)
1072 {
1073     uint32_t i;
1074 
1075     spice_debug("trace");
1076     for (i = 0; i < mig_surfaces->num_surfaces; i++) {
1077         uint32_t surface_id = mig_surfaces->surfaces[i].id;
1078         SpiceMigrateDataRect *mig_lossy_rect;
1079         SpiceRect lossy_rect;
1080 
1081         if (!restore_surface(dcc, surface_id))
1082             return FALSE;
1083 
1084         mig_lossy_rect = &mig_surfaces->surfaces[i].lossy_rect;
1085         lossy_rect.left = mig_lossy_rect->left;
1086         lossy_rect.top = mig_lossy_rect->top;
1087         lossy_rect.right = mig_lossy_rect->right;
1088         lossy_rect.bottom = mig_lossy_rect->bottom;
1089         region_init(&dcc->priv->surface_client_lossy_region[surface_id]);
1090         region_add(&dcc->priv->surface_client_lossy_region[surface_id], &lossy_rect);
1091     }
1092     return TRUE;
1093 }
1094 
handle_migrate_data(uint32_t size,void * message)1095 bool DisplayChannelClient::handle_migrate_data(uint32_t size, void *message)
1096 {
1097     DisplayChannelClient *dcc = this;
1098     DisplayChannel *display = DCC_TO_DC(dcc);
1099     int surfaces_restored = FALSE;
1100     auto header = (SpiceMigrateDataHeader *)message;
1101     auto migrate_data = (SpiceMigrateDataDisplay *)(header + 1);
1102     uint8_t *surfaces;
1103     int i;
1104 
1105     spice_return_val_if_fail(
1106         size >= (sizeof(*migrate_data) + sizeof(SpiceMigrateDataHeader)), FALSE);
1107     spice_return_val_if_fail(
1108          migration_protocol_validate_header(header,
1109              SPICE_MIGRATE_DATA_DISPLAY_MAGIC, SPICE_MIGRATE_DATA_DISPLAY_VERSION), FALSE);
1110 
1111     /* size is set to -1 in order to keep the cache frozen until the original
1112      * channel client that froze the cache on the src size receives the migrate
1113      * data and unfreezes the cache by setting its size > 0 and by triggering
1114      * pixmap_cache_reset */
1115     dcc->priv->pixmap_cache = pixmap_cache_get(dcc->get_client(),
1116                                                migrate_data->pixmap_cache_id, -1);
1117     spice_return_val_if_fail(dcc->priv->pixmap_cache, FALSE);
1118 
1119     pthread_mutex_lock(&dcc->priv->pixmap_cache->lock);
1120     for (i = 0; i < MAX_CACHE_CLIENTS; i++) {
1121         dcc->priv->pixmap_cache->sync[i] = MAX(dcc->priv->pixmap_cache->sync[i],
1122                                                migrate_data->pixmap_cache_clients[i]);
1123     }
1124     pthread_mutex_unlock(&dcc->priv->pixmap_cache->lock);
1125 
1126     if (migrate_data->pixmap_cache_freezer) {
1127         /* activating the cache. The cache will start to be active after
1128          * pixmap_cache_reset is called, when handling RED_PIPE_ITEM_TYPE_PIXMAP_RESET */
1129         dcc->priv->pixmap_cache->size = migrate_data->pixmap_cache_size;
1130         dcc->pipe_add_type(RED_PIPE_ITEM_TYPE_PIXMAP_RESET);
1131     }
1132 
1133     if (dcc_handle_migrate_glz_dictionary(dcc, migrate_data)) {
1134         image_encoders_glz_create(&dcc->priv->encoders, dcc->priv->id);
1135     } else {
1136         spice_critical("restoring global lz dictionary failed");
1137     }
1138 
1139     dcc->is_low_bandwidth = migrate_data->low_bandwidth_setting;
1140 
1141     if (migrate_data->low_bandwidth_setting) {
1142         dcc->ack_set_client_window(WIDE_CLIENT_ACK_WINDOW);
1143         if (dcc->priv->jpeg_state == SPICE_WAN_COMPRESSION_AUTO) {
1144             display->priv->enable_jpeg = TRUE;
1145         }
1146         if (dcc->priv->zlib_glz_state == SPICE_WAN_COMPRESSION_AUTO) {
1147             display->priv->enable_zlib_glz_wrap = TRUE;
1148         }
1149     }
1150 
1151     surfaces = (uint8_t *)message + migrate_data->surfaces_at_client_ptr;
1152     surfaces_restored = display->priv->enable_jpeg ?
1153         restore_surfaces_lossy(dcc, (MigrateDisplaySurfacesAtClientLossy *)surfaces) :
1154         restore_surfaces_lossless(dcc, (MigrateDisplaySurfacesAtClientLossless*)surfaces);
1155 
1156     spice_return_val_if_fail(surfaces_restored, FALSE);
1157 
1158     dcc->pipe_add_type(RED_PIPE_ITEM_TYPE_INVAL_PALETTE_CACHE);
1159     /* enable sending messages */
1160     dcc->ack_zero_messages_window();
1161     return TRUE;
1162 }
1163 
dcc_get_video_stream_agent(DisplayChannelClient * dcc,int stream_id)1164 VideoStreamAgent* dcc_get_video_stream_agent(DisplayChannelClient *dcc, int stream_id)
1165 {
1166     return &dcc->priv->stream_agents[stream_id];
1167 }
1168 
dcc_get_encoders(DisplayChannelClient * dcc)1169 ImageEncoders* dcc_get_encoders(DisplayChannelClient *dcc)
1170 {
1171     return &dcc->priv->encoders;
1172 }
1173 
dcc_get_jpeg_state(DisplayChannelClient * dcc)1174 spice_wan_compression_t dcc_get_jpeg_state(DisplayChannelClient *dcc)
1175 {
1176     return dcc->priv->jpeg_state;
1177 }
1178 
dcc_get_zlib_glz_state(DisplayChannelClient * dcc)1179 spice_wan_compression_t dcc_get_zlib_glz_state(DisplayChannelClient *dcc)
1180 {
1181     return dcc->priv->zlib_glz_state;
1182 }
1183 
dcc_get_max_stream_latency(DisplayChannelClient * dcc)1184 uint32_t dcc_get_max_stream_latency(DisplayChannelClient *dcc)
1185 {
1186     return dcc->priv->streams_max_latency;
1187 }
1188 
dcc_set_max_stream_latency(DisplayChannelClient * dcc,uint32_t latency)1189 void dcc_set_max_stream_latency(DisplayChannelClient *dcc, uint32_t latency)
1190 {
1191     dcc->priv->streams_max_latency = latency;
1192 }
1193 
dcc_get_max_stream_bit_rate(DisplayChannelClient * dcc)1194 uint64_t dcc_get_max_stream_bit_rate(DisplayChannelClient *dcc)
1195 {
1196     return dcc->priv->streams_max_bit_rate;
1197 }
1198 
dcc_set_max_stream_bit_rate(DisplayChannelClient * dcc,uint64_t rate)1199 void dcc_set_max_stream_bit_rate(DisplayChannelClient *dcc, uint64_t rate)
1200 {
1201     dcc->priv->streams_max_bit_rate = rate;
1202 }
1203 
config_socket()1204 bool DisplayChannelClient::config_socket()
1205 {
1206     RedClient *client = get_client();
1207     MainChannelClient *mcc = client->get_main();
1208 
1209     is_low_bandwidth = mcc->is_low_bandwidth();
1210 
1211     return CommonGraphicsChannelClient::config_socket();
1212 }
1213 
on_disconnect()1214 void DisplayChannelClient::on_disconnect()
1215 {
1216     DisplayChannel *display;
1217 
1218     spice_debug("trace");
1219 
1220     display = DCC_TO_DC(this);
1221 
1222     dcc_stop(this); // TODO: start/stop -> connect/disconnect?
1223     display_channel_compress_stats_print(display);
1224 
1225     // this was the last channel client
1226     spice_debug("#draw=%d, #glz_draw=%d",
1227                 display->priv->drawable_count,
1228                 display->priv->encoder_shared_data.glz_drawable_count);
1229 }
1230 
dcc_is_low_bandwidth(DisplayChannelClient * dcc)1231 gboolean dcc_is_low_bandwidth(DisplayChannelClient *dcc)
1232 {
1233     return dcc->is_low_bandwidth;
1234 }
1235