summaryrefslogtreecommitdiffstats
path: root/client/display_channel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'client/display_channel.cpp')
-rw-r--r--client/display_channel.cpp1483
1 files changed, 1483 insertions, 0 deletions
diff --git a/client/display_channel.cpp b/client/display_channel.cpp
new file mode 100644
index 00000000..5412e7f2
--- /dev/null
+++ b/client/display_channel.cpp
@@ -0,0 +1,1483 @@
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define GL_GLEXT_PROTOTYPES
+
+#include "common.h"
+#include "canvas.h"
+#include "red_pixmap.h"
+#ifdef USE_OGL
+#include "red_pixmap_gl.h"
+#endif
+#include "debug.h"
+#include "utils.h"
+#include "common.h"
+#include "display_channel.h"
+#include "application.h"
+#include "screen.h"
+#ifdef USE_OGL
+#include "red_gl_canvas.h"
+#endif
+#include "red_cairo_canvas.h"
+#include "red_client.h"
+#include "utils.h"
+#include "debug.h"
+#ifdef WIN32
+#include "red_gdi_canvas.h"
+#endif
+#include "platform_utils.h"
+
+extern "C" {
+#include "libavcodec/avcodec.h"
+}
+
+static Mutex avcodec_mutex;
+
+class SetModeEvent: public SyncEvent {
+public:
+ SetModeEvent(DisplayChannel& channel, int width, int height, int depth)
+ : _channel (channel)
+ , _width (width)
+ , _height (height)
+ , _depth (depth)
+ {
+ }
+
+ virtual void do_responce(Application& application)
+ {
+ _channel.screen()->set_mode(_width, _height, _depth);
+ }
+
+private:
+ DisplayChannel& _channel;
+ int _width;
+ int _height;
+ int _depth;
+};
+
+class DisplayMarkEvent: public Event {
+public:
+ DisplayMarkEvent(int screen_id)
+ : _screen_id (screen_id)
+ {
+ }
+
+ virtual void responce(Application& application)
+ {
+ application.hide_splash(_screen_id);
+ }
+
+private:
+ int _screen_id;
+};
+
+#define CLIP_ARRAY_SIZE 1500
+#define CLIP_ARRAY_SHIFT 500
+
+static uint8_t clip_array[CLIP_ARRAY_SIZE];
+static uint8_t *clip_zero = &clip_array[CLIP_ARRAY_SHIFT];
+
+static void init_clip_array()
+{
+ int i;
+ for (i = 0; i < CLIP_ARRAY_SHIFT; i++) {
+ clip_array[i] = 0;
+ }
+
+ for (i = CLIP_ARRAY_SHIFT + 256; i < CLIP_ARRAY_SIZE; i++) {
+ clip_array[i] = 0xff;
+ }
+
+ for (i = 0; i < 256; i++) {
+ clip_zero[i] = i;
+ }
+}
+
+#define CLIP(val) clip_zero[(int)(val)]
+
+#define R(pixel) (((uint8_t*)(pixel))[0])
+#define G(pixel) (((uint8_t*)(pixel))[1])
+#define B(pixel) (((uint8_t*)(pixel))[2])
+
+#define YUV_TO_RGB(pixel, y, u , v) { \
+ int Y = (y) - 16; \
+ int U = (u) - 128; \
+ int V = (v) - 128; \
+ R(pixel) = CLIP((298 * Y + 409 * V + 128) >> 8); \
+ G(pixel) = CLIP((298 * Y - 100 * U - 208 * V + 128) >> 8); \
+ B(pixel) = CLIP((298 * Y + 516 * U + 128) >> 8); \
+}
+
+static inline void yuv420_to_rgb(AVFrame* frame, uint8_t* data, uint32_t width, uint32_t height,
+ int stride, int top_down)
+{
+ ASSERT(width % 2 == 0);
+ ASSERT(height % 2 == 0);
+
+ if (top_down) {
+ data += stride * height - 1;
+ stride = -stride;
+ }
+
+ // B = 1.164(Y - 16) + 2.018(U - 128)
+ // G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128)
+ // R = 1.164(Y - 16) + 1.596(V - 128)
+
+ uint8_t* y_line = frame->data[0];
+ uint8_t* u = frame->data[1];
+ uint8_t* v = frame->data[2];
+
+ uint32_t y_stride = frame->linesize[0];
+ uint32_t u_stride = frame->linesize[1];
+ uint32_t v_stride = frame->linesize[2];
+
+ for (unsigned int i = 0; i < height / 2; i++) {
+ uint8_t* now = data;
+ uint8_t* y = y_line;
+
+ for (unsigned int j = 0; j < width / 2; j++) {
+ YUV_TO_RGB(now, *y, u[j], v[j]);
+ YUV_TO_RGB(now + sizeof(uint32_t), *(y + 1), u[j], v[j]);
+ YUV_TO_RGB(now + stride, *(y + y_stride), u[j], v[j]);
+ YUV_TO_RGB(now + stride + sizeof(uint32_t), *(y + y_stride + 1), u[j], v[j]);
+ y += 2;
+ now += 2 * sizeof(uint32_t);
+ }
+ data += stride * 2;
+ y_line += y_stride * 2;
+ u += u_stride;
+ v += v_stride;
+ }
+}
+
+#define MAX_VIDEO_FRAMES 30
+#define MAX_OVER 15
+#define MAX_UNDER -15
+
+
+class VideoStream {
+public:
+ VideoStream(RedClient& client, Canvas& canvas, DisplayChannel& channel,
+ uint32_t codec_type, bool top_down, uint32_t stream_width,
+ uint32_t stream_height, uint32_t src_width, uint32_t src_height,
+ Rect* dest, int clip_type, uint32_t num_clip_rects, Rect* clip_rects);
+ ~VideoStream();
+
+ void push_data(uint32_t mm_time, uint32_t length, uint8_t* data, uint32_t ped_size);
+ void set_clip(int type, uint32_t num_clip_rects, Rect* clip_rects);
+ const Rect& get_dest() {return _dest;}
+ void handle_update_mark(uint64_t update_mark);
+ uint32_t handle_timer_update(uint32_t now);
+
+ static void init();
+
+private:
+ void free_frame(uint32_t frame_index);
+ void release_all_bufs();
+ void remove_dead_frames(uint32_t mm_time);
+ uint32_t alloc_frame_slot();
+ void maintenance();
+ void drop_one_frame();
+ uint32_t frame_slot(uint32_t frame_index) { return frame_index % MAX_VIDEO_FRAMES;}
+ static bool is_time_to_display(uint32_t now, uint32_t frame_time);
+
+private:
+ RedClient& _client;
+ Canvas& _canvas;
+ DisplayChannel& _channel;
+ AVCodecContext* _ctx;
+ AVFrame* _frame;
+ int _stream_width;
+ int _stream_height;
+ int _stride;
+ bool _top_down;
+ Rect _dest;
+ QRegion _clip_region;
+ QRegion* _clip;
+
+ struct VideoFrame {
+ uint32_t mm_time;
+ uint32_t compressed_data_size;
+ uint8_t* compressed_data;
+ };
+
+ uint32_t _frames_head;
+ uint32_t _frames_tail;
+ uint32_t _kill_mark;
+ VideoFrame _frames[MAX_VIDEO_FRAMES];
+
+#ifdef WIN32
+ HBITMAP _prev_bitmap;
+ HDC _dc;
+#endif
+ uint8_t *_uncompressed_data;
+ PixmapHeader _pixmap;
+ uint64_t _update_mark;
+ uint32_t _update_time;
+
+public:
+ VideoStream* next;
+};
+
+#ifdef WIN32
+static int create_bitmap(HDC *dc, HBITMAP *prev_bitmap,
+ uint8_t **data, int *nstride,
+ int width, int height)
+{
+ HBITMAP bitmap;
+ struct {
+ BITMAPINFO inf;
+ RGBQUAD palette[255];
+ } bitmap_info;
+
+ memset(&bitmap_info, 0, sizeof(bitmap_info));
+ bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader);
+ bitmap_info.inf.bmiHeader.biWidth = width;
+ bitmap_info.inf.bmiHeader.biHeight = height;
+
+ bitmap_info.inf.bmiHeader.biPlanes = 1;
+ bitmap_info.inf.bmiHeader.biBitCount = 32;
+ bitmap_info.inf.bmiHeader.biCompression = BI_RGB;
+ *nstride = width * 4;
+
+ *dc = create_compatible_dc();
+ if (!*dc) {
+ return 0;
+ }
+
+ bitmap = CreateDIBSection(*dc, &bitmap_info.inf, 0, (void **)data, NULL, 0);
+ if (!bitmap) {
+ DeleteObject(*dc);
+ return 0;
+ }
+
+ *prev_bitmap = (HBITMAP)SelectObject(*dc, bitmap);
+ return 1;
+}
+
+#endif
+
+VideoStream::VideoStream(RedClient& client, Canvas& canvas, DisplayChannel& channel,
+ uint32_t codec_type, bool top_down, uint32_t stream_width,
+ uint32_t stream_height, uint32_t src_width, uint32_t src_height,
+ Rect* dest, int clip_type, uint32_t num_clip_rects,
+ Rect* clip_rects)
+ : _client (client)
+ , _canvas (canvas)
+ , _channel (channel)
+ , _ctx (NULL)
+ , _frame (NULL)
+ , _stream_width (stream_width)
+ , _stream_height (stream_height)
+ , _stride (stream_width * sizeof(uint32_t))
+ , _top_down (top_down)
+ , _dest (*dest)
+ , _clip (NULL)
+ , _frames_head (0)
+ , _frames_tail (0)
+ , _uncompressed_data (NULL)
+ , _update_mark (0)
+ , _update_time (0)
+ , next (NULL)
+{
+ AVCodecContext* ctx = NULL;
+ AVCodec* codec;
+ AVFrame* frame = NULL;
+
+ enum CodecID type;
+
+ memset(_frames, 0, sizeof(_frames));
+ region_init(&_clip_region);
+ switch (codec_type) {
+ case RED_VIDEO_CODEC_TYPE_MJPEG:
+ type = CODEC_ID_MJPEG;
+ break;
+ default:
+ THROW("invalid vide codec type %u", codec_type);
+ }
+
+ if (!(codec = avcodec_find_decoder(type))) {
+ THROW("can't find codec %u", type);
+ }
+
+ if (!(ctx = avcodec_alloc_context())) {
+ THROW("alloc codec ctx failed");
+ }
+ try {
+ if (!(frame = avcodec_alloc_frame())) {
+ THROW("alloc frame failed");
+ }
+
+#ifdef WIN32
+ if (!create_bitmap(&_dc, &_prev_bitmap, &_uncompressed_data, &_stride,
+ stream_width, stream_height)) {
+ THROW("create_bitmap failed");
+ }
+#else
+ _uncompressed_data = new uint8_t[_stride * stream_height];
+#endif
+ _pixmap.width = src_width;
+ _pixmap.height = src_height;
+
+ if (top_down) {
+ _pixmap.data = _uncompressed_data;
+ _pixmap.stride = _stride;
+ } else {
+#ifdef WIN32
+ SetViewportOrgEx(_dc, 0, stream_height - src_height, NULL);
+#endif
+ _pixmap.data = _uncompressed_data + _stride * (src_height - 1);
+ _pixmap.stride = -_stride;
+ }
+
+ set_clip(clip_type, num_clip_rects, clip_rects);
+
+ Lock lock(avcodec_mutex);
+ if (avcodec_open(ctx, codec) < 0) {
+ THROW("open avcodec failed");
+ }
+ } catch (...) {
+ av_free(frame);
+ av_free(ctx);
+ release_all_bufs();
+ throw;
+ }
+ _frame = frame;
+ _ctx = ctx;
+}
+
+VideoStream::~VideoStream()
+{
+ if (_ctx) {
+ Lock lock(avcodec_mutex);
+ avcodec_close(_ctx);
+ av_free(_ctx);
+ av_free(_frame);
+ }
+ release_all_bufs();
+ region_destroy(&_clip_region);
+}
+
+void VideoStream::release_all_bufs()
+{
+ for (int i = 0; i < MAX_VIDEO_FRAMES; i++) {
+ delete[] _frames[i].compressed_data;
+ }
+#ifdef WIN32
+ if (_dc) {
+ HBITMAP bitmap = (HBITMAP)SelectObject(_dc, _prev_bitmap);
+ DeleteObject(bitmap);
+ DeleteObject(_dc);
+ }
+#else
+ delete[] _uncompressed_data;
+#endif
+}
+
+void VideoStream::free_frame(uint32_t frame_index)
+{
+ int slot = frame_slot(frame_index);
+ delete[] _frames[slot].compressed_data;
+ _frames[slot].compressed_data = NULL;
+}
+
+void VideoStream::remove_dead_frames(uint32_t mm_time)
+{
+ while (_frames_head != _frames_tail) {
+ if (int(_frames[frame_slot(_frames_tail)].mm_time - mm_time) >= MAX_UNDER) {
+ return;
+ }
+ free_frame(_frames_tail);
+ _frames_tail++;
+ }
+}
+
+void VideoStream::drop_one_frame()
+{
+ ASSERT(MAX_VIDEO_FRAMES > 2 && (_frames_head - _frames_tail) == MAX_VIDEO_FRAMES);
+ int frame_index = _frames_head - _kill_mark++ % (MAX_VIDEO_FRAMES - 2) - 2;
+
+ free_frame(frame_index);
+
+ while (frame_index != _frames_tail) {
+ --frame_index;
+ _frames[frame_slot(frame_index + 1)] = _frames[frame_slot(frame_index)];
+ }
+ _frames_tail++;
+}
+
+bool VideoStream::is_time_to_display(uint32_t now, uint32_t frame_time)
+{
+ int delta = frame_time - now;
+ return delta <= MAX_OVER && delta >= MAX_UNDER;
+}
+
+void VideoStream::maintenance()
+{
+ uint32_t mm_time = _client.get_mm_time();
+
+ remove_dead_frames(mm_time);
+ if (!_update_mark && !_update_time && _frames_head != _frames_tail) {
+ VideoFrame* tail = &_frames[frame_slot(_frames_tail)];
+
+ ASSERT(tail->compressed_data);
+ uint8_t* data = tail->compressed_data;
+ uint32_t length = tail->compressed_data_size;
+ int got_picture = 0;
+ while (length > 0) {
+ int n = avcodec_decode_video(_ctx, _frame, &got_picture, data, length);
+ if (n < 0) {
+ THROW("decoding eror");
+ }
+ if (got_picture) {
+ yuv420_to_rgb(_frame, _uncompressed_data, _stream_width, _stream_height, _stride,
+ _top_down);
+ ASSERT(length - n == 0);
+ break;
+ }
+ length -= n;
+ data += n;
+ }
+ if (got_picture) {
+#ifdef WIN32
+ _canvas.put_image(_dc, _pixmap, _dest, _clip);
+#else
+ _canvas.put_image(_pixmap, _dest, _clip);
+#endif
+ if (is_time_to_display(mm_time, tail->mm_time)) {
+ _update_mark = _channel.invalidate(_dest, true);
+ Platform::yield();
+ } else {
+ _update_time = tail->mm_time;
+ _channel.stream_update_request(_update_time);
+ }
+ }
+ free_frame(_frames_tail++);
+ }
+}
+
+uint32_t VideoStream::handle_timer_update(uint32_t now)
+{
+ if (!_update_time) {
+ return 0;
+ }
+
+ if (is_time_to_display(now, _update_time)) {
+ _update_time = 0;
+ _update_mark = _channel.invalidate(_dest, true);
+ } else if ((int)(_update_time - now) < 0) {
+ DBG(0, "to late");
+ _update_time = 0;
+ }
+ return _update_time;
+}
+
+void VideoStream::handle_update_mark(uint64_t update_mark)
+{
+ if (!_update_mark || update_mark < _update_mark) {
+ return;
+ }
+ _update_mark = 0;
+ maintenance();
+}
+
+uint32_t VideoStream::alloc_frame_slot()
+{
+ if ((_frames_head - _frames_tail) == MAX_VIDEO_FRAMES) {
+ drop_one_frame();
+ }
+ return frame_slot(_frames_head++);
+}
+
+void VideoStream::push_data(uint32_t mm_time, uint32_t length, uint8_t* data, uint32_t ped_size)
+{
+ if (ped_size < FF_INPUT_BUFFER_PADDING_SIZE) {
+ THROW("insufficieant pedding");
+ }
+ maintenance();
+ uint32_t frame_slot = alloc_frame_slot();
+ _frames[frame_slot].compressed_data = new uint8_t[length];
+ memcpy(_frames[frame_slot].compressed_data, data, length);
+ _frames[frame_slot].compressed_data_size = length;
+ _frames[frame_slot].mm_time = mm_time ? mm_time : 1;
+ maintenance();
+}
+
+void VideoStream::set_clip(int type, uint32_t num_clip_rects, Rect* clip_rects)
+{
+ if (type == CLIP_TYPE_NONE) {
+ _clip = NULL;
+ return;
+ }
+
+ ASSERT(type == CLIP_TYPE_RECTS)
+ region_clear(&_clip_region);
+
+ for (unsigned int i = 0; i < num_clip_rects; i++) {
+ region_add(&_clip_region, &clip_rects[i]);
+ }
+ _clip = &_clip_region;
+}
+
+void VideoStream::init()
+{
+ avcodec_init();
+ avcodec_register_all();
+ init_clip_array();
+}
+
+class AutoVStreamInit {
+public:
+ AutoVStreamInit()
+ {
+ VideoStream::init();
+ }
+};
+
+static AutoVStreamInit auto_init;
+
+
+StreamsTrigger::StreamsTrigger(DisplayChannel& channel)
+ : _channel (channel)
+{
+}
+
+void StreamsTrigger::on_event()
+{
+ _channel.on_streams_trigger();
+}
+
+#ifdef USE_OGL
+
+GLInterruptRecreate::GLInterruptRecreate(DisplayChannel& channel)
+ : _channel (channel)
+{
+}
+
+void GLInterruptRecreate::trigger()
+{
+ Lock lock(_lock);
+ EventsLoop::Trigger::trigger();
+ _cond.wait(lock);
+}
+
+void GLInterruptRecreate::on_event()
+{
+ Lock lock(_lock);
+ _channel.recreate_ogl_context_interrupt();
+ _cond.notify_one();
+}
+
+#endif
+
+void InterruptUpdate::on_event()
+{
+ _channel.update_interrupt();
+}
+
+InterruptUpdate::InterruptUpdate(DisplayChannel& channel)
+ : _channel (channel)
+{
+}
+
+class DisplayHandler: public MessageHandlerImp<DisplayChannel, RED_DISPLAY_MESSAGES_END> {
+public:
+ DisplayHandler(DisplayChannel& channel)
+ : MessageHandlerImp<DisplayChannel, RED_DISPLAY_MESSAGES_END>(channel) {}
+};
+
+DisplayChannel::DisplayChannel(RedClient& client, uint32_t id,
+ PixmapCache& pixmap_cache, GlzDecoderWindow& glz_window)
+ : RedChannel(client, RED_CHANNEL_DISPLAY, id, new DisplayHandler(*this),
+ Platform::PRIORITY_LOW)
+ , ScreenLayer (SCREEN_LAYER_DISPLAY, true)
+ , _canvas (NULL)
+ , _pixmap_cache (pixmap_cache)
+ , _glz_window (glz_window)
+ , _mark (false)
+ , _update_mark (0)
+ , _streams_timer (INVALID_TIMER)
+ , _next_timer_time (0)
+ , _active_streams (NULL)
+ , _streams_trigger (*this)
+#ifdef USE_OGL
+ , _gl_interrupt_recreate (*this)
+#endif
+ , _interrupt_update (*this)
+{
+ DisplayHandler* handler = static_cast<DisplayHandler*>(get_message_handler());
+
+ handler->set_handler(RED_MIGRATE, &DisplayChannel::handle_migrate, 0);
+ handler->set_handler(RED_SET_ACK, &DisplayChannel::handle_set_ack, sizeof(RedSetAck));
+ handler->set_handler(RED_PING, &DisplayChannel::handle_ping, sizeof(RedPing));
+ handler->set_handler(RED_WAIT_FOR_CHANNELS, &DisplayChannel::handle_wait_for_channels,
+ sizeof(RedWaitForChannels));
+ handler->set_handler(RED_DISCONNECTING, &DisplayChannel::handle_disconnect,
+ sizeof(RedDisconnect));
+ handler->set_handler(RED_NOTIFY, &DisplayChannel::handle_notify, sizeof(RedNotify));
+
+ handler->set_handler(RED_DISPLAY_MODE, &DisplayChannel::handle_mode, sizeof(RedMode));
+ handler->set_handler(RED_DISPLAY_MARK, &DisplayChannel::handle_mark, 0);
+ handler->set_handler(RED_DISPLAY_RESET, &DisplayChannel::handle_reset, 0);
+
+ handler->set_handler(RED_DISPLAY_INVAL_LIST,
+ &DisplayChannel::handle_inval_list,
+ sizeof(RedResorceList));
+ handler->set_handler(RED_DISPLAY_INVAL_ALL_PIXMAPS,
+ &DisplayChannel::handle_inval_all_pixmaps,
+ sizeof(RedWaitForChannels));
+ handler->set_handler(RED_DISPLAY_INVAL_PALETTE,
+ &DisplayChannel::handle_inval_palette, sizeof(RedInvalOne));
+ handler->set_handler(RED_DISPLAY_INVAL_ALL_PALETTES,
+ &DisplayChannel::handle_inval_all_palettes, 0);
+
+ handler->set_handler(RED_DISPLAY_STREAM_CREATE, &DisplayChannel::handle_stream_create,
+ sizeof(RedStreamCreate));
+ handler->set_handler(RED_DISPLAY_STREAM_CLIP, &DisplayChannel::handle_stream_clip,
+ sizeof(RedStreamClip));
+ handler->set_handler(RED_DISPLAY_STREAM_DESTROY, &DisplayChannel::handle_stream_destroy,
+ sizeof(RedStreamDestroy));
+ handler->set_handler(RED_DISPLAY_STREAM_DESTROY_ALL,
+ &DisplayChannel::handle_stream_destroy_all, 0);
+
+ get_events_loop().add_trigger(_streams_trigger);
+#ifdef USE_OGL
+ get_events_loop().add_trigger(_gl_interrupt_recreate);
+#endif
+ get_events_loop().add_trigger(_interrupt_update);
+}
+
+DisplayChannel::~DisplayChannel()
+{
+ if (screen()) {
+ screen()->set_update_interrupt_trigger(NULL);
+ }
+ destroy_strams();
+}
+
+void DisplayChannel::destroy_strams()
+{
+ Lock lock(_streams_lock);
+ for (unsigned int i = 0; i < _streams.size(); i++) {
+ delete _streams[i];
+ _streams[i] = NULL;
+ }
+ _active_streams = NULL;
+}
+
+void DisplayChannel::set_draw_handlers()
+{
+ DisplayHandler* handler = static_cast<DisplayHandler*>(get_message_handler());
+
+ handler->set_handler(RED_DISPLAY_COPY_BITS, &DisplayChannel::handle_copy_bits,
+ sizeof(RedCopyBits));
+
+ handler->set_handler(RED_DISPLAY_DRAW_FILL, &DisplayChannel::handle_draw_fill,
+ sizeof(RedFill));
+ handler->set_handler(RED_DISPLAY_DRAW_OPAQUE, &DisplayChannel::handle_draw_opaque,
+ sizeof(RedOpaque));
+ handler->set_handler(RED_DISPLAY_DRAW_COPY, &DisplayChannel::handle_draw_copy,
+ sizeof(RedCopy));
+ handler->set_handler(RED_DISPLAY_DRAW_BLEND, &DisplayChannel::handle_draw_blend,
+ sizeof(RedBlend));
+ handler->set_handler(RED_DISPLAY_DRAW_BLACKNESS, &DisplayChannel::handle_draw_blackness,
+ sizeof(RedBlackness));
+ handler->set_handler(RED_DISPLAY_DRAW_WHITENESS, &DisplayChannel::handle_draw_whiteness,
+ sizeof(RedWhiteness));
+ handler->set_handler(RED_DISPLAY_DRAW_INVERS, &DisplayChannel::handle_draw_invers,
+ sizeof(RedInvers));
+ handler->set_handler(RED_DISPLAY_DRAW_ROP3, &DisplayChannel::handle_draw_rop3,
+ sizeof(RedRop3));
+ handler->set_handler(RED_DISPLAY_DRAW_STROKE, &DisplayChannel::handle_draw_stroke,
+ sizeof(RedStroke));
+ handler->set_handler(RED_DISPLAY_DRAW_TEXT, &DisplayChannel::handle_draw_text,
+ sizeof(RedText));
+ handler->set_handler(RED_DISPLAY_DRAW_TRANSPARENT,
+ &DisplayChannel::handle_draw_transparent, sizeof(RedTransparent));
+ handler->set_handler(RED_DISPLAY_DRAW_ALPHA_BLEND,
+ &DisplayChannel::handle_draw_alpha_blend, sizeof(RedAlphaBlend));
+ handler->set_handler(RED_DISPLAY_STREAM_DATA, &DisplayChannel::handle_stream_data,
+ sizeof(RedStreamData));
+}
+
+void DisplayChannel::clear_draw_handlers()
+{
+ DisplayHandler* handler = static_cast<DisplayHandler*>(get_message_handler());
+
+ handler->set_handler(RED_DISPLAY_COPY_BITS, NULL, 0);
+ handler->set_handler(RED_DISPLAY_DRAW_FILL, NULL, 0);
+ handler->set_handler(RED_DISPLAY_DRAW_OPAQUE, NULL, 0);
+ handler->set_handler(RED_DISPLAY_DRAW_COPY, NULL, 0);
+ handler->set_handler(RED_DISPLAY_DRAW_BLEND, NULL, 0);
+ handler->set_handler(RED_DISPLAY_DRAW_BLACKNESS, NULL, 0);
+ handler->set_handler(RED_DISPLAY_DRAW_WHITENESS, NULL, 0);
+ handler->set_handler(RED_DISPLAY_DRAW_INVERS, NULL, 0);
+ handler->set_handler(RED_DISPLAY_DRAW_ROP3, NULL, 0);
+ handler->set_handler(RED_DISPLAY_DRAW_STROKE, NULL, 0);
+ handler->set_handler(RED_DISPLAY_DRAW_TEXT, NULL, 0);
+ handler->set_handler(RED_DISPLAY_DRAW_TRANSPARENT, NULL, 0);
+ handler->set_handler(RED_DISPLAY_DRAW_ALPHA_BLEND, NULL, 0);
+ handler->set_handler(RED_DISPLAY_STREAM_DATA, NULL, 0);
+}
+
+void DisplayChannel::copy_pixels(const QRegion& dest_region,
+ const PixmapHeader &dest_pixmap)
+{
+ if (!_canvas.get()) {
+ return;
+ }
+
+ _canvas->copy_pixels(dest_region, NULL, &dest_pixmap);
+}
+
+#ifdef USE_OGL
+void DisplayChannel::recreate_ogl_context_interrupt()
+{
+ Canvas* canvas = _canvas.release();
+ if (canvas) {
+ ((GCanvas *)canvas)->textures_lost();
+ delete canvas;
+ }
+
+ if (!create_ogl_canvas(_x_res, _y_res, _depth, 0, _rendertype)) {
+ THROW("create_ogl_canvas failed");
+ }
+}
+
+void DisplayChannel::recreate_ogl_context()
+{
+ if (_canvas.get() && _canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+ if (!screen()->need_recreate_context_gl()) {
+ _gl_interrupt_recreate.trigger();
+ }
+ }
+}
+
+#endif
+
+void DisplayChannel::update_interrupt()
+{
+ if (!_canvas.get() || !screen()) {
+ return;
+ }
+
+#ifdef USE_OGL
+ if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+ ((GCanvas *)(_canvas.get()))->pre_gl_copy();
+ }
+#endif
+
+ screen()->update();
+
+#ifdef USE_OGL
+ if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+ ((GCanvas *)(_canvas.get()))->post_gl_copy();
+ }
+#endif
+}
+
+#ifdef USE_OGL
+
+void DisplayChannel::pre_migrate()
+{
+}
+
+void DisplayChannel::post_migrate()
+{
+#ifdef USE_OGL
+ if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+ _gl_interrupt_recreate.trigger();
+ }
+#endif
+}
+
+#endif
+
+void DisplayChannel::copy_pixels(const QRegion& dest_region,
+ RedDrawable& dest_dc)
+{
+ if (!_canvas.get()) {
+ return;
+ }
+
+ _canvas->copy_pixels(dest_region, dest_dc);
+}
+
+class CreateTimerEvent: public SyncEvent {
+public:
+ CreateTimerEvent(timer_proc_t proc, void* user_data)
+ : SyncEvent()
+ , _proc (proc)
+ , _user_data (user_data)
+ , _timer (INVALID_TIMER)
+ {
+ }
+
+ virtual ~CreateTimerEvent()
+ {
+ ASSERT(_timer == INVALID_TIMER);
+ }
+
+ virtual void do_responce(Application& application)
+ {
+ if ((_timer = Platform::create_interval_timer(_proc, _user_data)) == INVALID_TIMER) {
+ THROW("create timer failed");
+ }
+ }
+
+ TimerID release() { TimerID ret = _timer; _timer = INVALID_TIMER; return ret;}
+
+private:
+ timer_proc_t _proc;
+ void* _user_data;
+ TimerID _timer;
+};
+
+class DestroyTimerEvent: public Event {
+public:
+ DestroyTimerEvent(TimerID timer) : _timer (timer) {}
+
+ virtual void responce(Application& application)
+ {
+ Platform::destroy_interval_timer(_timer);
+ }
+
+private:
+ TimerID _timer;
+};
+
+class ActivateTimerEvent: public Event {
+public:
+ ActivateTimerEvent(DisplayChannel& channel)
+ : _channel (channel)
+ {
+ }
+
+ virtual void responce(Application& application)
+ {
+ _channel.activate_streams_timer();
+ }
+
+private:
+ DisplayChannel& _channel;
+};
+
+void DisplayChannel::streams_timer_callback(void* opaque, TimerID timer)
+{
+ ((DisplayChannel *)opaque)->streams_time();
+}
+
+#define RESET_TIMEOUT (1000 * 5)
+
+void DisplayChannel::reset_timer_callback(void* opaque, TimerID timer)
+{
+ ((RedScreen *)opaque)->unref();
+ Platform::destroy_interval_timer(timer);
+}
+
+void DisplayChannel::on_connect()
+{
+ Message* message = new Message(REDC_DISPLAY_INIT, sizeof(RedcDisplayInit));
+ RedcDisplayInit* init = (RedcDisplayInit*)message->data();
+ init->pixmap_cache_id = 1;
+ init->pixmap_cache_size = get_client().get_pixmap_cache_size();
+ init->glz_dictionary_id = 1;
+ init->glz_dictionary_window_size = get_client().get_glz_window_size();
+ post_message(message);
+ AutoRef<CreateTimerEvent> event(new CreateTimerEvent(streams_timer_callback, this));
+ get_client().push_event(*event);
+ (*event)->wait();
+ _streams_timer = (*event)->release();
+ if (!(*event)->success()) {
+ THROW("create timer failed");
+ }
+}
+
+void DisplayChannel::on_disconnect()
+{
+ if (_canvas.get()) {
+ _canvas->clear();
+ }
+
+ if (screen()) {
+ screen()->set_update_interrupt_trigger(NULL);
+ }
+ detach_from_screen(get_client().get_application());
+ AutoRef<DestroyTimerEvent> event(new DestroyTimerEvent(_streams_timer));
+ get_client().push_event(*event);
+ AutoRef<SyncEvent> sync_event(new SyncEvent());
+ get_client().push_event(*sync_event);
+ (*sync_event)->wait();
+}
+
+bool DisplayChannel::create_cairo_canvas(int width, int height, int depth)
+{
+ try {
+ std::auto_ptr<CCanvas> canvas(new CCanvas(_pixmap_cache, _palette_cache, _glz_window));
+ canvas->set_mode(width, height, depth, screen()->get_window());
+ _canvas.reset(canvas.release());
+ LOG_INFO("display %d: using cairo", get_id());
+ } catch (...) {
+ return false;
+ }
+ return true;
+}
+
+#ifdef USE_OGL
+bool DisplayChannel::create_ogl_canvas(int width, int height, int depth,
+ bool recreate, RenderType rendertype)
+{
+ try {
+ RedWindow *win;
+ int ret = 1;
+
+ std::auto_ptr<GCanvas> canvas(new GCanvas(_pixmap_cache,
+ _palette_cache,
+ _glz_window));
+
+ win = screen()->get_window();
+ if (!ret) {
+ return false;
+ }
+
+ screen()->untouch_context();
+
+ canvas->set_mode(width, height, depth, win, rendertype);
+
+ _canvas.reset(canvas.release());
+ _rendertype = rendertype;
+ LOG_INFO("display %d: using ogl", get_id());
+ } catch (...) {
+ return false;
+ }
+ return true;
+}
+
+#endif
+
+#ifdef WIN32
+bool DisplayChannel::create_gdi_canvas(int width, int height, int depth)
+{
+ try {
+ std::auto_ptr<GDICanvas> canvas(
+ new GDICanvas(_pixmap_cache, _palette_cache, _glz_window));
+ canvas->set_mode(width, height, depth);
+ _canvas.reset(canvas.release());
+ LOG_INFO("display %d: using gdi", get_id());
+ } catch (...) {
+ return false;
+ }
+ return true;
+}
+
+#endif
+
+void DisplayChannel::destroy_canvas()
+{
+ Canvas* canvas = _canvas.release();
+
+ if (canvas) {
+ delete canvas;
+ }
+}
+
+void DisplayChannel::create_canvas(const std::vector<int>& canvas_types, int width,
+ int height, int depth)
+{
+#ifdef USE_OGL
+ bool recreate = true;
+#endif
+ unsigned int i;
+
+ clear_draw_handlers();
+
+#ifdef USE_OGL
+ if (screen()->need_recreate_context_gl()) {
+ recreate = false;
+ }
+#endif
+
+ screen()->set_update_interrupt_trigger(NULL);
+
+ for (i = 0; i < canvas_types.size(); i++) {
+
+ if (canvas_types[i] == CANVAS_OPTION_CAIRO && create_cairo_canvas(width, height, depth)) {
+ break;
+ }
+#ifdef USE_OGL
+ if (canvas_types[i] == CANVAS_OPTION_OGL_FBO && create_ogl_canvas(width, height, depth,
+ recreate,
+ RENDER_TYPE_FBO)) {
+ break;
+ }
+ if (canvas_types[i] == CANVAS_OPTION_OGL_PBUFF && create_ogl_canvas(width, height, depth,
+ recreate,
+ RENDER_TYPE_PBUFF)) {
+ break;
+ }
+#endif
+#ifdef WIN32
+ if (canvas_types[i] == CANVAS_OPTION_GDI && create_gdi_canvas(width, height, depth)) {
+ break;
+ }
+#endif
+ }
+
+ if (i == canvas_types.size()) {
+ THROW("create canvas failed");
+ }
+
+ set_draw_handlers();
+}
+
+void DisplayChannel::handle_mode(RedPeer::InMessage* message)
+{
+ RedMode *mode = (RedMode *)message->data();
+
+ _mark = false;
+ attach_to_screen(get_client().get_application(), get_id());
+ clear_area();
+#ifdef USE_OGL
+ if (screen()) {
+ if (_canvas.get()) {
+ if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+ screen()->unset_type_gl();
+ //glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ }
+ }
+ }
+#endif
+ destroy_canvas();
+ AutoRef<SetModeEvent> event(new SetModeEvent(*this, mode->x_res,
+ mode->y_res, mode->bits));
+ get_client().push_event(*event);
+ (*event)->wait();
+ if (!(*event)->success()) {
+ THROW("set mode failed");
+ }
+
+ _x_res = mode->x_res;
+ _y_res = mode->y_res;
+ _depth = mode->bits;
+
+ create_canvas(get_client().get_application().get_canvas_types(), _x_res,
+ _y_res, _depth);
+#ifdef USE_OGL
+ if (_canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
+ screen()->set_update_interrupt_trigger(&_interrupt_update);
+ screen()->set_type_gl();
+ }
+#endif
+}
+
+void DisplayChannel::handle_mark(RedPeer::InMessage *message)
+{
+ _mark = true;
+ Rect area;
+ area.top = area.left = 0;
+ area.right = _x_res;
+ area.bottom = _y_res;
+
+ AutoRef<DisplayMarkEvent> event(new DisplayMarkEvent(get_id()));
+ get_client().push_event(*event);
+ set_rect_area(area);
+}
+
+void DisplayChannel::handle_reset(RedPeer::InMessage *message)
+{
+ TimerID reset_timer;
+
+ screen()->set_update_interrupt_trigger(NULL);
+
+ if (_canvas.get()) {
+ _canvas->clear();
+ }
+ reset_timer = Platform::create_interval_timer(reset_timer_callback, screen()->ref());
+ if (reset_timer == INVALID_TIMER) {
+ THROW("invalid reset timer");
+ }
+ detach_from_screen(get_client().get_application());
+ _palette_cache.clear();
+
+ Platform::activate_interval_timer(reset_timer, RESET_TIMEOUT);
+}
+
+void DisplayChannel::handle_inval_list(RedPeer::InMessage* message)
+{
+ RedResorceList *inval_list = (RedResorceList *)message->data();
+
+ if (message->size() <
+ sizeof(*inval_list) + inval_list->count * sizeof(inval_list->resorces[0])) {
+ THROW("access violation");
+ }
+
+ for (int i = 0; i < inval_list->count; i++) {
+ if (inval_list->resorces[i].type != RED_RES_TYPE_PIXMAP) {
+ THROW("invalid res type");
+ }
+
+ _pixmap_cache.remove(inval_list->resorces[i].id);
+ }
+}
+
+void DisplayChannel::handle_inval_all_pixmaps(RedPeer::InMessage* message)
+{
+ RedWaitForChannels *wait = (RedWaitForChannels *)message->data();
+ if (message->size() < sizeof(*wait) + wait->wait_count * sizeof(wait->wait_list[0])) {
+ THROW("access violation");
+ }
+ get_client().wait_for_channels(wait->wait_count, wait->wait_list);
+ _pixmap_cache.clear();
+}
+
+void DisplayChannel::handle_inval_palette(RedPeer::InMessage* message)
+{
+ RedInvalOne* inval = (RedInvalOne*)message->data();
+ _palette_cache.remove(inval->id);
+}
+
+void DisplayChannel::handle_inval_all_palettes(RedPeer::InMessage* message)
+{
+ _palette_cache.clear();
+}
+
+void DisplayChannel::set_clip_rects(const Clip& clip, uint32_t& num_clip_rects,
+ Rect*& clip_rects, unsigned long addr_offset,
+ uint8_t *min, uint8_t *max)
+{
+ switch (clip.type) {
+ case CLIP_TYPE_RECTS: {
+ uint32_t* n = (uint32_t*)GET_ADDRESS(clip.data + addr_offset);
+ if (n < (uint32_t*)min || n + 1 > (uint32_t*)max) {
+ THROW("access violation");
+ }
+ num_clip_rects = *n;
+ clip_rects = (Rect *)(n + 1);
+ if (clip_rects + num_clip_rects > (Rect*)max) {
+ THROW("access violation");
+ }
+ break;
+ }
+ case CLIP_TYPE_NONE:
+ num_clip_rects = 0;
+ clip_rects = NULL;
+ break;
+ case CLIP_TYPE_PATH:
+ THROW("unexpected clip type");
+ }
+}
+
+void DisplayChannel::handle_stream_create(RedPeer::InMessage* message)
+{
+ RedStreamCreate* stream_create = (RedStreamCreate*)message->data();
+
+ Lock lock(_streams_lock);
+ if (_streams.size() <= stream_create->id) {
+ _streams.resize(stream_create->id + 1);
+ }
+
+ if (_streams[stream_create->id]) {
+ THROW("stream exist");
+ }
+
+ uint32_t num_clip_rects;
+ Rect* clip_rects;
+ set_clip_rects(stream_create->clip, num_clip_rects, clip_rects,
+ (unsigned long)message->data(), (uint8_t*)(stream_create + 1),
+ message->data() + message->size());
+ _streams[stream_create->id] = new VideoStream(get_client(), *_canvas.get(),
+ *this, stream_create->codec_type,
+ !!(stream_create->flags & STREAM_TOP_DOWN),
+ stream_create->stream_width,
+ stream_create->stream_height,
+ stream_create->src_width,
+ stream_create->src_height,
+ &stream_create->dest,
+ stream_create->clip.type,
+ num_clip_rects,
+ clip_rects);
+ _streams[stream_create->id]->next = _active_streams;
+ _active_streams = _streams[stream_create->id];
+}
+
+void DisplayChannel::handle_stream_data(RedPeer::InMessage* message)
+{
+ RedStreamData* stream_data = (RedStreamData*)message->data();
+ VideoStream* stream;
+
+ if (stream_data->id >= _streams.size() || !(stream = _streams[stream_data->id])) {
+ THROW("invalid stream");
+ }
+
+ if (message->size() < sizeof(RedStreamData) + stream_data->data_size + stream_data->ped_size) {
+ THROW("access violation");
+ }
+
+ stream->push_data(stream_data->multi_media_time, stream_data->data_size, stream_data->data,
+ stream_data->ped_size);
+}
+
+void DisplayChannel::handle_stream_clip(RedPeer::InMessage* message)
+{
+ RedStreamClip* clip_data = (RedStreamClip*)message->data();
+ VideoStream* stream;
+ uint32_t num_clip_rects;
+ Rect* clip_rects;
+
+ if (clip_data->id >= _streams.size() || !(stream = _streams[clip_data->id])) {
+ THROW("invalid stream");
+ }
+
+ if (message->size() < sizeof(RedStreamClip)) {
+ THROW("access violation");
+ }
+ set_clip_rects(clip_data->clip, num_clip_rects, clip_rects,
+ (unsigned long)message->data(), (uint8_t*)(clip_data + 1),
+ message->data() + message->size());
+ Lock lock(_streams_lock);
+ stream->set_clip(clip_data->clip.type, num_clip_rects, clip_rects);
+}
+
+void DisplayChannel::handle_stream_destroy(RedPeer::InMessage* message)
+{
+ RedStreamDestroy* stream_destroy = (RedStreamDestroy*)message->data();
+
+ if (stream_destroy->id >= _streams.size() || !_streams[stream_destroy->id]) {
+ THROW("invalid stream");
+ }
+ Lock lock(_streams_lock);
+
+ VideoStream **active_stream = &_active_streams;
+ for (;;) {
+ if (!*active_stream) {
+ THROW("not in actibe streams");
+ }
+
+ if (*active_stream == _streams[stream_destroy->id]) {
+ *active_stream = _streams[stream_destroy->id]->next;
+ break;
+ }
+ active_stream = &(*active_stream)->next;
+ }
+
+ delete _streams[stream_destroy->id];
+ _streams[stream_destroy->id] = NULL;
+}
+
+void DisplayChannel::handle_stream_destroy_all(RedPeer::InMessage* message)
+{
+ destroy_strams();
+}
+
+#define PRE_DRAW
+#define POST_DRAW
+
+#define DRAW(type) { \
+ PRE_DRAW; \
+ _canvas->draw_##type(*type, message->size()); \
+ POST_DRAW; \
+ invalidate(type->base.box, false); \
+}
+
+void DisplayChannel::handle_copy_bits(RedPeer::InMessage* message)
+{
+ RedCopyBits* copy_bits = (RedCopyBits*)message->data();
+ PRE_DRAW;
+ _canvas->copy_bits(*copy_bits, message->size());
+ POST_DRAW;
+ invalidate(copy_bits->base.box, false);
+}
+
+void DisplayChannel::handle_draw_fill(RedPeer::InMessage* message)
+{
+ RedFill* fill = (RedFill*)message->data();
+ DRAW(fill);
+}
+
+void DisplayChannel::handle_draw_opaque(RedPeer::InMessage* message)
+{
+ RedOpaque* opaque = (RedOpaque*)message->data();
+ DRAW(opaque);
+}
+
+void DisplayChannel::handle_draw_copy(RedPeer::InMessage* message)
+{
+ RedCopy* copy = (RedCopy*)message->data();
+ DRAW(copy);
+}
+
+void DisplayChannel::handle_draw_blend(RedPeer::InMessage* message)
+{
+ RedBlend* blend = (RedBlend*)message->data();
+ DRAW(blend);
+}
+
+void DisplayChannel::handle_draw_blackness(RedPeer::InMessage* message)
+{
+ RedBlackness* blackness = (RedBlackness*)message->data();
+ DRAW(blackness);
+}
+
+void DisplayChannel::handle_draw_whiteness(RedPeer::InMessage* message)
+{
+ RedWhiteness* whiteness = (RedWhiteness*)message->data();
+ DRAW(whiteness);
+}
+
+void DisplayChannel::handle_draw_invers(RedPeer::InMessage* message)
+{
+ RedInvers* invers = (RedInvers*)message->data();
+ DRAW(invers);
+}
+
+void DisplayChannel::handle_draw_rop3(RedPeer::InMessage* message)
+{
+ RedRop3* rop3 = (RedRop3*)message->data();
+ DRAW(rop3);
+}
+
+void DisplayChannel::handle_draw_stroke(RedPeer::InMessage* message)
+{
+ RedStroke* stroke = (RedStroke*)message->data();
+ DRAW(stroke);
+}
+
+void DisplayChannel::handle_draw_text(RedPeer::InMessage* message)
+{
+ RedText* text = (RedText*)message->data();
+ DRAW(text);
+}
+
+void DisplayChannel::handle_draw_transparent(RedPeer::InMessage* message)
+{
+ RedTransparent* transparent = (RedTransparent*)message->data();
+ DRAW(transparent);
+}
+
+void DisplayChannel::handle_draw_alpha_blend(RedPeer::InMessage* message)
+{
+ RedAlphaBlend* alpha_blend = (RedAlphaBlend*)message->data();
+ DRAW(alpha_blend);
+}
+
+void DisplayChannel::streams_time()
+{
+ _next_timer_time = 0;
+ Lock lock(_streams_lock);
+ uint32_t mm_time = get_client().get_mm_time();
+ uint32_t next_time = 0;
+ VideoStream* stream = _active_streams;
+ while (stream) {
+ uint32_t next_frame_time;
+ if ((next_frame_time = stream->handle_timer_update(mm_time))) {
+ if (!next_time || int(next_frame_time - next_time) < 0) {
+ next_time = next_frame_time;
+ }
+ }
+ stream = stream->next;
+ }
+ Lock timer_lock(_timer_lock);
+ mm_time = get_client().get_mm_time();
+ next_time = mm_time + 15;
+ if (next_time && (!_next_timer_time || int(next_time - _next_timer_time) < 0)) {
+ Platform::activate_interval_timer(_streams_timer, MAX(int(next_time - mm_time), 0));
+ _next_timer_time = next_time;
+ } else if (!_next_timer_time) {
+ Platform::deactivate_interval_timer(_streams_timer);
+ }
+ timer_lock.unlock();
+ lock.unlock();
+ Platform::yield();
+}
+
+void DisplayChannel::activate_streams_timer()
+{
+ uint32_t next_time = _next_timer_time;
+ if (!next_time) {
+ return;
+ }
+
+ int delta = next_time - get_client().get_mm_time();
+ if (delta <= 0) {
+ streams_time();
+ } else {
+ Lock timer_lock(_timer_lock);
+ if (!_next_timer_time) {
+ return;
+ }
+ delta = _next_timer_time - get_client().get_mm_time();
+ Platform::activate_interval_timer(_streams_timer, delta);
+ }
+}
+
+void DisplayChannel::stream_update_request(uint32_t mm_time)
+{
+ Lock lock(_timer_lock);
+ if (_next_timer_time && int(mm_time - _next_timer_time) > 0) {
+ return;
+ }
+ _next_timer_time = mm_time;
+ lock.unlock();
+ AutoRef<ActivateTimerEvent> event(new ActivateTimerEvent(*this));
+ get_client().push_event(*event);
+}
+
+void DisplayChannel::on_update_completion(uint64_t mark)
+{
+#ifndef RED64
+ Lock lock(_mark_lock);
+#endif
+ _update_mark = mark;
+#ifndef RED64
+ lock.unlock();
+#endif
+ _streams_trigger.trigger();
+}
+
+void DisplayChannel::on_streams_trigger()
+{
+#ifndef RED64
+ Lock lock(_mark_lock);
+#endif
+ uint64_t update_mark = _update_mark;
+#ifndef RED64
+ lock.unlock();
+#endif
+ VideoStream* stream = _active_streams;
+ while (stream) {
+ stream->handle_update_mark(update_mark);
+ stream = stream->next;
+ }
+}
+
+class DisplayFactory: public ChannelFactory {
+public:
+ DisplayFactory() : ChannelFactory(RED_CHANNEL_DISPLAY) {}
+ virtual RedChannel* construct(RedClient& client, uint32_t id)
+ {
+ return new DisplayChannel(client, id,
+ client.get_pixmap_cache(), client.get_glz_window());
+ }
+};
+
+static DisplayFactory factory;
+
+ChannelFactory& DisplayChannel::Factory()
+{
+ return factory;
+}
+