@@ -860,5 +860,7 @@ void CConnection::updateEncodings() | |||
if (qualityLevel >= 0 && qualityLevel <= 9) | |||
encodings.push_back(pseudoEncodingQualityLevel0 + qualityLevel); | |||
encodings.push_back(pseudoEncodingExtendedMouseButtons); | |||
writer()->writeSetEncodings(encodings); | |||
} |
@@ -80,6 +80,11 @@ void CMsgHandler::endOfContinuousUpdates() | |||
server.supportsContinuousUpdates = true; | |||
} | |||
void CMsgHandler::supportExtendedMouseButtons() | |||
{ | |||
server.supportsExtendedMouseButtons = true; | |||
} | |||
void CMsgHandler::supportsQEMUKeyEvent() | |||
{ | |||
server.supportsQEMUKeyEvent = true; |
@@ -58,6 +58,7 @@ namespace rfb { | |||
virtual void fence(uint32_t flags, unsigned len, const uint8_t data[]); | |||
virtual void endOfContinuousUpdates(); | |||
virtual void supportsQEMUKeyEvent(); | |||
virtual void supportExtendedMouseButtons(); | |||
virtual void serverInit(int width, int height, | |||
const PixelFormat& pf, | |||
const char* name) = 0; |
@@ -202,6 +202,10 @@ bool CMsgReader::readMsg() | |||
handler->supportsQEMUKeyEvent(); | |||
ret = true; | |||
break; | |||
case pseudoEncodingExtendedMouseButtons: | |||
handler->supportExtendedMouseButtons(); | |||
ret = true; | |||
break; | |||
default: | |||
ret = readRect(dataRect, rectEncoding); | |||
break; |
@@ -182,14 +182,22 @@ void CMsgWriter::writePointerEvent(const Point& pos, int buttonMask) | |||
if (p.x >= server->width()) p.x = server->width() - 1; | |||
if (p.y >= server->height()) p.y = server->height() - 1; | |||
startMsg(msgTypePointerEvent); | |||
os->writeU8(buttonMask); | |||
os->writeU16(p.x); | |||
os->writeU16(p.y); | |||
endMsg(); | |||
if (server->supportsExtendedMouseButtons) { | |||
startMsg(msgTypePointerEventExt); | |||
os->writeU16(buttonMask); | |||
os->writeU16(p.x); | |||
os->writeU16(p.y); | |||
endMsg(); | |||
} | |||
else { | |||
startMsg(msgTypePointerEvent); | |||
os->writeU8(buttonMask); | |||
os->writeU16(p.x); | |||
os->writeU16(p.y); | |||
endMsg(); | |||
} | |||
} | |||
void CMsgWriter::writeClientCutText(const char* str) | |||
{ | |||
if (strchr(str, '\r') != NULL) |
@@ -228,3 +228,10 @@ bool ClientParams::supportsContinuousUpdates() const | |||
return true; | |||
return false; | |||
} | |||
bool ClientParams::supportExtendedMouseButtons() const | |||
{ | |||
if (supportsEncoding(pseudoEncodingExtendedMouseButtons)) | |||
return true; | |||
return false; | |||
} |
@@ -101,6 +101,7 @@ namespace rfb { | |||
bool supportsLEDState() const; | |||
bool supportsFence() const; | |||
bool supportsContinuousUpdates() const; | |||
bool supportExtendedMouseButtons() const; | |||
int compressLevel; | |||
int qualityLevel; |
@@ -446,6 +446,11 @@ void SConnection::supportsQEMUKeyEvent() | |||
writer()->writeQEMUKeyEvent(); | |||
} | |||
void SConnection::supportsExtendedMouseButtons() | |||
{ | |||
writer()->writeExtendedMouseButtonSupport(); | |||
} | |||
void SConnection::versionReceived() | |||
{ | |||
} |
@@ -95,6 +95,8 @@ namespace rfb { | |||
virtual void supportsQEMUKeyEvent(); | |||
virtual void supportsExtendedMouseButtons() override; | |||
// Methods to be overridden in a derived class | |||
@@ -53,12 +53,13 @@ void SMsgHandler::setPixelFormat(const PixelFormat& pf) | |||
void SMsgHandler::setEncodings(int nEncodings, const int32_t* encodings) | |||
{ | |||
bool firstFence, firstContinuousUpdates, firstLEDState, | |||
firstQEMUKeyEvent; | |||
firstQEMUKeyEvent, firstExtMouseButtonEvent; | |||
firstFence = !client.supportsFence(); | |||
firstContinuousUpdates = !client.supportsContinuousUpdates(); | |||
firstLEDState = !client.supportsLEDState(); | |||
firstQEMUKeyEvent = !client.supportsEncoding(pseudoEncodingQEMUKeyEvent); | |||
firstExtMouseButtonEvent = !client.supportsEncoding(pseudoEncodingExtendedMouseButtons); | |||
client.setEncodings(nEncodings, encodings); | |||
@@ -72,6 +73,8 @@ void SMsgHandler::setEncodings(int nEncodings, const int32_t* encodings) | |||
supportsLEDState(); | |||
if (client.supportsEncoding(pseudoEncodingQEMUKeyEvent) && firstQEMUKeyEvent) | |||
supportsQEMUKeyEvent(); | |||
if (client.supportsEncoding(pseudoEncodingExtendedMouseButtons) && firstExtMouseButtonEvent) | |||
supportsExtendedMouseButtons(); | |||
} | |||
void SMsgHandler::handleClipboardCaps(uint32_t flags, const uint32_t* lengths) | |||
@@ -153,3 +156,7 @@ void SMsgHandler::supportsLEDState() | |||
void SMsgHandler::supportsQEMUKeyEvent() | |||
{ | |||
} | |||
void SMsgHandler::supportsExtendedMouseButtons() | |||
{ | |||
} |
@@ -95,6 +95,11 @@ namespace rfb { | |||
// handler will send a pseudo-rect back, signalling server support. | |||
virtual void supportsQEMUKeyEvent(); | |||
// supportsExtendedMouseButtons() is called the first time we detect that the | |||
// client supports sending 16 bit mouse button state. This lets us pass more button | |||
// states between server and client. | |||
virtual void supportsExtendedMouseButtons(); | |||
ClientParams client; | |||
}; | |||
} |
@@ -99,6 +99,9 @@ bool SMsgReader::readMsg() | |||
case msgTypePointerEvent: | |||
ret = readPointerEvent(); | |||
break; | |||
case msgTypePointerEventExt: | |||
ret = readPointerEventExt(); | |||
break; | |||
case msgTypeClientCutText: | |||
ret = readClientCutText(); | |||
break; | |||
@@ -281,6 +284,16 @@ bool SMsgReader::readPointerEvent() | |||
return true; | |||
} | |||
bool SMsgReader::readPointerEventExt() | |||
{ | |||
if (!is->hasData(2 + 2 + 2)) | |||
return false; | |||
int mask = is->readU16(); | |||
int x = is->readU16(); | |||
int y = is->readU16(); | |||
handler->pointerEvent(Point(x, y), mask); | |||
return true; | |||
} | |||
bool SMsgReader::readClientCutText() | |||
{ |
@@ -53,6 +53,7 @@ namespace rfb { | |||
bool readKeyEvent(); | |||
bool readPointerEvent(); | |||
bool readPointerEventExt(); | |||
bool readClientCutText(); | |||
bool readExtendedClipboard(int32_t len); | |||
@@ -50,6 +50,7 @@ SMsgWriter::SMsgWriter(ClientParams* client_, rdr::OutStream* os_) | |||
needSetDesktopName(false), needCursor(false), | |||
needCursorPos(false), needLEDState(false), | |||
needQEMUKeyEvent(false) | |||
,needExtMouseButtonEvent(false) | |||
{ | |||
} | |||
@@ -303,6 +304,14 @@ void SMsgWriter::writeQEMUKeyEvent() | |||
needQEMUKeyEvent = true; | |||
} | |||
void SMsgWriter::writeExtendedMouseButtonSupport() | |||
{ | |||
if (!client->supportsEncoding(pseudoEncodingExtendedMouseButtons)) | |||
throw Exception("Client does not support Extended Mouse Buttons"); | |||
needExtMouseButtonEvent = true; | |||
} | |||
bool SMsgWriter::needFakeUpdate() | |||
{ | |||
if (needSetDesktopName) | |||
@@ -315,6 +324,8 @@ bool SMsgWriter::needFakeUpdate() | |||
return true; | |||
if (needQEMUKeyEvent) | |||
return true; | |||
if (needExtMouseButtonEvent) | |||
return true; | |||
if (needNoDataUpdate()) | |||
return true; | |||
@@ -363,6 +374,8 @@ void SMsgWriter::writeFramebufferUpdateStart(int nRects) | |||
nRects++; | |||
if (needQEMUKeyEvent) | |||
nRects++; | |||
if (needExtMouseButtonEvent) | |||
nRects++; | |||
} | |||
os->writeU16(nRects); | |||
@@ -502,6 +515,11 @@ void SMsgWriter::writePseudoRects() | |||
writeQEMUKeyEventRect(); | |||
needQEMUKeyEvent = false; | |||
} | |||
if (needExtMouseButtonEvent) { | |||
writeExtendedMouseButtonRect(); | |||
needExtMouseButtonEvent = false; | |||
} | |||
} | |||
void SMsgWriter::writeNoDataRects() | |||
@@ -735,3 +753,17 @@ void SMsgWriter::writeQEMUKeyEventRect() | |||
os->writeU16(0); | |||
os->writeU32(pseudoEncodingQEMUKeyEvent); | |||
} | |||
void SMsgWriter::writeExtendedMouseButtonRect() | |||
{ | |||
if (!client->supportsEncoding(pseudoEncodingExtendedMouseButtons)) | |||
throw Exception("Client does not support extended mouse button events"); | |||
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) | |||
throw Exception("SMsgWriter::writeExtendedMouseButtonRect: nRects out of sync"); | |||
os->writeS16(0); | |||
os->writeS16(0); | |||
os->writeU16(0); | |||
os->writeU16(0); | |||
os->writeU32(pseudoEncodingExtendedMouseButtons); | |||
} |
@@ -93,6 +93,9 @@ namespace rfb { | |||
// And QEMU keyboard event handshake | |||
void writeQEMUKeyEvent(); | |||
// let the client know we support extended mouse button support | |||
void writeExtendedMouseButtonSupport(); | |||
// needFakeUpdate() returns true when an immediate update is needed in | |||
// order to flush out pseudo-rectangles to the client. | |||
bool needFakeUpdate(); | |||
@@ -148,6 +151,7 @@ namespace rfb { | |||
void writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY); | |||
void writeLEDStateRect(uint8_t state); | |||
void writeQEMUKeyEventRect(); | |||
void writeExtendedMouseButtonRect(); | |||
ClientParams* client; | |||
rdr::OutStream* os; | |||
@@ -160,6 +164,7 @@ namespace rfb { | |||
bool needCursorPos; | |||
bool needLEDState; | |||
bool needQEMUKeyEvent; | |||
bool needExtMouseButtonEvent; | |||
typedef struct { | |||
uint16_t reason, result; |
@@ -32,7 +32,7 @@ ServerParams::ServerParams() | |||
: majorVersion(0), minorVersion(0), | |||
supportsQEMUKeyEvent(false), | |||
supportsSetDesktopSize(false), supportsFence(false), | |||
supportsContinuousUpdates(false), | |||
supportsContinuousUpdates(false), supportsExtendedMouseButtons(false), | |||
width_(0), height_(0), | |||
ledState_(ledUnknown) | |||
{ |
@@ -79,6 +79,7 @@ namespace rfb { | |||
bool supportsSetDesktopSize; | |||
bool supportsFence; | |||
bool supportsContinuousUpdates; | |||
bool supportsExtendedMouseButtons; | |||
private: | |||
@@ -36,6 +36,7 @@ namespace rfb { | |||
const int pseudoEncodingXCursor = -240; | |||
const int pseudoEncodingCursor = -239; | |||
const int pseudoEncodingExtendedMouseButtons = -241; | |||
const int pseudoEncodingDesktopSize = -223; | |||
const int pseudoEncodingLEDState = -261; | |||
const int pseudoEncodingExtendedDesktopSize = -308; |
@@ -39,6 +39,8 @@ namespace rfb { | |||
const int msgTypeKeyEvent = 4; | |||
const int msgTypePointerEvent = 5; | |||
const int msgTypeClientCutText = 6; | |||
const int msgTypePointerEventExt = 7; | |||
const int msgTypeEnableContinuousUpdates = 150; | |||
@@ -403,6 +403,12 @@ abstract public class CConnection extends CMsgHandler { | |||
requestNewUpdate(); | |||
} | |||
} | |||
public void SupportExtendedMouseButton() | |||
{ | |||
super.SupportExtendedMouseButton(); | |||
} | |||
// serverInit() is called when the ServerInit message is received. The | |||
// derived class must call on to CConnection::serverInit(). | |||
public void serverInit(int width, int height, | |||
@@ -683,6 +689,8 @@ abstract public class CConnection extends CMsgHandler { | |||
encodings.add(Encodings.pseudoEncodingLastRect); | |||
encodings.add(Encodings.pseudoEncodingContinuousUpdates); | |||
encodings.add(Encodings.pseudoEncodingFence); | |||
encodings.add(Encodings.pseudoEncodingExtendedMouseButtons); | |||
if (Decoder.supported(preferredEncoding)) { | |||
encodings.add(preferredEncoding); |
@@ -73,6 +73,11 @@ abstract public class CMsgHandler { | |||
server.supportsContinuousUpdates = true; | |||
} | |||
public void SupportExtendedMouseButton() | |||
{ | |||
server.supportsExtendedMouseButtons = true; | |||
} | |||
abstract public void clientRedirect(int port, String host, | |||
String x509subject); | |||
@@ -116,6 +116,9 @@ public class CMsgReader { | |||
case Encodings.pseudoEncodingExtendedDesktopSize: | |||
readExtendedDesktopSize(x, y, w, h); | |||
break; | |||
case Encodings.pseudoEncodingExtendedMouseButtons: | |||
handler.SupportExtendedMouseButton(); | |||
break; | |||
case Encodings.pseudoEncodingClientRedirect: | |||
nUpdateRectsLeft = 0; | |||
readClientRedirect(x, y, w, h); |
@@ -150,11 +150,20 @@ public class CMsgWriter { | |||
if (p.x >= server.width()) p.x = server.width() - 1; | |||
if (p.y >= server.height()) p.y = server.height() - 1; | |||
startMsg(MsgTypes.msgTypePointerEvent); | |||
os.writeU8(buttonMask); | |||
os.writeU16(p.x); | |||
os.writeU16(p.y); | |||
endMsg(); | |||
if(server.supportsExtendedMouseButtons) { | |||
startMsg(MsgTypes.msgTypePointerEventExt); | |||
os.writeU16(buttonMask); | |||
os.writeU16(p.x); | |||
os.writeU16(p.y); | |||
endMsg(); | |||
} | |||
else { | |||
startMsg(MsgTypes.msgTypePointerEvent); | |||
os.writeU8(buttonMask); | |||
os.writeU16(p.x); | |||
os.writeU16(p.y); | |||
endMsg(); | |||
} | |||
} | |||
synchronized public void writeClientCutText(String str, int len) |
@@ -34,6 +34,7 @@ public class Encodings { | |||
public static final int pseudoEncodingXCursor = -240; | |||
public static final int pseudoEncodingCursor = -239; | |||
public static final int pseudoEncodingExtendedMouseButtons = -241; | |||
public static final int pseudoEncodingDesktopSize = -223; | |||
public static final int pseudoEncodingExtendedDesktopSize = -308; | |||
public static final int pseudoEncodingDesktopName = -307; |
@@ -29,6 +29,8 @@ public class MsgTypes { | |||
public static final int msgTypeEndOfContinuousUpdates = 150; | |||
public static final int msgTypeExtendedMouseSupport = 151; | |||
public static final int msgTypeServerFence = 248; | |||
// client to server | |||
@@ -40,6 +42,7 @@ public class MsgTypes { | |||
public static final int msgTypeKeyEvent = 4; | |||
public static final int msgTypePointerEvent = 5; | |||
public static final int msgTypeClientCutText = 6; | |||
public static final int msgTypePointerEventExt = 7; | |||
public static final int msgTypeEnableContinuousUpdates = 150; | |||
@@ -38,7 +38,7 @@ public class ServerParams { | |||
{ | |||
majorVersion = 0; minorVersion = 0; | |||
width_ = 0; height_ = 0; useCopyRect = false; | |||
supportsLocalCursor = false; supportsLocalXCursor = false; | |||
supportsLocalCursor = false; supportsLocalXCursor = false; supportsExtendedMouseButtons = false; | |||
supportsLocalCursorWithAlpha = false; | |||
supportsDesktopResize = false; supportsExtendedDesktopSize = false; | |||
supportsDesktopRename = false; supportsLastRect = false; | |||
@@ -120,6 +120,7 @@ public class ServerParams { | |||
supportsDesktopResize = false; | |||
supportsExtendedDesktopSize = false; | |||
supportsLocalXCursor = false; | |||
supportsExtendedMouseButtons = false; | |||
supportsLastRect = false; | |||
compressLevel = -1; | |||
qualityLevel = -1; | |||
@@ -140,6 +141,9 @@ public class ServerParams { | |||
case Encodings.pseudoEncodingXCursor: | |||
supportsLocalXCursor = true; | |||
break; | |||
case Encodings.pseudoEncodingExtendedMouseButtons: | |||
supportsExtendedMouseButtons = true; | |||
break; | |||
case Encodings.pseudoEncodingCursorWithAlpha: | |||
supportsLocalCursorWithAlpha = true; | |||
break; | |||
@@ -205,6 +209,7 @@ public class ServerParams { | |||
public boolean supportsLocalCursor; | |||
public boolean supportsLocalXCursor; | |||
public boolean supportsExtendedMouseButtons; | |||
public boolean supportsLocalCursorWithAlpha; | |||
public boolean supportsDesktopResize; | |||
public boolean supportsExtendedDesktopSize; |
@@ -253,6 +253,16 @@ class Viewport extends JPanel implements ActionListener { | |||
buttonMask |= 2; | |||
if ((e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) != 0) | |||
buttonMask |= 4; | |||
//there are no masks for buttons 6 and 7 so we need to check for them only when pressed. | |||
if (tk.areExtraMouseButtonsEnabled()){ | |||
//Back | |||
if (MouseInfo.getNumberOfButtons() >= 6 && ((e.getModifiersEx() & e.getMaskForButton(6)) != 0)) | |||
buttonMask |= 1024; | |||
//Forward | |||
if (MouseInfo.getNumberOfButtons() >= 7 && ((e.getModifiersEx() & e.getMaskForButton(7)) != 0)) | |||
buttonMask |= 512; | |||
} | |||
if (e.getID() == MouseEvent.MOUSE_WHEEL) { | |||
wheelMask = 0; |
@@ -50,7 +50,7 @@ extern const unsigned int code_map_qnum_to_xorgevdev_len; | |||
extern const unsigned short code_map_qnum_to_xorgkbd[]; | |||
extern const unsigned int code_map_qnum_to_xorgkbd_len; | |||
#define BUTTONS 7 | |||
#define BUTTONS 11 | |||
DeviceIntPtr vncKeyboardDev; | |||
DeviceIntPtr vncPointerDev; | |||
@@ -206,6 +206,10 @@ static int vncPointerProc(DeviceIntPtr pDevice, int onoff) | |||
btn_labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN); | |||
btn_labels[5] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_LEFT); | |||
btn_labels[6] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_RIGHT); | |||
btn_labels[7] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_SIDE); | |||
btn_labels[8] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_EXTRA); | |||
btn_labels[9] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_FORWARD); | |||
btn_labels[10] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_BACK); | |||
axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X); | |||
axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y); |
@@ -606,6 +606,14 @@ int Viewport::handle(int event) | |||
if (Fl::event_button3()) | |||
buttonMask |= 4; | |||
//Fl::event_button is only good for FL_PUSH and FL_RELEASE | |||
if(event == FL_PUSH) { | |||
if (Fl::event_button() == 8) //Mouse Back | |||
buttonMask |= 1024; | |||
if (Fl::event_button() == 9) //Mouse Forward | |||
buttonMask |= 512; | |||
} | |||
if (event == FL_MOUSEWHEEL) { | |||
wheelMask = 0; | |||
if (Fl::event_dy() < 0) |