@@ -43,8 +43,8 @@ static LogWriter vlog("CConnection"); | |||
CConnection::CConnection() | |||
: csecurity(0), | |||
supportsLocalCursor(false), supportsDesktopResize(false), | |||
supportsLEDState(false), | |||
supportsLocalCursor(false), supportsCursorPosition(false), | |||
supportsDesktopResize(false), supportsLEDState(false), | |||
is(0), os(0), reader_(0), writer_(0), | |||
shared(false), | |||
state_(RFBSTATE_UNINITIALISED), | |||
@@ -805,6 +805,9 @@ void CConnection::updateEncodings() | |||
encodings.push_back(pseudoEncodingCursor); | |||
encodings.push_back(pseudoEncodingXCursor); | |||
} | |||
if (supportsCursorPosition) { | |||
encodings.push_back(pseudoEncodingVMwareCursorPosition); | |||
} | |||
if (supportsDesktopResize) { | |||
encodings.push_back(pseudoEncodingDesktopSize); | |||
encodings.push_back(pseudoEncodingExtendedDesktopSize); |
@@ -239,6 +239,7 @@ namespace rfb { | |||
// Optional capabilities that a subclass is expected to set to true | |||
// if supported | |||
bool supportsLocalCursor; | |||
bool supportsCursorPosition; | |||
bool supportsDesktopResize; | |||
bool supportsLEDState; | |||
@@ -52,6 +52,7 @@ namespace rfb { | |||
const ScreenSet& layout); | |||
virtual void setCursor(int width, int height, const Point& hotspot, | |||
const rdr::U8* data) = 0; | |||
virtual void setCursorPos(const Point& pos) = 0; | |||
virtual void setPixelFormat(const PixelFormat& pf); | |||
virtual void setName(const char* name); | |||
virtual void fence(rdr::U32 flags, unsigned len, const char data[]); |
@@ -165,6 +165,10 @@ bool CMsgReader::readMsg() | |||
case pseudoEncodingVMwareCursor: | |||
ret = readSetVMwareCursor(dataRect.width(), dataRect.height(), dataRect.tl); | |||
break; | |||
case pseudoEncodingVMwareCursorPosition: | |||
handler->setCursorPos(dataRect.tl); | |||
ret = true; | |||
break; | |||
case pseudoEncodingDesktopName: | |||
ret = readSetDesktopName(dataRect.tl.x, dataRect.tl.y, | |||
dataRect.width(), dataRect.height()); |
@@ -30,7 +30,7 @@ ClientParams::ClientParams() | |||
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1), | |||
subsampling(subsampleUndefined), | |||
width_(0), height_(0), name_(0), | |||
ledState_(ledUnknown) | |||
cursorPos_(0, 0), ledState_(ledUnknown) | |||
{ | |||
setName(""); | |||
@@ -85,6 +85,11 @@ void ClientParams::setCursor(const Cursor& other) | |||
cursor_ = new Cursor(other); | |||
} | |||
void ClientParams::setCursorPos(const Point& pos) | |||
{ | |||
cursorPos_ = pos; | |||
} | |||
bool ClientParams::supportsEncoding(rdr::S32 encoding) const | |||
{ | |||
return encodings_.count(encoding) != 0; | |||
@@ -182,6 +187,13 @@ bool ClientParams::supportsLocalCursor() const | |||
return false; | |||
} | |||
bool ClientParams::supportsCursorPosition() const | |||
{ | |||
if (supportsEncoding(pseudoEncodingVMwareCursorPosition)) | |||
return true; | |||
return false; | |||
} | |||
bool ClientParams::supportsDesktopSize() const | |||
{ | |||
if (supportsEncoding(pseudoEncodingExtendedDesktopSize)) |
@@ -77,6 +77,9 @@ namespace rfb { | |||
const Cursor& cursor() const { return *cursor_; } | |||
void setCursor(const Cursor& cursor); | |||
const Point& cursorPos() const { return cursorPos_; } | |||
void setCursorPos(const Point& pos); | |||
bool supportsEncoding(rdr::S32 encoding) const; | |||
void setEncodings(int nEncodings, const rdr::S32* encodings); | |||
@@ -91,6 +94,7 @@ namespace rfb { | |||
// Wrappers to check for functionality rather than specific | |||
// encodings | |||
bool supportsLocalCursor() const; | |||
bool supportsCursorPosition() const; | |||
bool supportsDesktopSize() const; | |||
bool supportsLEDState() const; | |||
bool supportsFence() const; | |||
@@ -110,6 +114,7 @@ namespace rfb { | |||
PixelFormat pf_; | |||
char* name_; | |||
Cursor* cursor_; | |||
Point cursorPos_; | |||
std::set<rdr::S32> encodings_; | |||
unsigned int ledState_; | |||
rdr::U32 clipFlags; |
@@ -42,7 +42,8 @@ SMsgWriter::SMsgWriter(ClientParams* client_, rdr::OutStream* os_) | |||
: client(client_), os(os_), | |||
nRectsInUpdate(0), nRectsInHeader(0), | |||
needSetDesktopName(false), needCursor(false), | |||
needLEDState(false), needQEMUKeyEvent(false) | |||
needCursorPos(false), needLEDState(false), | |||
needQEMUKeyEvent(false) | |||
{ | |||
} | |||
@@ -269,6 +270,14 @@ void SMsgWriter::writeCursor() | |||
needCursor = true; | |||
} | |||
void SMsgWriter::writeCursorPos() | |||
{ | |||
if (!client->supportsEncoding(pseudoEncodingVMwareCursorPosition)) | |||
throw Exception("Client does not support cursor position"); | |||
needCursorPos = true; | |||
} | |||
void SMsgWriter::writeLEDState() | |||
{ | |||
if (!client->supportsEncoding(pseudoEncodingLEDState) && | |||
@@ -294,6 +303,8 @@ bool SMsgWriter::needFakeUpdate() | |||
return true; | |||
if (needCursor) | |||
return true; | |||
if (needCursorPos) | |||
return true; | |||
if (needLEDState) | |||
return true; | |||
if (needQEMUKeyEvent) | |||
@@ -340,6 +351,8 @@ void SMsgWriter::writeFramebufferUpdateStart(int nRects) | |||
nRects++; | |||
if (needCursor) | |||
nRects++; | |||
if (needCursorPos) | |||
nRects++; | |||
if (needLEDState) | |||
nRects++; | |||
if (needQEMUKeyEvent) | |||
@@ -455,6 +468,18 @@ void SMsgWriter::writePseudoRects() | |||
needCursor = false; | |||
} | |||
if (needCursorPos) { | |||
const Point& cursorPos = client->cursorPos(); | |||
if (client->supportsEncoding(pseudoEncodingVMwareCursorPosition)) { | |||
writeSetVMwareCursorPositionRect(cursorPos.x, cursorPos.y); | |||
} else { | |||
throw Exception("Client does not support cursor position"); | |||
} | |||
needCursorPos = false; | |||
} | |||
if (needSetDesktopName) { | |||
writeSetDesktopNameRect(client->name()); | |||
needSetDesktopName = false; | |||
@@ -650,6 +675,20 @@ void SMsgWriter::writeSetVMwareCursorRect(int width, int height, | |||
os->writeBytes(data, width*height*4); | |||
} | |||
void SMsgWriter::writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY) | |||
{ | |||
if (!client->supportsEncoding(pseudoEncodingVMwareCursorPosition)) | |||
throw Exception("Client does not support cursor position"); | |||
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) | |||
throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync"); | |||
os->writeS16(hotspotX); | |||
os->writeS16(hotspotY); | |||
os->writeU16(0); | |||
os->writeU16(0); | |||
os->writeU32(pseudoEncodingVMwareCursorPosition); | |||
} | |||
void SMsgWriter::writeLEDStateRect(rdr::U8 state) | |||
{ | |||
if (!client->supportsEncoding(pseudoEncodingLEDState) && |
@@ -83,6 +83,9 @@ namespace rfb { | |||
// immediately. | |||
void writeCursor(); | |||
// Notifies the client that the cursor pointer was moved by the server. | |||
void writeCursorPos(); | |||
// Same for LED state message | |||
void writeLEDState(); | |||
@@ -141,6 +144,7 @@ namespace rfb { | |||
void writeSetVMwareCursorRect(int width, int height, | |||
int hotspotX, int hotspotY, | |||
const rdr::U8* data); | |||
void writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY); | |||
void writeLEDStateRect(rdr::U8 state); | |||
void writeQEMUKeyEventRect(); | |||
@@ -152,6 +156,7 @@ namespace rfb { | |||
bool needSetDesktopName; | |||
bool needCursor; | |||
bool needCursorPos; | |||
bool needLEDState; | |||
bool needQEMUKeyEvent; | |||
@@ -370,6 +370,15 @@ void VNCSConnectionST::renderedCursorChange() | |||
} | |||
} | |||
// cursorPositionChange() is called whenever the cursor has changed position by | |||
// the server. If the client supports being informed about these changes then | |||
// it will arrange for the new cursor position to be sent to the client. | |||
void VNCSConnectionST::cursorPositionChange() | |||
{ | |||
setCursorPos(); | |||
} | |||
// needRenderedCursor() returns true if this client needs the server-side | |||
// rendered cursor. This may be because it does not support local cursor or | |||
// because the current cursor position has not been set by this client. | |||
@@ -1123,6 +1132,21 @@ void VNCSConnectionST::setCursor() | |||
writer()->writeCursor(); | |||
} | |||
// setCursorPos() is called whenever the cursor has changed position by the | |||
// server. If the client supports being informed about these changes then it | |||
// will arrange for the new cursor position to be sent to the client. | |||
void VNCSConnectionST::setCursorPos() | |||
{ | |||
if (state() != RFBSTATE_NORMAL) | |||
return; | |||
if (client.supportsCursorPosition()) { | |||
client.setCursorPos(server->getCursorPos()); | |||
writer()->writeCursorPos(); | |||
} | |||
} | |||
void VNCSConnectionST::setDesktopName(const char *name) | |||
{ | |||
client.setName(name); |
@@ -93,6 +93,11 @@ namespace rfb { | |||
// cursor. | |||
void renderedCursorChange(); | |||
// cursorPositionChange() is called whenever the cursor has changed position by | |||
// the server. If the client supports being informed about these changes then | |||
// it will arrange for the new cursor position to be sent to the client. | |||
void cursorPositionChange(); | |||
// needRenderedCursor() returns true if this client needs the server-side | |||
// rendered cursor. This may be because it does not support local cursor | |||
// or because the current cursor position has not been set by this client. | |||
@@ -155,6 +160,7 @@ namespace rfb { | |||
void screenLayoutChange(rdr::U16 reason); | |||
void setCursor(); | |||
void setCursorPos(); | |||
void setDesktopName(const char *name); | |||
void setLEDState(unsigned int state); | |||
@@ -97,8 +97,10 @@ namespace rfb { | |||
virtual void setCursor(int width, int height, const Point& hotspot, | |||
const rdr::U8* cursorData) = 0; | |||
// setCursorPos() tells the server the current position of the cursor. | |||
virtual void setCursorPos(const Point& p) = 0; | |||
// setCursorPos() tells the server the current position of the cursor, and | |||
// whether the server initiated that change (e.g. through another X11 | |||
// client calling XWarpPointer()). | |||
virtual void setCursorPos(const Point& p, bool warped) = 0; | |||
// setName() tells the server what desktop title to supply to clients | |||
virtual void setName(const char* name) = 0; |
@@ -429,14 +429,17 @@ void VNCServerST::setCursor(int width, int height, const Point& newHotspot, | |||
} | |||
} | |||
void VNCServerST::setCursorPos(const Point& pos) | |||
void VNCServerST::setCursorPos(const Point& pos, bool warped) | |||
{ | |||
if (!cursorPos.equals(pos)) { | |||
cursorPos = pos; | |||
renderedCursorInvalid = true; | |||
std::list<VNCSConnectionST*>::iterator ci; | |||
for (ci = clients.begin(); ci != clients.end(); ci++) | |||
for (ci = clients.begin(); ci != clients.end(); ci++) { | |||
(*ci)->renderedCursorChange(); | |||
if (warped) | |||
(*ci)->cursorPositionChange(); | |||
} | |||
} | |||
} | |||
@@ -99,7 +99,7 @@ namespace rfb { | |||
virtual void add_copied(const Region &dest, const Point &delta); | |||
virtual void setCursor(int width, int height, const Point& hotspot, | |||
const rdr::U8* data); | |||
virtual void setCursorPos(const Point& p); | |||
virtual void setCursorPos(const Point& p, bool warped); | |||
virtual void setName(const char* name_); | |||
virtual void setLEDState(unsigned state); | |||
@@ -61,6 +61,7 @@ namespace rfb { | |||
// VMware-specific | |||
const int pseudoEncodingVMwareCursor = 0x574d5664; | |||
const int pseudoEncodingVMwareCursorPosition = 0x574d5666; | |||
const int pseudoEncodingVMwareLEDState = 0x574d5668; | |||
// UltraVNC-specific |
@@ -66,6 +66,7 @@ public: | |||
virtual void initDone(); | |||
virtual void setPixelFormat(const rfb::PixelFormat& pf); | |||
virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*); | |||
virtual void setCursorPos(const rfb::Point&); | |||
virtual void framebufferUpdateStart(); | |||
virtual void framebufferUpdateEnd(); | |||
virtual void setColourMapEntries(int, int, rdr::U16*); | |||
@@ -144,6 +145,10 @@ void CConn::setCursor(int, int, const rfb::Point&, const rdr::U8*) | |||
{ | |||
} | |||
void CConn::setCursorPos(const rfb::Point&) | |||
{ | |||
} | |||
void CConn::framebufferUpdateStart() | |||
{ | |||
CConnection::framebufferUpdateStart(); |
@@ -95,6 +95,7 @@ public: | |||
virtual void initDone() {}; | |||
virtual void resizeFramebuffer(); | |||
virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*); | |||
virtual void setCursorPos(const rfb::Point&); | |||
virtual void framebufferUpdateStart(); | |||
virtual void framebufferUpdateEnd(); | |||
virtual bool dataRect(const rfb::Rect&, int); | |||
@@ -216,6 +217,10 @@ void CConn::setCursor(int, int, const rfb::Point&, const rdr::U8*) | |||
{ | |||
} | |||
void CConn::setCursorPos(const rfb::Point&) | |||
{ | |||
} | |||
void CConn::framebufferUpdateStart() | |||
{ | |||
CConnection::framebufferUpdateStart(); |
@@ -217,7 +217,7 @@ void XDesktop::poll() { | |||
&x, &y, &wx, &wy, &mask); | |||
x -= geometry->offsetLeft(); | |||
y -= geometry->offsetTop(); | |||
server->setCursorPos(rfb::Point(x, y)); | |||
server->setCursorPos(rfb::Point(x, y), false); | |||
} | |||
} | |||
@@ -261,6 +261,15 @@ void XserverDesktop::setCursor(int width, int height, int hotX, int hotY, | |||
delete [] cursorData; | |||
} | |||
void XserverDesktop::setCursorPos(int x, int y, bool warped) | |||
{ | |||
try { | |||
server->setCursorPos(Point(x, y), warped); | |||
} catch (rdr::Exception& e) { | |||
vlog.error("XserverDesktop::setCursorPos: %s",e.str()); | |||
} | |||
} | |||
void XserverDesktop::add_changed(const rfb::Region ®ion) | |||
{ | |||
try { | |||
@@ -377,7 +386,7 @@ void XserverDesktop::blockHandler(int* timeout) | |||
if (oldCursorPos.x != cursorX || oldCursorPos.y != cursorY) { | |||
oldCursorPos.x = cursorX; | |||
oldCursorPos.y = cursorY; | |||
server->setCursorPos(oldCursorPos); | |||
server->setCursorPos(oldCursorPos, false); | |||
} | |||
// Trigger timers and check when the next will expire |
@@ -67,6 +67,7 @@ public: | |||
void setDesktopName(const char* name); | |||
void setCursor(int width, int height, int hotX, int hotY, | |||
const unsigned char *rgbaData); | |||
void setCursorPos(int x, int y, bool warped); | |||
void add_changed(const rfb::Region ®ion); | |||
void add_copied(const rfb::Region &dest, const rfb::Point &delta); | |||
void handleSocketEvent(int fd, bool read, bool write); |
@@ -400,6 +400,11 @@ void vncSetCursor(int width, int height, int hotX, int hotY, | |||
desktop[scr]->setCursor(width, height, hotX, hotY, rgbaData); | |||
} | |||
void vncSetCursorPos(int scrIdx, int x, int y) | |||
{ | |||
desktop[scrIdx]->setCursorPos(x, y, true); | |||
} | |||
void vncPreScreenResize(int scrIdx) | |||
{ | |||
// We need to prevent the RFB core from accessing the framebuffer |
@@ -81,6 +81,7 @@ void vncAddCopied(int scrIdx, int nRects, | |||
void vncSetCursor(int width, int height, int hotX, int hotY, | |||
const unsigned char *rgbaData); | |||
void vncSetCursorPos(int scrIdx, int x, int y); | |||
void vncPreScreenResize(int scrIdx); | |||
void vncPostScreenResize(int scrIdx, int success, int width, int height); |
@@ -62,6 +62,9 @@ typedef struct _vncHooksScreenRec { | |||
CopyWindowProcPtr CopyWindow; | |||
ClearToBackgroundProcPtr ClearToBackground; | |||
DisplayCursorProcPtr DisplayCursor; | |||
#if XORG >= 119 | |||
CursorWarpedToProcPtr CursorWarpedTo; | |||
#endif | |||
ScreenBlockHandlerProcPtr BlockHandler; | |||
#ifdef RENDER | |||
CompositeProcPtr Composite; | |||
@@ -113,6 +116,12 @@ static void vncHooksClearToBackground(WindowPtr pWin, int x, int y, int w, | |||
int h, Bool generateExposures); | |||
static Bool vncHooksDisplayCursor(DeviceIntPtr pDev, | |||
ScreenPtr pScreen, CursorPtr cursor); | |||
#if XORG >= 119 | |||
static void vncHooksCursorWarpedTo(DeviceIntPtr pDev, | |||
ScreenPtr pScreen_, ClientPtr pClient, | |||
WindowPtr pWindow, SpritePtr pSprite, | |||
int x, int y); | |||
#endif | |||
#if XORG <= 118 | |||
static void vncHooksBlockHandler(ScreenPtr pScreen, void * pTimeout, | |||
void * pReadmask); | |||
@@ -271,6 +280,9 @@ int vncHooksInit(int scrIdx) | |||
wrap(vncHooksScreen, pScreen, CopyWindow, vncHooksCopyWindow); | |||
wrap(vncHooksScreen, pScreen, ClearToBackground, vncHooksClearToBackground); | |||
wrap(vncHooksScreen, pScreen, DisplayCursor, vncHooksDisplayCursor); | |||
#if XORG >= 119 | |||
wrap(vncHooksScreen, pScreen, CursorWarpedTo, vncHooksCursorWarpedTo); | |||
#endif | |||
wrap(vncHooksScreen, pScreen, BlockHandler, vncHooksBlockHandler); | |||
#ifdef RENDER | |||
ps = GetPictureScreenIfSet(pScreen); | |||
@@ -631,6 +643,20 @@ out: | |||
return ret; | |||
} | |||
// CursorWarpedTo - notify that the cursor was warped | |||
#if XORG >= 119 | |||
static void vncHooksCursorWarpedTo(DeviceIntPtr pDev, | |||
ScreenPtr pScreen_, ClientPtr pClient, | |||
WindowPtr pWindow, SpritePtr pSprite, | |||
int x, int y) | |||
{ | |||
SCREEN_PROLOGUE(pScreen_, CursorWarpedTo); | |||
vncSetCursorPos(pScreen->myNum, x, y); | |||
SCREEN_EPILOGUE(CursorWarpedTo); | |||
} | |||
#endif | |||
// BlockHandler - ignore any changes during the block handler - it's likely | |||
// these are just drawing the cursor. | |||
@@ -84,6 +84,7 @@ CConn::CConn(const char* vncServerName, network::Socket* socket=NULL) | |||
sock = socket; | |||
supportsLocalCursor = true; | |||
supportsCursorPosition = true; | |||
supportsDesktopResize = true; | |||
supportsLEDState = false; | |||
@@ -430,6 +431,11 @@ void CConn::setCursor(int width, int height, const Point& hotspot, | |||
desktop->setCursor(width, height, hotspot, data); | |||
} | |||
void CConn::setCursorPos(const Point& pos) | |||
{ | |||
desktop->setCursorPos(pos); | |||
} | |||
void CConn::fence(rdr::U32 flags, unsigned len, const char data[]) | |||
{ | |||
CMsgHandler::fence(flags, len, data); |
@@ -63,6 +63,7 @@ public: | |||
void setCursor(int width, int height, const rfb::Point& hotspot, | |||
const rdr::U8* data); | |||
void setCursorPos(const rfb::Point& pos); | |||
void fence(rdr::U32 flags, unsigned len, const char data[]); | |||
@@ -51,6 +51,7 @@ | |||
#ifdef __APPLE__ | |||
#include "cocoa.h" | |||
#include <Carbon/Carbon.h> | |||
#endif | |||
#define EDGE_SCROLL_SIZE 32 | |||
@@ -186,6 +187,14 @@ DesktopWindow::DesktopWindow(int w, int h, const char *name, | |||
// Show hint about menu key | |||
Fl::add_timeout(0.5, menuOverlay, this); | |||
// By default we get a slight delay when we warp the pointer, something | |||
// we don't want or we'll get jerky movement | |||
#ifdef __APPLE__ | |||
CGEventSourceRef event = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); | |||
CGEventSourceSetLocalEventsSuppressionInterval(event, 0); | |||
CFRelease(event); | |||
#endif | |||
} | |||
@@ -322,6 +331,29 @@ void DesktopWindow::setCursor(int width, int height, | |||
} | |||
void DesktopWindow::setCursorPos(const rfb::Point& pos) | |||
{ | |||
if (!mouseGrabbed) { | |||
// Do nothing if we do not have the mouse captured. | |||
return; | |||
} | |||
#if defined(WIN32) | |||
SetCursorPos(pos.x + x_root() + viewport->x(), | |||
pos.y + y_root() + viewport->y()); | |||
#elif defined(__APPLE__) | |||
CGPoint new_pos; | |||
new_pos.x = pos.x + x_root() + viewport->x(); | |||
new_pos.y = pos.y + y_root() + viewport->y(); | |||
CGWarpMouseCursorPosition(new_pos); | |||
#else // Assume this is Xlib | |||
Window rootwindow = DefaultRootWindow(fl_display); | |||
XWarpPointer(fl_display, rootwindow, rootwindow, 0, 0, 0, 0, | |||
pos.x + x_root() + viewport->x(), | |||
pos.y + y_root() + viewport->y()); | |||
#endif | |||
} | |||
void DesktopWindow::show() | |||
{ | |||
Fl_Window::show(); |
@@ -66,6 +66,9 @@ public: | |||
void setCursor(int width, int height, const rfb::Point& hotspot, | |||
const rdr::U8* data); | |||
// Server-provided cursor position | |||
void setCursorPos(const rfb::Point& pos); | |||
// Change client LED state | |||
void setLEDState(unsigned int state); | |||
@@ -417,7 +417,7 @@ SDisplay::processEvent(HANDLE event) { | |||
// Update the cursor position | |||
// NB: First translate from Screen coordinates to Desktop | |||
Point desktopPos = info.position.translate(screenRect.tl.negate()); | |||
server->setCursorPos(desktopPos); | |||
server->setCursorPos(desktopPos, false); | |||
old_cursor = info; | |||
} |