mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-26 14:41:25 +00:00
666 lines
19 KiB
C++
666 lines
19 KiB
C++
/*
|
|
Copyright (C) 2009 Red Hat, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of
|
|
the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "cursor_channel.h"
|
|
#include "display_channel.h"
|
|
#include "cursor.h"
|
|
#include "red_client.h"
|
|
#include "application.h"
|
|
#include "debug.h"
|
|
#include "utils.h"
|
|
#include "screen.h"
|
|
#include "red_pixmap_cairo.h"
|
|
#include "rect.h"
|
|
|
|
static inline uint8_t revers_bits(uint8_t byte)
|
|
{
|
|
uint8_t ret = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
int shift = 7 - i * 2;
|
|
ret |= (byte & (1 << i)) << shift;
|
|
ret |= (byte & (0x80 >> i)) >> shift;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
class NaitivCursor: public CursorOpaque {
|
|
public:
|
|
virtual void draw(RedDrawable& dest, int x, int y, const Rect& area) = 0;
|
|
};
|
|
|
|
class AlphaCursor: public NaitivCursor {
|
|
public:
|
|
AlphaCursor(const CursorHeader& header, const uint8_t* data);
|
|
|
|
virtual void draw(RedDrawable& dest, int x, int y, const Rect& area);
|
|
|
|
private:
|
|
std::auto_ptr<RedPixmap> _pixmap;
|
|
};
|
|
|
|
class MonoCursor: public NaitivCursor {
|
|
public:
|
|
MonoCursor(const CursorHeader& header, const uint8_t* data);
|
|
|
|
virtual void draw(RedDrawable& dest, int x, int y, const Rect& area);
|
|
|
|
private:
|
|
std::auto_ptr<RedPixmap> _pixmap;
|
|
int _height;
|
|
};
|
|
|
|
class UnsupportedCursor: public NaitivCursor {
|
|
public:
|
|
UnsupportedCursor(const CursorHeader& header);
|
|
virtual void draw(RedDrawable& dest, int x, int y, const Rect& area);
|
|
|
|
private:
|
|
int _hot_x;
|
|
int _hot_y;
|
|
};
|
|
|
|
UnsupportedCursor::UnsupportedCursor(const CursorHeader& header)
|
|
: _hot_x (header.hot_spot_x)
|
|
, _hot_y (header.hot_spot_y)
|
|
{
|
|
LOG_WARN("Unsupported cursor %hu", header.type);
|
|
}
|
|
|
|
void UnsupportedCursor::draw(RedDrawable& dest, int x, int y, const Rect& area)
|
|
{
|
|
Rect dest_area;
|
|
Rect rect;
|
|
|
|
dest_area.left = area.left;
|
|
dest_area.right = area.right;
|
|
dest_area.top = area.top;
|
|
dest_area.bottom = area.bottom;
|
|
|
|
rect.left = x + _hot_x - 2;
|
|
rect.right = rect.left + 8;
|
|
rect.top = y + _hot_y - 2;
|
|
rect.bottom = rect.top + 8;
|
|
rect_sect(rect, dest_area);
|
|
|
|
dest.fill_rect(rect, rgb32_make(0xf8, 0xf1, 0xb8));
|
|
|
|
rect.left = x + _hot_x - 1;
|
|
rect.right = rect.left + 6;
|
|
rect.top = y + _hot_y - 1;
|
|
rect.bottom = rect.top + 6;
|
|
rect_sect(rect, dest_area);
|
|
|
|
dest.frame_rect(rect, rgb32_make(0, 0, 0));
|
|
}
|
|
|
|
AlphaCursor::AlphaCursor(const CursorHeader& header, const uint8_t* data)
|
|
: _pixmap (new RedPixmapCairo(header.width, header.height,
|
|
RedPixmap::ARGB32, true, NULL, NULL))
|
|
{
|
|
int stride = _pixmap->get_stride();
|
|
uint8_t* dest = _pixmap->get_data();
|
|
int line_size = header.width * sizeof(uint32_t);
|
|
for (int i = 0; i < header.height; i++, data += line_size, dest += stride) {
|
|
memcpy(dest, data, line_size);
|
|
}
|
|
}
|
|
|
|
void AlphaCursor::draw(RedDrawable& dest, int x, int y, const Rect& area)
|
|
{
|
|
dest.blend_pixels(*_pixmap, area.left - x, area.top - y, area);
|
|
}
|
|
|
|
MonoCursor::MonoCursor(const CursorHeader& header, const uint8_t* data)
|
|
: _pixmap (NULL)
|
|
, _height (header.height)
|
|
{
|
|
rgb32_t pallete[2] = { rgb32_make(0x00, 0x00, 0x00), rgb32_make(0xff, 0xff, 0xff)};
|
|
_pixmap.reset(new RedPixmapCairo(header.width, _height * 2, RedPixmap::A1,
|
|
true, pallete, NULL));
|
|
|
|
int dest_stride = _pixmap->get_stride();
|
|
uint8_t *dest_line = _pixmap->get_data();
|
|
int src_stride = ALIGN(header.width, 8) >> 3;
|
|
const uint8_t* src_line = data;
|
|
const uint8_t* end_line = src_line + _pixmap->get_height() * src_stride;
|
|
|
|
if (_pixmap->is_big_endian_bits()) {
|
|
for (; src_line < end_line; src_line += src_stride, dest_line += dest_stride) {
|
|
memcpy(dest_line, src_line, src_stride);
|
|
}
|
|
} else {
|
|
for (; src_line < end_line; src_line += src_stride, dest_line += dest_stride) {
|
|
for (int i = 0; i < src_stride; i++) {
|
|
dest_line[i] = revers_bits(src_line[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MonoCursor::draw(RedDrawable& dest, int x, int y, const Rect& area)
|
|
{
|
|
dest.combine_pixels(*_pixmap, area.left - x, area.top - y, area, RedDrawable::OP_AND);
|
|
dest.combine_pixels(*_pixmap, area.left - x, area.top - y + _height, area, RedDrawable::OP_XOR);
|
|
}
|
|
|
|
class ColorCursor: public NaitivCursor {
|
|
public:
|
|
ColorCursor(const CursorHeader& header);
|
|
|
|
virtual void draw(RedDrawable& dest, int x, int y, const Rect& area);
|
|
|
|
protected:
|
|
void init_pixels(const CursorHeader& header, const uint8_t* _pixels, const uint8_t *and_mask);
|
|
virtual uint32_t get_pixel_color(const uint8_t *data, int row, int col) = 0;
|
|
|
|
private:
|
|
std::auto_ptr<RedPixmap> _pixmap;
|
|
std::auto_ptr<RedPixmap> _invers;
|
|
};
|
|
|
|
ColorCursor::ColorCursor(const CursorHeader& header)
|
|
: _pixmap (new RedPixmapCairo(header.width, header.height,
|
|
RedPixmap::ARGB32, true, NULL, NULL))
|
|
, _invers (NULL)
|
|
{
|
|
rgb32_t pallete[2] = { rgb32_make(0x00, 0x00, 0x00), rgb32_make(0xff, 0xff, 0xff)};
|
|
_invers.reset(new RedPixmapCairo(header.width, header.height, RedPixmap::A1,
|
|
true, pallete, NULL));
|
|
}
|
|
|
|
void ColorCursor::init_pixels(const CursorHeader& header, const uint8_t* pixels,
|
|
const uint8_t *and_mask)
|
|
{
|
|
int mask_stride = ALIGN(header.width, 8) / 8;
|
|
int invers_stride = _invers->get_stride();
|
|
int pixmap_stride = _pixmap->get_stride();
|
|
uint8_t *_pixmap_line = _pixmap->get_data();
|
|
uint8_t* invers_line = _invers->get_data();
|
|
bool be_bits = _invers->is_big_endian_bits();
|
|
memset(invers_line, 0, header.height * invers_stride);
|
|
for (int i = 0; i < header.height; i++, and_mask += mask_stride, invers_line += invers_stride,
|
|
_pixmap_line += pixmap_stride) {
|
|
uint32_t *line_32 = (uint32_t *)_pixmap_line;
|
|
for (int j = 0; j < header.width; j++) {
|
|
uint32_t pixel_val = get_pixel_color(pixels, i, j);
|
|
int and_val = test_bit_be(and_mask, j);
|
|
if ((pixel_val & 0x00ffffff) == 0 && and_val) {
|
|
line_32[j] = 0;
|
|
} else if ((pixel_val & 0x00ffffff) == 0x00ffffff && and_val) {
|
|
line_32[j] = 0;
|
|
if (be_bits) {
|
|
set_bit_be(invers_line, j);
|
|
} else {
|
|
set_bit(invers_line, j);
|
|
}
|
|
} else {
|
|
line_32[j] = pixel_val | 0xff000000;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ColorCursor::draw(RedDrawable& dest, int x, int y, const Rect& area)
|
|
{
|
|
dest.blend_pixels(*_pixmap, area.left - x, area.top - y, area);
|
|
dest.combine_pixels(*_invers, area.left - x, area.top - y, area, RedDrawable::OP_XOR);
|
|
}
|
|
|
|
class ColorCursor32: public ColorCursor {
|
|
public:
|
|
ColorCursor32(const CursorHeader& header, const uint8_t* data)
|
|
: ColorCursor(header)
|
|
, _src_stride (header.width * sizeof(uint32_t))
|
|
{
|
|
init_pixels(header, data, data + _src_stride * header.height);
|
|
}
|
|
|
|
private:
|
|
uint32_t get_pixel_color(const uint8_t *data, int row, int col)
|
|
{
|
|
return *((uint32_t *)(data + row * _src_stride) + col);
|
|
}
|
|
|
|
private:
|
|
int _src_stride;
|
|
};
|
|
|
|
class ColorCursor16: public ColorCursor {
|
|
public:
|
|
ColorCursor16(const CursorHeader& header, const uint8_t* data)
|
|
: ColorCursor(header)
|
|
, _src_stride (header.width * sizeof(uint16_t))
|
|
{
|
|
init_pixels(header, data, data + _src_stride * header.height);
|
|
}
|
|
|
|
private:
|
|
uint32_t get_pixel_color(const uint8_t *data, int row, int col)
|
|
{
|
|
uint32_t pix = *((uint16_t*)(data + row * _src_stride) + col);
|
|
return ((pix & 0x1f) << 3) | ((pix & 0x3e0) << 6) | ((pix & 0x7c00) << 9);
|
|
}
|
|
|
|
private:
|
|
int _src_stride;
|
|
};
|
|
|
|
class ColorCursor4: public ColorCursor {
|
|
public:
|
|
ColorCursor4(const CursorHeader& header, const uint8_t* data)
|
|
: ColorCursor(header)
|
|
, _src_stride (ALIGN(header.width, 2) >> 1)
|
|
, _palette ((uint32_t*)(data + _src_stride * header.height))
|
|
{
|
|
init_pixels(header, data, (uint8_t*)(_palette + 16));
|
|
}
|
|
|
|
private:
|
|
uint32_t get_pixel_color(const uint8_t *data, int row, int col)
|
|
{
|
|
data += _src_stride * row + (col >> 1);
|
|
return (col & 1) ? _palette[*data & 0x0f] : _palette[*data >> 4];
|
|
}
|
|
|
|
private:
|
|
int _src_stride;
|
|
uint32_t* _palette;
|
|
};
|
|
|
|
class AttachDispayEvent: public Event {
|
|
public:
|
|
AttachDispayEvent(CursorChannel& channel)
|
|
: _channel (channel)
|
|
{
|
|
}
|
|
|
|
class UpdateDisplayChannel: public ForEachChannelFunc {
|
|
public:
|
|
UpdateDisplayChannel(CursorChannel& channel)
|
|
: _channel (channel)
|
|
{
|
|
}
|
|
|
|
virtual bool operator() (RedChannel& channel)
|
|
{
|
|
if (channel.get_type() != RED_CHANNEL_DISPLAY ||
|
|
channel.get_id() != _channel.get_id()) {
|
|
return true;
|
|
}
|
|
|
|
_channel.attach_display(&static_cast<DisplayChannel&>(channel));
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
CursorChannel& _channel;
|
|
};
|
|
|
|
virtual void response(AbstractProcessLoop& events_loop)
|
|
{
|
|
UpdateDisplayChannel func(_channel);
|
|
_channel.get_client().for_each_channel(func);
|
|
}
|
|
|
|
private:
|
|
CursorChannel& _channel;
|
|
};
|
|
|
|
class CursorUpdateEvent: public Event {
|
|
public:
|
|
CursorUpdateEvent(CursorChannel& channel)
|
|
: _channel (channel)
|
|
{
|
|
}
|
|
|
|
virtual void response(AbstractProcessLoop& events_loop)
|
|
{
|
|
DisplayChannel* display_channel = _channel._display_channel;
|
|
if (!display_channel) {
|
|
return;
|
|
}
|
|
|
|
Lock lock(_channel._update_lock);
|
|
if (_channel._cursor_visible) {
|
|
display_channel->set_cursor(_channel._cursor);
|
|
return;
|
|
}
|
|
|
|
display_channel->hide_cursor();
|
|
}
|
|
|
|
private:
|
|
CursorChannel& _channel;
|
|
};
|
|
|
|
class CursorHandler: public MessageHandlerImp<CursorChannel, RED_CURSOR_MESSAGES_END> {
|
|
public:
|
|
CursorHandler(CursorChannel& channel)
|
|
: MessageHandlerImp<CursorChannel, RED_CURSOR_MESSAGES_END>(channel) {}
|
|
};
|
|
|
|
CursorChannel::CursorChannel(RedClient& client, uint32_t id)
|
|
: RedChannel(client, RED_CHANNEL_CURSOR, id, new CursorHandler(*this))
|
|
, ScreenLayer(SCREEN_LAYER_CURSOR, false)
|
|
, _cursor (NULL)
|
|
, _cursor_visible (false)
|
|
, _display_channel (NULL)
|
|
{
|
|
CursorHandler* handler = static_cast<CursorHandler*>(get_message_handler());
|
|
|
|
handler->set_handler(RED_MIGRATE, &CursorChannel::handle_migrate, 0);
|
|
handler->set_handler(RED_SET_ACK, &CursorChannel::handle_set_ack, sizeof(RedSetAck));
|
|
handler->set_handler(RED_PING, &CursorChannel::handle_ping, sizeof(RedPing));
|
|
handler->set_handler(RED_WAIT_FOR_CHANNELS, &CursorChannel::handle_wait_for_channels,
|
|
sizeof(RedWaitForChannels));
|
|
handler->set_handler(RED_DISCONNECTING, &CursorChannel::handle_disconnect,
|
|
sizeof(RedDisconnect));
|
|
handler->set_handler(RED_NOTIFY, &CursorChannel::handle_notify, sizeof(RedNotify));
|
|
|
|
handler->set_handler(RED_CURSOR_INIT, &CursorChannel::handle_init, sizeof(RedCursorInit));
|
|
handler->set_handler(RED_CURSOR_RESET, &CursorChannel::handle_reset, 0);
|
|
handler->set_handler(RED_CURSOR_SET, &CursorChannel::handle_cursor_set,
|
|
sizeof(RedCursorSet));
|
|
handler->set_handler(RED_CURSOR_MOVE, &CursorChannel::handle_cursor_move,
|
|
sizeof(RedCursorMove));
|
|
handler->set_handler(RED_CURSOR_HIDE, &CursorChannel::handle_cursor_hide, 0);
|
|
handler->set_handler(RED_CURSOR_TRAIL, &CursorChannel::handle_cursor_trail,
|
|
sizeof(RedCursorTrail));
|
|
handler->set_handler(RED_CURSOR_INVAL_ONE, &CursorChannel::handle_inval_one,
|
|
sizeof(RedInvalOne));
|
|
handler->set_handler(RED_CURSOR_INVAL_ALL, &CursorChannel::handle_inval_all, 0);
|
|
}
|
|
|
|
CursorChannel::~CursorChannel()
|
|
{
|
|
ASSERT(!_cursor);
|
|
}
|
|
|
|
void CursorChannel::on_connect()
|
|
{
|
|
AutoRef<AttachDispayEvent> attach_event(new AttachDispayEvent(*this));
|
|
get_client().push_event(*attach_event);
|
|
}
|
|
|
|
void CursorChannel::on_disconnect()
|
|
{
|
|
remove_cursor();
|
|
_cursor_cache.clear();
|
|
AutoRef<SyncEvent> sync_event(new SyncEvent());
|
|
get_client().push_event(*sync_event);
|
|
(*sync_event)->wait();
|
|
detach_from_screen(get_client().get_application());
|
|
}
|
|
|
|
void CursorChannel::update_display_cursor()
|
|
{
|
|
if (!_display_channel) {
|
|
return;
|
|
}
|
|
|
|
AutoRef<CursorUpdateEvent> update_event(new CursorUpdateEvent(*this));
|
|
get_client().push_event(*update_event);
|
|
}
|
|
|
|
void CursorChannel::remove_cursor()
|
|
{
|
|
Lock lock(_update_lock);
|
|
_cursor_visible = false;
|
|
if (_cursor) {
|
|
_cursor->unref();
|
|
_cursor = NULL;
|
|
}
|
|
lock.unlock();
|
|
clear_area();
|
|
update_display_cursor();
|
|
}
|
|
|
|
void CursorChannel::copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc)
|
|
{
|
|
Lock lock(_update_lock);
|
|
|
|
if (!_cursor_visible) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < (int)dest_region.num_rects; i++) {
|
|
ASSERT(_cursor && _cursor->get_opaque());
|
|
((NaitivCursor*)_cursor->get_opaque())->draw(dest_dc, _cursor_rect.left, _cursor_rect.top,
|
|
dest_region.rects[i]);
|
|
}
|
|
}
|
|
|
|
void CursorChannel::create_native_cursor(CursorData* cursor)
|
|
{
|
|
CursorOpaque* native_cursor = cursor->get_opaque();
|
|
|
|
if (native_cursor) {
|
|
return;
|
|
}
|
|
|
|
switch (cursor->header().type) {
|
|
case CURSOR_TYPE_ALPHA:
|
|
native_cursor = new AlphaCursor(cursor->header(), cursor->data());
|
|
break;
|
|
case CURSOR_TYPE_COLOR32:
|
|
native_cursor = new ColorCursor32(cursor->header(), cursor->data());
|
|
break;
|
|
case CURSOR_TYPE_MONO:
|
|
native_cursor = new MonoCursor(cursor->header(), cursor->data());
|
|
break;
|
|
case CURSOR_TYPE_COLOR4:
|
|
native_cursor = new ColorCursor4(cursor->header(), cursor->data());
|
|
break;
|
|
case CURSOR_TYPE_COLOR8:
|
|
native_cursor = new UnsupportedCursor(cursor->header());
|
|
break;
|
|
case CURSOR_TYPE_COLOR16:
|
|
native_cursor = new ColorCursor16(cursor->header(), cursor->data());
|
|
break;
|
|
case CURSOR_TYPE_COLOR24:
|
|
native_cursor = new UnsupportedCursor(cursor->header());
|
|
break;
|
|
default:
|
|
THROW("invalid curosr type");
|
|
}
|
|
cursor->set_opaque(native_cursor);
|
|
}
|
|
|
|
void CursorChannel::set_cursor(RedCursor& red_cursor, int data_size, int x, int y, bool visible)
|
|
{
|
|
CursorData *cursor;
|
|
|
|
if (red_cursor.flags & RED_CURSOR_NONE) {
|
|
remove_cursor();
|
|
return;
|
|
}
|
|
|
|
if (red_cursor.flags & RED_CURSOR_FROM_CACHE) {
|
|
cursor = _cursor_cache.get(red_cursor.header.unique);
|
|
} else {
|
|
cursor = new CursorData(red_cursor, data_size);
|
|
if (red_cursor.flags & RED_CURSOR_CACHE_ME) {
|
|
ASSERT(red_cursor.header.unique);
|
|
_cursor_cache.add(red_cursor.header.unique, cursor);
|
|
}
|
|
}
|
|
|
|
AutoRef<CursorData> cursor_ref(cursor);
|
|
create_native_cursor(cursor);
|
|
|
|
Lock lock(_update_lock);
|
|
_hot_pos.x = x;
|
|
_hot_pos.y = y;
|
|
_cursor_visible = visible;
|
|
_cursor_rect.left = x - cursor->header().hot_spot_x;
|
|
_cursor_rect.right = _cursor_rect.left + cursor->header().width;
|
|
_cursor_rect.top = y - cursor->header().hot_spot_y;
|
|
_cursor_rect.bottom = _cursor_rect.top + cursor->header().height;
|
|
|
|
if (_cursor) {
|
|
_cursor->unref();
|
|
}
|
|
_cursor = cursor->ref();
|
|
lock.unlock();
|
|
|
|
update_display_cursor();
|
|
|
|
if (get_client().get_mouse_mode() == RED_MOUSE_MODE_SERVER) {
|
|
if (_cursor_visible) {
|
|
set_rect_area(_cursor_rect);
|
|
} else {
|
|
clear_area();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CursorChannel::attach_display(DisplayChannel* channel)
|
|
{
|
|
if (_display_channel) {
|
|
return;
|
|
}
|
|
|
|
_display_channel = channel;
|
|
|
|
Lock lock(_update_lock);
|
|
if (!_cursor_visible) {
|
|
return;
|
|
}
|
|
|
|
_display_channel->set_cursor(_cursor);
|
|
}
|
|
|
|
void CursorChannel::detach_display()
|
|
{
|
|
_display_channel = NULL;
|
|
}
|
|
|
|
void CursorChannel::handle_init(RedPeer::InMessage *message)
|
|
{
|
|
RedCursorInit *init = (RedCursorInit*)message->data();
|
|
attach_to_screen(get_client().get_application(), get_id());
|
|
remove_cursor();
|
|
_cursor_cache.clear();
|
|
set_cursor(init->cursor, message->size() - sizeof(RedCursorInit), init->position.x,
|
|
init->position.y, init->visible != 0);
|
|
}
|
|
|
|
void CursorChannel::handle_reset(RedPeer::InMessage *message)
|
|
{
|
|
remove_cursor();
|
|
detach_from_screen(get_client().get_application());
|
|
_cursor_cache.clear();
|
|
}
|
|
|
|
void CursorChannel::handle_cursor_set(RedPeer::InMessage* message)
|
|
{
|
|
RedCursorSet* set = (RedCursorSet*)message->data();
|
|
set_cursor(set->cursor, message->size() - sizeof(RedCursorSet), set->postition.x,
|
|
set->postition.y, set->visible != 0);
|
|
}
|
|
|
|
void CursorChannel::handle_cursor_move(RedPeer::InMessage* message)
|
|
{
|
|
RedCursorMove* move = (RedCursorMove*)message->data();
|
|
|
|
if (!_cursor) {
|
|
return;
|
|
}
|
|
|
|
Lock lock(_update_lock);
|
|
_cursor_visible = true;
|
|
int dx = move->postition.x - _hot_pos.x;
|
|
int dy = move->postition.y - _hot_pos.y;
|
|
_hot_pos.x += dx;
|
|
_hot_pos.y += dy;
|
|
_cursor_rect.left += dx;
|
|
_cursor_rect.right += dx;
|
|
_cursor_rect.top += dy;
|
|
_cursor_rect.bottom += dy;
|
|
lock.unlock();
|
|
|
|
if (get_client().get_mouse_mode() == RED_MOUSE_MODE_SERVER) {
|
|
set_rect_area(_cursor_rect);
|
|
return;
|
|
}
|
|
|
|
update_display_cursor();
|
|
}
|
|
|
|
void CursorChannel::handle_cursor_hide(RedPeer::InMessage* message)
|
|
{
|
|
Lock lock(_update_lock);
|
|
|
|
_cursor_visible = false;
|
|
update_display_cursor();
|
|
|
|
if (get_client().get_mouse_mode() == RED_MOUSE_MODE_SERVER) {
|
|
clear_area();
|
|
}
|
|
}
|
|
|
|
void CursorChannel::handle_cursor_trail(RedPeer::InMessage* message)
|
|
{
|
|
RedCursorTrail* trail = (RedCursorTrail*)message->data();
|
|
DBG(0, "length %u frequency %u", trail->length, trail->frequency)
|
|
}
|
|
|
|
void CursorChannel::handle_inval_one(RedPeer::InMessage* message)
|
|
{
|
|
RedInvalOne* inval = (RedInvalOne*)message->data();
|
|
_cursor_cache.remove(inval->id);
|
|
}
|
|
|
|
void CursorChannel::handle_inval_all(RedPeer::InMessage* message)
|
|
{
|
|
_cursor_cache.clear();
|
|
}
|
|
|
|
void CursorChannel::on_mouse_mode_change()
|
|
{
|
|
Lock lock(_update_lock);
|
|
|
|
if (get_client().get_mouse_mode() == RED_MOUSE_MODE_CLIENT) {
|
|
clear_area();
|
|
return;
|
|
}
|
|
|
|
if (_cursor_visible) {
|
|
set_rect_area(_cursor_rect);
|
|
}
|
|
}
|
|
|
|
class CursorFactory: public ChannelFactory {
|
|
public:
|
|
CursorFactory() : ChannelFactory(RED_CHANNEL_CURSOR) {}
|
|
virtual RedChannel* construct(RedClient& client, uint32_t id)
|
|
{
|
|
return new CursorChannel(client, id);
|
|
}
|
|
};
|
|
|
|
static CursorFactory factory;
|
|
|
|
ChannelFactory& CursorChannel::Factory()
|
|
{
|
|
return factory;
|
|
}
|
|
|