mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-26 14:41:25 +00:00
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
1748 lines
50 KiB
C++
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;
|
|
}
|
|
|