spice/client/display_channel.cpp
Christophe Fergeau 78c1465ed3 add #include <config.h> to all source files
When using config.h, it must be the very first include in all source
files since it contains #define that may change the compilation process
(eg libc structure layout changes when it's used to enable large file
support on 32 bit x86 archs). This commit adds it at the beginning
of all .c and .cpp files
2011-05-03 14:44:10 +02:00

1748 lines
50 KiB
C++

/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#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_sw_canvas.h"
#include "red_client.h"
#include "utils.h"
#include "debug.h"
#ifdef WIN32
#include "red_gdi_canvas.h"
#endif
#include "platform_utils.h"
#include "inputs_channel.h"
#include "cursor_channel.h"
#include "mjpeg_decoder.h"
class CreatePrimarySurfaceEvent: public SyncEvent {
public:
CreatePrimarySurfaceEvent(DisplayChannel& channel, int width, int height, uint32_t format)
: _channel (channel)
, _width (width)
, _height (height)
, _format (format)
{
}
virtual void do_response(AbstractProcessLoop& events_loop)
{
Application* app = (Application*)events_loop.get_owner();
_channel.screen()->lock_size();
app->resize_screen(_channel.screen(), _width, _height);
_channel.create_canvas(0, app->get_canvas_types(), _width, _height, _format);
}
private:
DisplayChannel& _channel;
int _width;
int _height;
uint32_t _format;
};
class DestroyPrimarySurfaceEvent: public SyncEvent {
public:
DestroyPrimarySurfaceEvent(DisplayChannel& channel)
: _channel (channel)
{
}
virtual void do_response(AbstractProcessLoop& events_loop)
{
_channel.destroy_canvas(0);
}
private:
DisplayChannel& _channel;
};
class CreateSurfaceEvent: public SyncEvent {
public:
CreateSurfaceEvent(DisplayChannel& channel, int surface_id, int width, int height,
uint32_t format)
: _channel (channel)
, _surface_id (surface_id)
, _width (width)
, _height (height)
, _format (format)
{
}
virtual void do_response(AbstractProcessLoop& events_loop)
{
Application* app = (Application*)events_loop.get_owner();
_channel.create_canvas(_surface_id, app->get_canvas_types(), _width, _height, _format);
}
private:
DisplayChannel& _channel;
int _surface_id;
int _width;
int _height;
uint32_t _format;
};
class DestroySurfaceEvent: public SyncEvent {
public:
DestroySurfaceEvent(DisplayChannel& channel, int surface_id)
: _channel (channel)
, _surface_id (surface_id)
{
}
virtual void do_response(AbstractProcessLoop& events_loop)
{
_channel.destroy_canvas(_surface_id);
}
private:
DisplayChannel& _channel;
int _surface_id;
};
class UnlockScreenEvent: public Event {
public:
UnlockScreenEvent(RedScreen* screen)
: _screen (screen->ref())
{
}
virtual void response(AbstractProcessLoop& events_loop)
{
(*_screen)->unlock_size();
}
private:
AutoRef<RedScreen> _screen;
};
#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,
SpiceRect* dest, int clip_type, uint32_t num_clip_rects, SpiceRect* clip_rects);
~VideoStream();
void push_data(uint32_t mm_time, uint32_t length, uint8_t* data);
void set_clip(int type, uint32_t num_clip_rects, SpiceRect* clip_rects);
const SpiceRect& get_dest() {return _dest;}
void handle_update_mark(uint64_t update_mark);
uint32_t handle_timer_update(uint32_t now);
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;
MJpegDecoder *_mjpeg_decoder;
int _stream_width;
int _stream_height;
int _stride;
bool _top_down;
SpiceRect _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,
SpiceRect* dest, int clip_type, uint32_t num_clip_rects,
SpiceRect* clip_rects)
: _client (client)
, _canvas (canvas)
, _channel (channel)
, _mjpeg_decoder (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)
, _kill_mark (0)
, _uncompressed_data (NULL)
, _update_mark (0)
, _update_time (0)
, next (NULL)
{
memset(_frames, 0, sizeof(_frames));
region_init(&_clip_region);
if (codec_type != SPICE_VIDEO_CODEC_TYPE_MJPEG) {
THROW("invalid video codec type %u", codec_type);
}
try {
#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;
_mjpeg_decoder = new MJpegDecoder(stream_width, stream_height, _stride, _uncompressed_data, channel.get_peer_major() == 1);
#ifdef WIN32
SetViewportOrgEx(_dc, 0, stream_height - src_height, NULL);
#endif
if (_top_down) {
_pixmap.data = _uncompressed_data;
_pixmap.stride = _stride;
} else {
_pixmap.data = _uncompressed_data + _stride * (src_height - 1);
_pixmap.stride = -_stride;
}
set_clip(clip_type, num_clip_rects, clip_rects);
} catch (...) {
if (_mjpeg_decoder) {
delete _mjpeg_decoder;
_mjpeg_decoder = NULL;
}
release_all_bufs();
throw;
}
}
VideoStream::~VideoStream()
{
if (_mjpeg_decoder) {
delete _mjpeg_decoder;
_mjpeg_decoder = NULL;
}
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);
unsigned 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;
got_picture =_mjpeg_decoder->decode_data(data, length);
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)
{
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, SpiceRect* clip_rects)
{
if (type == SPICE_CLIP_TYPE_NONE) {
_clip = NULL;
return;
}
ASSERT(type == SPICE_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;
}
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);
EventSources::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)
{
}
StreamsTimer::StreamsTimer(DisplayChannel& channel)
: _channel (channel)
{
}
void StreamsTimer::response(AbstractProcessLoop& events_loop)
{
_channel.streams_time();
}
#define RESET_TIMEOUT (1000 * 5)
class ResetTimer: public Timer {
public:
ResetTimer(RedScreen* screen, RedClient& client) : _screen(screen), _client(client) {}
virtual void response(AbstractProcessLoop& events_loop);
private:
RedScreen* _screen;
RedClient& _client;
};
void ResetTimer::response(AbstractProcessLoop& events_loop)
{
_screen->unref();
_client.deactivate_interval_timer(this);
}
void DisplaySurfacesManger::add_surface(int surface_id, SpiceCanvas *surface)
{
surfaces.add(surface_id, surface);
}
void DisplaySurfacesManger::del_surface(int surface_id)
{
surfaces.remove(surface_id);
}
void DisplaySurfacesManger::add_canvas(int surface_id, Canvas *canvas)
{
canvases.add(surface_id, canvas);
}
void DisplaySurfacesManger::del_canvas(int surface_id)
{
canvases.remove(surface_id);
}
CSurfaces& DisplaySurfacesManger::get_surfaces()
{
return surfaces;
}
bool DisplaySurfacesManger::is_present_canvas(int surface_id)
{
return canvases.is_present(surface_id);
}
Canvas* DisplaySurfacesManger::get_canvas(int surface_id)
{
return canvases.get(surface_id);
}
class DisplayHandler: public MessageHandlerImp<DisplayChannel, SPICE_CHANNEL_DISPLAY> {
public:
DisplayHandler(DisplayChannel& channel)
: MessageHandlerImp<DisplayChannel, SPICE_CHANNEL_DISPLAY>(channel) {}
};
DisplayChannel::DisplayChannel(RedClient& client, uint32_t id,
PixmapCache& pixmap_cache, GlzDecoderWindow& glz_window)
: RedChannel(client, SPICE_CHANNEL_DISPLAY, id, new DisplayHandler(*this),
Platform::PRIORITY_LOW)
, ScreenLayer (SCREEN_LAYER_DISPLAY, true)
, _pixmap_cache (pixmap_cache)
, _glz_window (glz_window)
, _mark (false)
, _update_mark (0)
, _streams_timer (new StreamsTimer(*this))
, _next_timer_time (0)
, _cursor_visibal (false)
, _active_pointer (false)
, _capture_mouse_mode (false)
, _inputs_channel (NULL)
, _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(SPICE_MSG_MIGRATE, &DisplayChannel::handle_migrate);
handler->set_handler(SPICE_MSG_SET_ACK, &DisplayChannel::handle_set_ack);
handler->set_handler(SPICE_MSG_PING, &DisplayChannel::handle_ping);
handler->set_handler(SPICE_MSG_WAIT_FOR_CHANNELS, &DisplayChannel::handle_wait_for_channels);
handler->set_handler(SPICE_MSG_DISCONNECTING, &DisplayChannel::handle_disconnect);
handler->set_handler(SPICE_MSG_NOTIFY, &DisplayChannel::handle_notify);
handler->set_handler(SPICE_MSG_DISPLAY_MODE, &DisplayChannel::handle_mode);
handler->set_handler(SPICE_MSG_DISPLAY_MARK, &DisplayChannel::handle_mark);
handler->set_handler(SPICE_MSG_DISPLAY_RESET, &DisplayChannel::handle_reset);
handler->set_handler(SPICE_MSG_DISPLAY_INVAL_LIST,
&DisplayChannel::handle_inval_list);
handler->set_handler(SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS,
&DisplayChannel::handle_inval_all_pixmaps);
handler->set_handler(SPICE_MSG_DISPLAY_INVAL_PALETTE,
&DisplayChannel::handle_inval_palette);
handler->set_handler(SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES,
&DisplayChannel::handle_inval_all_palettes);
handler->set_handler(SPICE_MSG_DISPLAY_STREAM_CREATE, &DisplayChannel::handle_stream_create);
handler->set_handler(SPICE_MSG_DISPLAY_STREAM_CLIP, &DisplayChannel::handle_stream_clip);
handler->set_handler(SPICE_MSG_DISPLAY_STREAM_DESTROY, &DisplayChannel::handle_stream_destroy);
handler->set_handler(SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL,
&DisplayChannel::handle_stream_destroy_all);
handler->set_handler(SPICE_MSG_DISPLAY_SURFACE_CREATE, &DisplayChannel::handle_surface_create);
handler->set_handler(SPICE_MSG_DISPLAY_SURFACE_DESTROY, &DisplayChannel::handle_surface_destroy);
get_process_loop().add_trigger(_streams_trigger);
#ifdef USE_OGL
get_process_loop().add_trigger(_gl_interrupt_recreate);
#endif
get_process_loop().add_trigger(_interrupt_update);
set_draw_handlers();
}
DisplayChannel::~DisplayChannel()
{
if (screen()) {
screen()->set_update_interrupt_trigger(NULL);
}
//destroy_canvas(); fixme destroy all
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(SPICE_MSG_DISPLAY_COPY_BITS, &DisplayChannel::handle_copy_bits);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_FILL, &DisplayChannel::handle_draw_fill);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_OPAQUE, &DisplayChannel::handle_draw_opaque);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_COPY, &DisplayChannel::handle_draw_copy);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_BLEND, &DisplayChannel::handle_draw_blend);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_BLACKNESS, &DisplayChannel::handle_draw_blackness);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_WHITENESS, &DisplayChannel::handle_draw_whiteness);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_INVERS, &DisplayChannel::handle_draw_invers);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_ROP3, &DisplayChannel::handle_draw_rop3);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_STROKE, &DisplayChannel::handle_draw_stroke);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_TEXT, &DisplayChannel::handle_draw_text);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_TRANSPARENT,
&DisplayChannel::handle_draw_transparent);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND,
&DisplayChannel::handle_draw_alpha_blend);
handler->set_handler(SPICE_MSG_DISPLAY_STREAM_DATA, &DisplayChannel::handle_stream_data);
}
void DisplayChannel::clear_draw_handlers()
{
DisplayHandler* handler = static_cast<DisplayHandler*>(get_message_handler());
handler->set_handler(SPICE_MSG_DISPLAY_COPY_BITS, NULL);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_FILL, NULL);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_OPAQUE, NULL);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_COPY, NULL);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_BLEND, NULL);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_BLACKNESS, NULL);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_WHITENESS, NULL);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_INVERS, NULL);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_ROP3, NULL);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_STROKE, NULL);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_TEXT, NULL);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_TRANSPARENT, NULL);
handler->set_handler(SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND, NULL);
handler->set_handler(SPICE_MSG_DISPLAY_STREAM_DATA, NULL);
}
void DisplayChannel::copy_pixels(const QRegion& dest_region,
const PixmapHeader &dest_pixmap)
{
Canvas *canvas;
if (!surfaces_mngr.is_present_canvas(0)) {
return;
}
canvas = surfaces_mngr.get_canvas(0);
canvas->copy_pixels(dest_region, NULL, &dest_pixmap);
}
#ifdef USE_OGL
void DisplayChannel::recreate_ogl_context_interrupt()
{
Canvas* canvas;
if (surfaces_mngr.is_present_canvas(0)) { //fix me to all surfaces
canvas = surfaces_mngr.get_canvas(0);
((GCanvas *)(canvas))->touch_context();
((GCanvas *)canvas)->textures_lost();
delete canvas;
}
if (!create_ogl_canvas(0, _x_res, _y_res, _format, 0, _rendertype)) {
THROW("create_ogl_canvas failed");
}
canvas = surfaces_mngr.get_canvas(0);
((GCanvas *)(canvas))->touch_context();
}
void DisplayChannel::recreate_ogl_context()
{
if (surfaces_mngr.is_present_canvas(0) && surfaces_mngr.get_canvas(0)->get_pixmap_type() ==
CANVAS_TYPE_GL) {
if (!screen()->need_recreate_context_gl()) {
_gl_interrupt_recreate.trigger();
}
}
}
#endif
void DisplayChannel::update_cursor()
{
if (!screen() || !_active_pointer) {
return;
}
if (_capture_mouse_mode) {
//todo: use special cursor for capture mode
AutoRef<LocalCursor> default_cursor(Platform::create_default_cursor());
screen()->set_cursor(*default_cursor);
return;
}
if (!_cursor_visibal || !*_cursor) {
screen()->hide_cursor();
return;
}
if (!(*_cursor)->get_local()) {
AutoRef<LocalCursor> local_cursor(Platform::create_local_cursor(*_cursor));
if (*local_cursor == NULL) {
THROW("create local cursor failed");
}
(*_cursor)->set_local(*local_cursor);
}
screen()->set_cursor((*_cursor)->get_local());
}
void DisplayChannel::set_cursor(CursorData* cursor)
{
ASSERT(cursor);
_cursor.reset(cursor->ref());
_cursor_visibal = true;
update_cursor();
}
void DisplayChannel::hide_cursor()
{
_cursor_visibal = false;
update_cursor();
}
void DisplayChannel::attach_inputs(InputsChannel* inputs_channel)
{
if (_inputs_channel) {
return;
}
_inputs_channel = inputs_channel;
if (_active_pointer && !_capture_mouse_mode) {
_inputs_channel->on_mouse_position(_pointer_pos.x, _pointer_pos.y,
_buttons_state, get_id());
}
}
void DisplayChannel::detach_inputs()
{
_inputs_channel = NULL;
}
bool DisplayChannel::pointer_test(int x, int y)
{
return contains_point(x, y);
}
void DisplayChannel::on_pointer_enter(int x, int y, unsigned int buttons_state)
{
_active_pointer = true;
update_cursor();
on_pointer_motion(x, y, buttons_state);
}
void DisplayChannel::on_pointer_motion(int x, int y, unsigned int buttons_state)
{
_pointer_pos.x = x;
_pointer_pos.y = y;
_buttons_state = buttons_state;
if (!_capture_mouse_mode && _inputs_channel) {
_inputs_channel->on_mouse_position(x, y, buttons_state, get_id());
}
}
void DisplayChannel::on_pointer_leave()
{
_active_pointer = false;
}
void DisplayChannel::on_mouse_button_press(int button, int buttons_state)
{
_buttons_state = buttons_state;
if (!_capture_mouse_mode && _inputs_channel) {
_inputs_channel->on_mouse_down(button, buttons_state);
}
}
void DisplayChannel::on_mouse_button_release(int button, int buttons_state)
{
_buttons_state = buttons_state;
if (_capture_mouse_mode) {
if (button == SPICE_MOUSE_BUTTON_LEFT) {
get_client().on_mouse_capture_trigger(*screen());
}
return;
}
if (_inputs_channel) {
_inputs_channel->on_mouse_up(button, buttons_state);
}
}
void DisplayChannel::set_capture_mode(bool on)
{
if (_capture_mouse_mode == on) {
return;
}
_capture_mouse_mode = on;
update_cursor();
if (_inputs_channel && !_capture_mouse_mode && _active_pointer) {
_inputs_channel->on_mouse_position(_pointer_pos.x, _pointer_pos.y, _buttons_state,
get_id());
}
}
void DisplayChannel::update_interrupt()
{
#ifdef USE_OGL
Canvas *canvas;
#endif
if (!surfaces_mngr.is_present_canvas(0) || !screen()) {
return;
}
#ifdef USE_OGL
canvas = surfaces_mngr.get_canvas(0);
if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
((GCanvas *)(canvas))->pre_gl_copy();
}
#endif
screen()->update();
#ifdef USE_OGL
if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
((GCanvas *)(canvas))->post_gl_copy();
}
#endif
}
#ifdef USE_OGL
void DisplayChannel::pre_migrate()
{
}
void DisplayChannel::post_migrate()
{
#ifdef USE_OGL
if (surfaces_mngr.get_canvas(0)->get_pixmap_type() == CANVAS_TYPE_GL) {
_gl_interrupt_recreate.trigger();
}
#endif
}
#endif
void DisplayChannel::copy_pixels(const QRegion& dest_region,
RedDrawable& dest_dc)
{
Canvas *canvas;
if (!surfaces_mngr.is_present_canvas(0)) {
return;
}
canvas = surfaces_mngr.get_canvas(0);
canvas->copy_pixels(dest_region, dest_dc);
}
class ActivateTimerEvent: public Event {
public:
ActivateTimerEvent(DisplayChannel& channel)
: _channel (channel)
{
}
virtual void response(AbstractProcessLoop& events_loop)
{
_channel.activate_streams_timer();
}
private:
DisplayChannel& _channel;
};
class AttachChannelsEvent : public Event {
public:
AttachChannelsEvent(DisplayChannel& channel) : Event(), _channel (channel) {}
class AttachChannels: public ForEachChannelFunc {
public:
AttachChannels(DisplayChannel& channel)
: _channel (channel)
{
}
virtual bool operator() (RedChannel& channel)
{
if (channel.get_type() == SPICE_CHANNEL_CURSOR && channel.get_id() == _channel.get_id()) {
static_cast<CursorChannel&>(channel).attach_display(&_channel);
} else if (channel.get_type() == SPICE_CHANNEL_INPUTS) {
_channel.attach_inputs(&static_cast<InputsChannel&>(channel));
}
return false;
}
private:
DisplayChannel& _channel;
};
virtual void response(AbstractProcessLoop& events_loop)
{
uint32_t mouse_mode = _channel.get_client().get_mouse_mode();
_channel._capture_mouse_mode = (mouse_mode == SPICE_MOUSE_MODE_SERVER);
AttachChannels for_each_func(_channel);
_channel.get_client().for_each_channel(for_each_func);
}
private:
DisplayChannel& _channel;
};
class DetachChannelsEvent : public Event {
public:
DetachChannelsEvent(DisplayChannel& channel) : Event(), _channel (channel) {}
class DetatchChannels: public ForEachChannelFunc {
public:
DetatchChannels(DisplayChannel& channel)
: _channel (channel)
{
}
virtual bool operator() (RedChannel& channel)
{
if (channel.get_type() == SPICE_CHANNEL_CURSOR && channel.get_id() == _channel.get_id()) {
static_cast<CursorChannel&>(channel).detach_display();
return true;
}
return false;
}
private:
DisplayChannel& _channel;
};
virtual void response(AbstractProcessLoop& events_loop)
{
DetatchChannels for_each_func(_channel);
_channel.get_client().for_each_channel(for_each_func);
}
private:
DisplayChannel& _channel;
};
void DisplayChannel::on_connect()
{
Message* message = new Message(SPICE_MSGC_DISPLAY_INIT);
SpiceMsgcDisplayInit init;
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();
_marshallers->msgc_display_init(message->marshaller(), &init);
post_message(message);
AutoRef<AttachChannelsEvent> attach_channels(new AttachChannelsEvent(*this));
get_client().push_event(*attach_channels);
}
void DisplayChannel::on_disconnect()
{
if (surfaces_mngr.is_present_canvas(0)) {
Canvas *canvas;
canvas = surfaces_mngr.get_canvas(0);
canvas->clear();
}
if (screen()) {
screen()->set_update_interrupt_trigger(NULL);
}
AutoRef<DetachChannelsEvent> detach_channels(new DetachChannelsEvent(*this));
get_client().push_event(*detach_channels);
if (screen()) {
AutoRef<UnlockScreenEvent> unlock_event(new UnlockScreenEvent(screen()));
get_client().push_event(*unlock_event);
detach_from_screen(get_client().get_application());
}
get_client().deactivate_interval_timer(*_streams_timer);
AutoRef<SyncEvent> sync_event(new SyncEvent());
get_client().push_event(*sync_event);
(*sync_event)->wait();
}
bool DisplayChannel::create_sw_canvas(int surface_id, int width, int height, uint32_t format)
{
try {
SCanvas *canvas = new SCanvas(surface_id == 0, width, height, format,
screen()->get_window(),
_pixmap_cache, _palette_cache, _glz_window,
surfaces_mngr.get_surfaces());
surfaces_mngr.add_canvas(surface_id, canvas);
surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
if (surface_id == 0) {
LOG_INFO("display %d: using sw", get_id());
}
} catch (...) {
return false;
}
return true;
}
#ifdef USE_OGL
bool DisplayChannel::create_ogl_canvas(int surface_id, int width, int height, uint32_t format,
bool recreate, RenderType rendertype)
{
try {
RedWindow *win;
win = screen()->get_window();
GCanvas *canvas = new GCanvas(width, height, format, win, rendertype,
_pixmap_cache,
_palette_cache,
_glz_window,
surfaces_mngr.get_surfaces());
screen()->untouch_context();
surfaces_mngr.add_canvas(surface_id, canvas);
surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
_rendertype = rendertype;
if (surface_id == 0) {
LOG_INFO("display %d: using ogl", get_id());
}
} catch (...) {
return false;
}
return true;
}
#endif
#ifdef WIN32
bool DisplayChannel::create_gdi_canvas(int surface_id, int width, int height, uint32_t format)
{
try {
GDICanvas *canvas = new GDICanvas(width, height, format,
_pixmap_cache, _palette_cache, _glz_window,
surfaces_mngr.get_surfaces());
surfaces_mngr.add_canvas(surface_id, canvas);
surfaces_mngr.add_surface(surface_id, canvas->get_internal_canvas());
if (surface_id == 0) {
LOG_INFO("display %d: using gdi", get_id());
}
} catch (...) {
return false;
}
return true;
}
#endif
void DisplayChannel::destroy_canvas(int surface_id)
{
Canvas *canvas;
if (!surfaces_mngr.is_present_canvas(surface_id)) {
return;
}
canvas = surfaces_mngr.get_canvas(surface_id);
#ifdef USE_OGL
if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
((GCanvas *)(canvas))->touch_context();
}
#endif
surfaces_mngr.del_canvas(surface_id);
surfaces_mngr.del_surface(surface_id);
delete canvas;
}
void DisplayChannel::create_canvas(int surface_id, const std::vector<int>& canvas_types, int width,
int height, uint32_t format)
{
#ifdef USE_OGL
bool recreate = true;
#endif
unsigned int i;
#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_SW && create_sw_canvas(surface_id, width, height, format)) {
break;
}
#ifdef USE_OGL
if (canvas_types[i] == CANVAS_OPTION_OGL_FBO && create_ogl_canvas(surface_id, width, height, format,
recreate,
RENDER_TYPE_FBO)) {
break;
}
if (canvas_types[i] == CANVAS_OPTION_OGL_PBUFF && create_ogl_canvas(surface_id, width, height, format,
recreate,
RENDER_TYPE_PBUFF)) {
break;
}
#endif
#ifdef WIN32
if (canvas_types[i] == CANVAS_OPTION_GDI && create_gdi_canvas(surface_id, width, height, format)) {
break;
}
#endif
}
if (i == canvas_types.size()) {
THROW("create canvas failed");
}
}
void DisplayChannel::handle_mode(RedPeer::InMessage* message)
{
SpiceMsgDisplayMode *mode = (SpiceMsgDisplayMode *)message->data();
if (surfaces_mngr.is_present_canvas(0)) {
destroy_primary_surface();
}
create_primary_surface(mode->x_res, mode->y_res,
mode->bits == 32 ? SPICE_SURFACE_FMT_32_xRGB : SPICE_SURFACE_FMT_16_555);
}
void DisplayChannel::handle_mark(RedPeer::InMessage *message)
{
_mark = true;
SpiceRect area;
area.top = area.left = 0;
area.right = _x_res;
area.bottom = _y_res;
AutoRef<VisibilityEvent> event(new VisibilityEvent(get_id()));
get_client().push_event(*event);
set_rect_area(area);
}
void DisplayChannel::reset_screen()
{
AutoRef<UnlockScreenEvent> unlock_event(new UnlockScreenEvent(screen()));
get_client().push_event(*unlock_event);
screen()->set_update_interrupt_trigger(NULL);
AutoRef<ResetTimer> reset_timer(new ResetTimer(screen()->ref(), get_client()));
detach_from_screen(get_client().get_application());
get_client().activate_interval_timer(*reset_timer, RESET_TIMEOUT);
}
void DisplayChannel::handle_reset(RedPeer::InMessage *message)
{
if (surfaces_mngr.is_present_canvas(0)) {
Canvas *canvas;
canvas = surfaces_mngr.get_canvas(0);
canvas->clear();
}
_palette_cache.clear();
reset_screen();
}
void DisplayChannel::handle_inval_list(RedPeer::InMessage* message)
{
SpiceResourceList *inval_list = (SpiceResourceList *)message->data();
if (message->size() <
sizeof(*inval_list) + inval_list->count * sizeof(inval_list->resources[0])) {
THROW("access violation");
}
for (int i = 0; i < inval_list->count; i++) {
if (inval_list->resources[i].type != SPICE_RES_TYPE_PIXMAP) {
THROW("invalid res type");
}
_pixmap_cache.remove(inval_list->resources[i].id);
}
}
void DisplayChannel::handle_inval_all_pixmaps(RedPeer::InMessage* message)
{
SpiceMsgWaitForChannels *wait = (SpiceMsgWaitForChannels *)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)
{
SpiceMsgDisplayInvalOne* inval = (SpiceMsgDisplayInvalOne*)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 SpiceClip& clip, uint32_t& num_clip_rects,
SpiceRect*& clip_rects)
{
switch (clip.type) {
case SPICE_CLIP_TYPE_RECTS: {
num_clip_rects = clip.rects->num_rects;
clip_rects = clip.rects->rects;
break;
}
case SPICE_CLIP_TYPE_NONE:
num_clip_rects = 0;
clip_rects = NULL;
break;
default:
THROW("unexpected clip type");
}
}
void DisplayChannel::handle_stream_create(RedPeer::InMessage* message)
{
SpiceMsgDisplayStreamCreate* stream_create = (SpiceMsgDisplayStreamCreate*)message->data();
int surface_id = stream_create->surface_id;
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;
SpiceRect* clip_rects;
set_clip_rects(stream_create->clip, num_clip_rects, clip_rects);
_streams[stream_create->id] = new VideoStream(get_client(), *surfaces_mngr.get_canvas(surface_id),
*this, stream_create->codec_type,
!!(stream_create->flags & SPICE_STREAM_FLAGS_TOP_DOWN),
stream_create->stream_width,
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)
{
SpiceMsgDisplayStreamData* stream_data = (SpiceMsgDisplayStreamData*)message->data();
VideoStream* stream;
if (stream_data->id >= _streams.size() || !(stream = _streams[stream_data->id])) {
THROW("invalid stream");
}
if (message->size() < sizeof(SpiceMsgDisplayStreamData) + stream_data->data_size) {
THROW("access violation");
}
stream->push_data(stream_data->multi_media_time, stream_data->data_size, stream_data->data);
}
void DisplayChannel::handle_stream_clip(RedPeer::InMessage* message)
{
SpiceMsgDisplayStreamClip* clip_data = (SpiceMsgDisplayStreamClip*)message->data();
VideoStream* stream;
uint32_t num_clip_rects;
SpiceRect* clip_rects;
if (clip_data->id >= _streams.size() || !(stream = _streams[clip_data->id])) {
THROW("invalid stream");
}
if (message->size() < sizeof(SpiceMsgDisplayStreamClip)) {
THROW("access violation");
}
set_clip_rects(clip_data->clip, num_clip_rects, clip_rects);
Lock lock(_streams_lock);
stream->set_clip(clip_data->clip.type, num_clip_rects, clip_rects);
}
void DisplayChannel::handle_stream_destroy(RedPeer::InMessage* message)
{
SpiceMsgDisplayStreamDestroy* stream_destroy = (SpiceMsgDisplayStreamDestroy*)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 active 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();
}
void DisplayChannel::create_primary_surface(int width, int height, uint32_t format)
{
#ifdef USE_OGL
Canvas *canvas;
#endif
_mark = false;
attach_to_screen(get_client().get_application(), get_id());
clear_area();
AutoRef<CreatePrimarySurfaceEvent> event(new CreatePrimarySurfaceEvent(*this, width, height,
format));
get_client().push_event(*event);
(*event)->wait();
if (!(*event)->success()) {
THROW("Create primary surface failed");
}
_x_res = width;
_y_res = height;
_format = format;
#ifdef USE_OGL
canvas = surfaces_mngr.get_canvas(0);
if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
((GCanvas *)(canvas))->touch_context();
screen()->set_update_interrupt_trigger(&_interrupt_update);
screen()->set_type_gl();
}
#endif
}
void DisplayChannel::create_surface(int surface_id, int width, int height, uint32_t format)
{
AutoRef<CreateSurfaceEvent> event(new CreateSurfaceEvent(*this, surface_id, width, height,
format));
get_client().push_event(*event);
(*event)->wait();
if (!(*event)->success()) {
THROW("Create surface failed");
}
#ifdef USE_OGL
Canvas *canvas;
canvas = surfaces_mngr.get_canvas(surface_id);
if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
((GCanvas *)(canvas))->touch_context();
}
#endif
}
void DisplayChannel::destroy_primary_surface()
{
if (screen()) {
#ifdef USE_OGL
if (surfaces_mngr.is_present_canvas(0)) {
Canvas *canvas;
canvas = surfaces_mngr.get_canvas(0);
if (canvas->get_pixmap_type() == CANVAS_TYPE_GL) {
screen()->unset_type_gl();
screen()->untouch_context();
}
}
#endif
reset_screen();
}
AutoRef<DestroyPrimarySurfaceEvent> event(new DestroyPrimarySurfaceEvent(*this));
get_client().push_event(*event);
(*event)->wait();
if (!(*event)->success()) {
THROW("Destroying primary surface failed");
}
}
void DisplayChannel::destroy_surface(int surface_id)
{
AutoRef<DestroySurfaceEvent> event(new DestroySurfaceEvent(*this, surface_id));
get_client().push_event(*event);
(*event)->wait();
if (!(*event)->success()) {
THROW("Destroying surface failed");
}
}
void DisplayChannel::handle_surface_create(RedPeer::InMessage* message)
{
SpiceMsgSurfaceCreate* surface_create = (SpiceMsgSurfaceCreate*)message->data();
if (surface_create->flags == SPICE_SURFACE_FLAGS_PRIMARY) {
create_primary_surface(surface_create->width, surface_create->height,
surface_create->format);
} else {
create_surface(surface_create->surface_id, surface_create->width, surface_create->height,
surface_create->format);
}
}
void DisplayChannel::handle_surface_destroy(RedPeer::InMessage* message)
{
SpiceMsgSurfaceDestroy* surface_destroy = (SpiceMsgSurfaceDestroy*)message->data();
if (surface_destroy->surface_id == 0) { //fixme
destroy_primary_surface();
} else {
destroy_surface(surface_destroy->surface_id);
}
}
#define PRE_DRAW
#define POST_DRAW
#define DRAW(type) { \
PRE_DRAW; \
canvas->draw_##type(*type, message->size()); \
POST_DRAW; \
if (type->base.surface_id == 0) { \
invalidate(type->base.box, false); \
} \
}
void DisplayChannel::handle_copy_bits(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayCopyBits* copy_bits = (SpiceMsgDisplayCopyBits*)message->data();
PRE_DRAW;
canvas = surfaces_mngr.get_canvas(copy_bits->base.surface_id);
canvas->copy_bits(*copy_bits, message->size());
POST_DRAW;
if (copy_bits->base.surface_id == 0) {
invalidate(copy_bits->base.box, false);
}
}
void DisplayChannel::handle_draw_fill(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawFill* fill = (SpiceMsgDisplayDrawFill*)message->data();
canvas = surfaces_mngr.get_canvas(fill->base.surface_id);
DRAW(fill);
}
void DisplayChannel::handle_draw_opaque(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawOpaque* opaque = (SpiceMsgDisplayDrawOpaque*)message->data();
canvas = surfaces_mngr.get_canvas(opaque->base.surface_id);
DRAW(opaque);
}
void DisplayChannel::handle_draw_copy(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawCopy* copy = (SpiceMsgDisplayDrawCopy*)message->data();
canvas = surfaces_mngr.get_canvas(copy->base.surface_id);
DRAW(copy);
}
void DisplayChannel::handle_draw_blend(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawBlend* blend = (SpiceMsgDisplayDrawBlend*)message->data();
canvas = surfaces_mngr.get_canvas(blend->base.surface_id);
DRAW(blend);
}
void DisplayChannel::handle_draw_blackness(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawBlackness* blackness = (SpiceMsgDisplayDrawBlackness*)message->data();
canvas = surfaces_mngr.get_canvas(blackness->base.surface_id);
DRAW(blackness);
}
void DisplayChannel::handle_draw_whiteness(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawWhiteness* whiteness = (SpiceMsgDisplayDrawWhiteness*)message->data();
canvas = surfaces_mngr.get_canvas(whiteness->base.surface_id);
DRAW(whiteness);
}
void DisplayChannel::handle_draw_invers(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawInvers* invers = (SpiceMsgDisplayDrawInvers*)message->data();
canvas = surfaces_mngr.get_canvas(invers->base.surface_id);
DRAW(invers);
}
void DisplayChannel::handle_draw_rop3(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawRop3* rop3 = (SpiceMsgDisplayDrawRop3*)message->data();
canvas = surfaces_mngr.get_canvas(rop3->base.surface_id);
DRAW(rop3);
}
void DisplayChannel::handle_draw_stroke(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawStroke* stroke = (SpiceMsgDisplayDrawStroke*)message->data();
canvas = surfaces_mngr.get_canvas(stroke->base.surface_id);
DRAW(stroke);
}
void DisplayChannel::handle_draw_text(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawText* text = (SpiceMsgDisplayDrawText*)message->data();
canvas = surfaces_mngr.get_canvas(text->base.surface_id);
DRAW(text);
}
void DisplayChannel::handle_draw_transparent(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawTransparent* transparent = (SpiceMsgDisplayDrawTransparent*)message->data();
canvas = surfaces_mngr.get_canvas(transparent->base.surface_id);
DRAW(transparent);
}
void DisplayChannel::handle_draw_alpha_blend(RedPeer::InMessage* message)
{
Canvas *canvas;
SpiceMsgDisplayDrawAlphaBlend* alpha_blend = (SpiceMsgDisplayDrawAlphaBlend*)message->data();
canvas = surfaces_mngr.get_canvas(alpha_blend->base.surface_id);
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)) {
get_client().activate_interval_timer(*_streams_timer, MAX(int(next_time - mm_time), 0));
_next_timer_time = next_time;
} else if (!_next_timer_time) {
get_client().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();
get_client().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(SPICE_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;
}