This adds the basic infrastructure and handshake for the QEMU Extended Key Events extension. No viewer or server makes use of the extra functionality yet though.tags/v1.8.90
@@ -75,6 +75,11 @@ void CMsgHandler::endOfContinuousUpdates() | |||
cp.supportsContinuousUpdates = true; | |||
} | |||
void CMsgHandler::supportsQEMUKeyEvent() | |||
{ | |||
cp.supportsQEMUKeyEvent = true; | |||
} | |||
void CMsgHandler::framebufferUpdateStart() | |||
{ | |||
} |
@@ -55,6 +55,7 @@ namespace rfb { | |||
virtual void setName(const char* name); | |||
virtual void fence(rdr::U32 flags, unsigned len, const char data[]); | |||
virtual void endOfContinuousUpdates(); | |||
virtual void supportsQEMUKeyEvent(); | |||
virtual void serverInit() = 0; | |||
virtual void readAndDecodeRect(const Rect& r, int encoding, |
@@ -111,6 +111,8 @@ void CMsgReader::readMsg() | |||
break; | |||
case pseudoEncodingLEDState: | |||
readLEDState(); | |||
case pseudoEncodingQEMUKeyEvent: | |||
handler->supportsQEMUKeyEvent(); | |||
break; | |||
default: | |||
readRect(Rect(x, y, x+w, y+h), encoding); |
@@ -21,6 +21,7 @@ | |||
#include <rfb/msgTypes.h> | |||
#include <rfb/fenceTypes.h> | |||
#include <rfb/encodings.h> | |||
#include <rfb/qemuTypes.h> | |||
#include <rfb/Exception.h> | |||
#include <rfb/PixelFormat.h> | |||
#include <rfb/Rect.h> | |||
@@ -88,6 +89,7 @@ void CMsgWriter::writeSetEncodings(int preferredEncoding, bool useCopyRect) | |||
encodings[nEncodings++] = pseudoEncodingLastRect; | |||
encodings[nEncodings++] = pseudoEncodingContinuousUpdates; | |||
encodings[nEncodings++] = pseudoEncodingFence; | |||
encodings[nEncodings++] = pseudoEncodingQEMUKeyEvent; | |||
if (Decoder::supported(preferredEncoding)) { | |||
encodings[nEncodings++] = preferredEncoding; | |||
@@ -215,13 +217,26 @@ void CMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[]) | |||
endMsg(); | |||
} | |||
void CMsgWriter::keyEvent(rdr::U32 key, bool down) | |||
void CMsgWriter::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) | |||
{ | |||
startMsg(msgTypeKeyEvent); | |||
os->writeU8(down); | |||
os->pad(2); | |||
os->writeU32(key); | |||
endMsg(); | |||
if (!cp->supportsQEMUKeyEvent || !keycode) { | |||
/* This event isn't meaningful without a valid keysym */ | |||
if (!keysym) | |||
return; | |||
startMsg(msgTypeKeyEvent); | |||
os->writeU8(down); | |||
os->pad(2); | |||
os->writeU32(keysym); | |||
endMsg(); | |||
} else { | |||
startMsg(msgTypeQEMUClientMessage); | |||
os->writeU8(qemuExtendedKeyEvent); | |||
os->writeU16(down); | |||
os->writeU32(keysym); | |||
os->writeU32(keycode); | |||
endMsg(); | |||
} | |||
} | |||
@@ -55,7 +55,7 @@ namespace rfb { | |||
// InputHandler implementation | |||
virtual void keyEvent(rdr::U32 key, bool down); | |||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); | |||
virtual void pointerEvent(const Point& pos, int buttonMask); | |||
virtual void clientCutText(const char* str, rdr::U32 len); | |||
@@ -35,8 +35,9 @@ ConnParams::ConnParams() | |||
supportsLocalCursorWithAlpha(false), | |||
supportsDesktopResize(false), supportsExtendedDesktopSize(false), | |||
supportsDesktopRename(false), supportsLastRect(false), | |||
supportsLEDState(false), supportsSetDesktopSize(false), | |||
supportsFence(false), supportsContinuousUpdates(false), | |||
supportsLEDState(false), supportsQEMUKeyEvent(false), | |||
supportsSetDesktopSize(false), supportsFence(false), | |||
supportsContinuousUpdates(false), | |||
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1), | |||
subsampling(subsampleUndefined), name_(0), verStrPos(0), | |||
ledState_(ledUnknown) | |||
@@ -109,6 +110,7 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings) | |||
supportsExtendedDesktopSize = false; | |||
supportsLocalXCursor = false; | |||
supportsLastRect = false; | |||
supportsQEMUKeyEvent = false; | |||
compressLevel = -1; | |||
qualityLevel = -1; | |||
fineQualityLevel = -1; | |||
@@ -145,6 +147,8 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings) | |||
break; | |||
case pseudoEncodingLEDState: | |||
supportsLEDState = true; | |||
case pseudoEncodingQEMUKeyEvent: | |||
supportsQEMUKeyEvent = true; | |||
break; | |||
case pseudoEncodingFence: | |||
supportsFence = true; |
@@ -97,6 +97,7 @@ namespace rfb { | |||
bool supportsDesktopRename; | |||
bool supportsLastRect; | |||
bool supportsLEDState; | |||
bool supportsQEMUKeyEvent; | |||
bool supportsSetDesktopSize; | |||
bool supportsFence; |
@@ -31,7 +31,7 @@ namespace rfb { | |||
class InputHandler { | |||
public: | |||
virtual ~InputHandler() {} | |||
virtual void keyEvent(rdr::U32 key, bool down) {} | |||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) {} | |||
virtual void pointerEvent(const Point& pos, int buttonMask) {} | |||
virtual void clientCutText(const char* str, int len) {} | |||
}; |
@@ -278,6 +278,11 @@ void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings) | |||
SMsgHandler::setEncodings(nEncodings, encodings); | |||
} | |||
void SConnection::supportsQEMUKeyEvent() | |||
{ | |||
writer()->writeQEMUKeyEvent(); | |||
} | |||
void SConnection::versionReceived() | |||
{ | |||
} |
@@ -73,6 +73,7 @@ namespace rfb { | |||
virtual void setEncodings(int nEncodings, const rdr::S32* encodings); | |||
virtual void supportsQEMUKeyEvent(); | |||
// Methods to be overridden in a derived class | |||
@@ -41,11 +41,13 @@ void SMsgHandler::setPixelFormat(const PixelFormat& pf) | |||
void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings) | |||
{ | |||
bool firstFence, firstContinuousUpdates, firstLEDState; | |||
bool firstFence, firstContinuousUpdates, firstLEDState, | |||
firstQEMUKeyEvent; | |||
firstFence = !cp.supportsFence; | |||
firstContinuousUpdates = !cp.supportsContinuousUpdates; | |||
firstLEDState = !cp.supportsLEDState; | |||
firstQEMUKeyEvent = !cp.supportsQEMUKeyEvent; | |||
cp.setEncodings(nEncodings, encodings); | |||
@@ -57,6 +59,8 @@ void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings) | |||
supportsContinuousUpdates(); | |||
if (cp.supportsLEDState && firstLEDState) | |||
supportsLEDState(); | |||
if (cp.supportsQEMUKeyEvent && firstQEMUKeyEvent) | |||
supportsQEMUKeyEvent(); | |||
} | |||
void SMsgHandler::supportsLocalCursor() | |||
@@ -75,6 +79,10 @@ void SMsgHandler::supportsLEDState() | |||
{ | |||
} | |||
void SMsgHandler::supportsQEMUKeyEvent() | |||
{ | |||
} | |||
void SMsgHandler::setDesktopSize(int fb_width, int fb_height, | |||
const ScreenSet& layout) | |||
{ |
@@ -80,6 +80,11 @@ namespace rfb { | |||
// server state. | |||
virtual void supportsLEDState(); | |||
// supportsQEMUKeyEvent() is called the first time we detect that the | |||
// client wants the QEMU Extended Key Event extension. The default | |||
// handler will send a pseudo-rect back, signalling server support. | |||
virtual void supportsQEMUKeyEvent(); | |||
ConnParams cp; | |||
}; | |||
} |
@@ -19,6 +19,7 @@ | |||
#include <stdio.h> | |||
#include <rdr/InStream.h> | |||
#include <rfb/msgTypes.h> | |||
#include <rfb/qemuTypes.h> | |||
#include <rfb/Exception.h> | |||
#include <rfb/util.h> | |||
#include <rfb/SMsgHandler.h> | |||
@@ -78,6 +79,9 @@ void SMsgReader::readMsg() | |||
case msgTypeClientCutText: | |||
readClientCutText(); | |||
break; | |||
case msgTypeQEMUClientMessage: | |||
readQEMUMessage(); | |||
break; | |||
default: | |||
fprintf(stderr, "unknown message type %d\n", msgType); | |||
throw Exception("unknown message type"); | |||
@@ -184,7 +188,7 @@ void SMsgReader::readKeyEvent() | |||
bool down = is->readU8(); | |||
is->skip(2); | |||
rdr::U32 key = is->readU32(); | |||
handler->keyEvent(key, down); | |||
handler->keyEvent(key, 0, down); | |||
} | |||
void SMsgReader::readPointerEvent() | |||
@@ -214,3 +218,26 @@ void SMsgReader::readClientCutText() | |||
handler->clientCutText(ca.buf, len); | |||
} | |||
void SMsgReader::readQEMUMessage() | |||
{ | |||
int subType = is->readU8(); | |||
switch (subType) { | |||
case qemuExtendedKeyEvent: | |||
readQEMUKeyEvent(); | |||
break; | |||
default: | |||
throw Exception("unknown QEMU submessage type %d", subType); | |||
} | |||
} | |||
void SMsgReader::readQEMUKeyEvent() | |||
{ | |||
bool down = is->readU16(); | |||
rdr::U32 keysym = is->readU32(); | |||
rdr::U32 keycode = is->readU32(); | |||
if (!keycode) { | |||
vlog.error("Key event without keycode - ignoring"); | |||
return; | |||
} | |||
handler->keyEvent(keysym, keycode, down); | |||
} |
@@ -55,6 +55,9 @@ namespace rfb { | |||
void readPointerEvent(); | |||
void readClientCutText(); | |||
void readQEMUMessage(); | |||
void readQEMUKeyEvent(); | |||
SMsgHandler* handler; | |||
rdr::InStream* is; | |||
}; |
@@ -39,7 +39,7 @@ SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_) | |||
needSetDesktopSize(false), needExtendedDesktopSize(false), | |||
needSetDesktopName(false), needSetCursor(false), | |||
needSetXCursor(false), needSetCursorWithAlpha(false), | |||
needLEDState(false) | |||
needLEDState(false), needQEMUKeyEvent(false) | |||
{ | |||
} | |||
@@ -207,6 +207,16 @@ bool SMsgWriter::writeLEDState() | |||
return true; | |||
} | |||
bool SMsgWriter::writeQEMUKeyEvent() | |||
{ | |||
if (!cp->supportsQEMUKeyEvent) | |||
return false; | |||
needQEMUKeyEvent = true; | |||
return true; | |||
} | |||
bool SMsgWriter::needFakeUpdate() | |||
{ | |||
if (needSetDesktopName) | |||
@@ -215,6 +225,8 @@ bool SMsgWriter::needFakeUpdate() | |||
return true; | |||
if (needLEDState) | |||
return true; | |||
if (needQEMUKeyEvent) | |||
return true; | |||
if (needNoDataUpdate()) | |||
return true; | |||
@@ -265,6 +277,8 @@ void SMsgWriter::writeFramebufferUpdateStart(int nRects) | |||
nRects++; | |||
if (needLEDState) | |||
nRects++; | |||
if (needQEMUKeyEvent) | |||
nRects++; | |||
} | |||
os->writeU16(nRects); | |||
@@ -385,6 +399,11 @@ void SMsgWriter::writePseudoRects() | |||
writeLEDStateRect(cp->ledState()); | |||
needLEDState = false; | |||
} | |||
if (needQEMUKeyEvent) { | |||
writeQEMUKeyEventRect(); | |||
needQEMUKeyEvent = false; | |||
} | |||
} | |||
void SMsgWriter::writeNoDataRects() | |||
@@ -565,3 +584,17 @@ void SMsgWriter::writeLEDStateRect(rdr::U8 state) | |||
os->writeU32(pseudoEncodingLEDState); | |||
os->writeU8(state); | |||
} | |||
void SMsgWriter::writeQEMUKeyEventRect() | |||
{ | |||
if (!cp->supportsQEMUKeyEvent) | |||
throw Exception("Client does not support QEMU extended key events"); | |||
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) | |||
throw Exception("SMsgWriter::writeQEMUKeyEventRect: nRects out of sync"); | |||
os->writeS16(0); | |||
os->writeS16(0); | |||
os->writeU16(0); | |||
os->writeU16(0); | |||
os->writeU32(pseudoEncodingQEMUKeyEvent); | |||
} |
@@ -85,6 +85,9 @@ namespace rfb { | |||
// Same for LED state message | |||
bool writeLEDState(); | |||
// And QEMU keyboard event handshake | |||
bool writeQEMUKeyEvent(); | |||
// needFakeUpdate() returns true when an immediate update is needed in | |||
// order to flush out pseudo-rectangles to the client. | |||
bool needFakeUpdate(); | |||
@@ -135,6 +138,7 @@ namespace rfb { | |||
int hotspotX, int hotspotY, | |||
const rdr::U8* data); | |||
void writeLEDStateRect(rdr::U8 state); | |||
void writeQEMUKeyEventRect(); | |||
ConnParams* cp; | |||
rdr::OutStream* os; | |||
@@ -150,6 +154,7 @@ namespace rfb { | |||
bool needSetXCursor; | |||
bool needSetCursorWithAlpha; | |||
bool needLEDState; | |||
bool needQEMUKeyEvent; | |||
typedef struct { | |||
rdr::U16 reason, result; |
@@ -103,7 +103,7 @@ VNCSConnectionST::~VNCSConnectionST() | |||
std::set<rdr::U32>::iterator i; | |||
for (i=pressedKeys.begin(); i!=pressedKeys.end(); i++) { | |||
vlog.debug("Releasing key 0x%x on client disconnect", *i); | |||
server->desktop->keyEvent(*i, false); | |||
server->desktop->keyEvent(*i, 0, false); | |||
} | |||
if (server->pointerClient == this) | |||
server->pointerClient = 0; | |||
@@ -538,12 +538,12 @@ public: | |||
~VNCSConnectionSTShiftPresser() { | |||
if (pressed) { | |||
vlog.debug("Releasing fake Shift_L"); | |||
desktop->keyEvent(XK_Shift_L, false); | |||
desktop->keyEvent(XK_Shift_L, 0, false); | |||
} | |||
} | |||
void press() { | |||
vlog.debug("Pressing fake Shift_L"); | |||
desktop->keyEvent(XK_Shift_L, true); | |||
desktop->keyEvent(XK_Shift_L, 0, true); | |||
pressed = true; | |||
} | |||
SDesktop* desktop; | |||
@@ -552,32 +552,32 @@ public: | |||
// keyEvent() - record in the pressedKeys which keys were pressed. Allow | |||
// multiple down events (for autorepeat), but only allow a single up event. | |||
void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) { | |||
void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { | |||
lastEventTime = time(0); | |||
server->lastUserInputTime = lastEventTime; | |||
if (!(accessRights & AccessKeyEvents)) return; | |||
if (!rfb::Server::acceptKeyEvents) return; | |||
if (down) | |||
vlog.debug("Key pressed: 0x%x", key); | |||
vlog.debug("Key pressed: 0x%x / 0x%x", keysym, keycode); | |||
else | |||
vlog.debug("Key released: 0x%x", key); | |||
vlog.debug("Key released: 0x%x / 0x%x", keysym, keycode); | |||
// Remap the key if required | |||
if (server->keyRemapper) { | |||
rdr::U32 newkey; | |||
newkey = server->keyRemapper->remapKey(key); | |||
if (newkey != key) { | |||
newkey = server->keyRemapper->remapKey(keysym); | |||
if (newkey != keysym) { | |||
vlog.debug("Key remapped to 0x%x", newkey); | |||
key = newkey; | |||
keysym = newkey; | |||
} | |||
} | |||
// Avoid lock keys if we don't know the server state | |||
if ((server->ledState == ledUnknown) && | |||
((key == XK_Caps_Lock) || | |||
(key == XK_Num_Lock) || | |||
(key == XK_Scroll_Lock))) { | |||
((keysym == XK_Caps_Lock) || | |||
(keysym == XK_Num_Lock) || | |||
(keysym == XK_Scroll_Lock))) { | |||
vlog.debug("Ignoring lock key (e.g. caps lock)"); | |||
return; | |||
} | |||
@@ -587,7 +587,7 @@ void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) { | |||
if (!cp.supportsLEDState) { | |||
// Always ignore ScrollLock as we don't have a heuristic | |||
// for that | |||
if (key == XK_Scroll_Lock) { | |||
if (keysym == XK_Scroll_Lock) { | |||
vlog.debug("Ignoring lock key (e.g. caps lock)"); | |||
return; | |||
} | |||
@@ -596,32 +596,32 @@ void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) { | |||
// CapsLock synchronisation heuristic | |||
// (this assumes standard interaction between CapsLock the Shift | |||
// keys and normal characters) | |||
if (((key >= XK_A) && (key <= XK_Z)) || | |||
((key >= XK_a) && (key <= XK_z))) { | |||
if (((keysym >= XK_A) && (keysym <= XK_Z)) || | |||
((keysym >= XK_a) && (keysym <= XK_z))) { | |||
bool uppercase, shift, lock; | |||
uppercase = (key >= XK_A) && (key <= XK_Z); | |||
uppercase = (keysym >= XK_A) && (keysym <= XK_Z); | |||
shift = pressedKeys.find(XK_Shift_L) != pressedKeys.end() || | |||
pressedKeys.find(XK_Shift_R) != pressedKeys.end(); | |||
lock = server->ledState & ledCapsLock; | |||
if (lock == (uppercase == shift)) { | |||
vlog.debug("Inserting fake CapsLock to get in sync with client"); | |||
server->desktop->keyEvent(XK_Caps_Lock, true); | |||
server->desktop->keyEvent(XK_Caps_Lock, false); | |||
server->desktop->keyEvent(XK_Caps_Lock, 0, true); | |||
server->desktop->keyEvent(XK_Caps_Lock, 0, false); | |||
} | |||
} | |||
// NumLock synchronisation heuristic | |||
// (this is more cautious because of the differences between Unix, | |||
// Windows and macOS) | |||
if (((key >= XK_KP_Home) && (key <= XK_KP_Delete)) || | |||
((key >= XK_KP_0) && (key <= XK_KP_9)) || | |||
(key == XK_KP_Separator) || (key == XK_KP_Decimal)) { | |||
if (((keysym >= XK_KP_Home) && (keysym <= XK_KP_Delete)) || | |||
((keysym >= XK_KP_0) && (keysym <= XK_KP_9)) || | |||
(keysym == XK_KP_Separator) || (keysym == XK_KP_Decimal)) { | |||
bool number, shift, lock; | |||
number = ((key >= XK_KP_0) && (key <= XK_KP_9)) || | |||
(key == XK_KP_Separator) || (key == XK_KP_Decimal); | |||
number = ((keysym >= XK_KP_0) && (keysym <= XK_KP_9)) || | |||
(keysym == XK_KP_Separator) || (keysym == XK_KP_Decimal); | |||
shift = pressedKeys.find(XK_Shift_L) != pressedKeys.end() || | |||
pressedKeys.find(XK_Shift_R) != pressedKeys.end(); | |||
lock = server->ledState & ledNumLock; | |||
@@ -638,8 +638,8 @@ void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) { | |||
// | |||
} else if (lock == (number == shift)) { | |||
vlog.debug("Inserting fake NumLock to get in sync with client"); | |||
server->desktop->keyEvent(XK_Num_Lock, true); | |||
server->desktop->keyEvent(XK_Num_Lock, false); | |||
server->desktop->keyEvent(XK_Num_Lock, 0, true); | |||
server->desktop->keyEvent(XK_Num_Lock, 0, false); | |||
} | |||
} | |||
} | |||
@@ -647,19 +647,20 @@ void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) { | |||
// Turn ISO_Left_Tab into shifted Tab. | |||
VNCSConnectionSTShiftPresser shiftPresser(server->desktop); | |||
if (key == XK_ISO_Left_Tab) { | |||
if (keysym == XK_ISO_Left_Tab) { | |||
if (pressedKeys.find(XK_Shift_L) == pressedKeys.end() && | |||
pressedKeys.find(XK_Shift_R) == pressedKeys.end()) | |||
shiftPresser.press(); | |||
key = XK_Tab; | |||
keysym = XK_Tab; | |||
} | |||
if (down) { | |||
pressedKeys.insert(key); | |||
pressedKeys.insert(keysym); | |||
} else { | |||
if (!pressedKeys.erase(key)) return; | |||
if (!pressedKeys.erase(keysym)) | |||
return; | |||
} | |||
server->desktop->keyEvent(key, down); | |||
server->desktop->keyEvent(keysym, keycode, down); | |||
} | |||
void VNCSConnectionST::clientCutText(const char* str, int len) |
@@ -136,7 +136,7 @@ namespace rfb { | |||
virtual void clientInit(bool shared); | |||
virtual void setPixelFormat(const PixelFormat& pf); | |||
virtual void pointerEvent(const Point& pos, int buttonMask); | |||
virtual void keyEvent(rdr::U32 key, bool down); | |||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); | |||
virtual void clientCutText(const char* str, int len); | |||
virtual void framebufferUpdateRequest(const Rect& r, bool incremental); | |||
virtual void setDesktopSize(int fb_width, int fb_height, |
@@ -40,6 +40,7 @@ namespace rfb { | |||
const int pseudoEncodingFence = -312; | |||
const int pseudoEncodingContinuousUpdates = -313; | |||
const int pseudoEncodingCursorWithAlpha = -314; | |||
const int pseudoEncodingQEMUKeyEvent = -258; | |||
// TightVNC-specific | |||
const int pseudoEncodingLastRect = -224; |
@@ -45,5 +45,7 @@ namespace rfb { | |||
const int msgTypeClientFence = 248; | |||
const int msgTypeSetDesktopSize = 251; | |||
const int msgTypeQEMUClientMessage = 255; | |||
} | |||
#endif |
@@ -0,0 +1,25 @@ | |||
/* Copyright 2017 Pierre Ossman for Cendio AB | |||
* | |||
* This 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 software 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 software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
#ifndef __RFB_QEMUTYPES_H__ | |||
#define __RFB_QEMUTYPES_H__ | |||
namespace rfb { | |||
const int qemuExtendedKeyEvent = 0; | |||
const int qemuAudio = 1; | |||
} | |||
#endif |
@@ -295,10 +295,10 @@ public: | |||
#endif | |||
} | |||
virtual void keyEvent(rdr::U32 key, bool down) { | |||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) { | |||
#ifdef HAVE_XTEST | |||
if (!haveXtest) return; | |||
int keycode = XKeysymToKeycode(dpy, key); | |||
int keycode = XKeysymToKeycode(dpy, keysym); | |||
if (keycode) | |||
XTestFakeKeyEvent(dpy, keycode, down, CurrentTime); | |||
#endif |
@@ -771,7 +771,7 @@ void XserverDesktop::grabRegion(const rfb::Region& region) | |||
} | |||
} | |||
void XserverDesktop::keyEvent(rdr::U32 keysym, bool down) | |||
void XserverDesktop::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) | |||
{ | |||
vncKeyboardEvent(keysym, down); | |||
} |
@@ -89,7 +89,7 @@ public: | |||
// rfb::SDesktop callbacks | |||
virtual void pointerEvent(const rfb::Point& pos, int buttonMask); | |||
virtual void keyEvent(rdr::U32 key, bool down); | |||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); | |||
virtual void clientCutText(const char* str, int len); | |||
virtual rfb::Point getFbSize() { return rfb::Point(width(), height()); } | |||
virtual unsigned int setScreenLayout(int fb_width, int fb_height, |
@@ -709,8 +709,8 @@ void Viewport::handleKeyPress(int keyCode, rdr::U32 keySym) | |||
if (ctrlPressed && altPressed) { | |||
vlog.debug("Faking release of AltGr (Ctrl_L+Alt_R)"); | |||
try { | |||
cc->writer()->keyEvent(XK_Control_L, false); | |||
cc->writer()->keyEvent(XK_Alt_R, false); | |||
cc->writer()->keyEvent(XK_Control_L, 0, false); | |||
cc->writer()->keyEvent(XK_Alt_R, 0, false); | |||
} catch (rdr::Exception& e) { | |||
vlog.error("%s", e.str()); | |||
exit_vncviewer(e.str()); | |||
@@ -732,7 +732,7 @@ void Viewport::handleKeyPress(int keyCode, rdr::U32 keySym) | |||
#endif | |||
try { | |||
cc->writer()->keyEvent(keySym, true); | |||
cc->writer()->keyEvent(keySym, 0, true); | |||
} catch (rdr::Exception& e) { | |||
vlog.error("%s", e.str()); | |||
exit_vncviewer(e.str()); | |||
@@ -743,8 +743,8 @@ void Viewport::handleKeyPress(int keyCode, rdr::U32 keySym) | |||
if (ctrlPressed && altPressed) { | |||
vlog.debug("Restoring AltGr state"); | |||
try { | |||
cc->writer()->keyEvent(XK_Control_L, true); | |||
cc->writer()->keyEvent(XK_Alt_R, true); | |||
cc->writer()->keyEvent(XK_Control_L, 0, true); | |||
cc->writer()->keyEvent(XK_Alt_R, 0, true); | |||
} catch (rdr::Exception& e) { | |||
vlog.error("%s", e.str()); | |||
exit_vncviewer(e.str()); | |||
@@ -777,7 +777,7 @@ void Viewport::handleKeyRelease(int keyCode) | |||
#endif | |||
try { | |||
cc->writer()->keyEvent(iter->second, false); | |||
cc->writer()->keyEvent(iter->second, 0, false); | |||
} catch (rdr::Exception& e) { | |||
vlog.error("%s", e.str()); | |||
exit_vncviewer(e.str()); |
@@ -280,12 +280,12 @@ void SDisplay::pointerEvent(const Point& pos, int buttonmask) { | |||
} | |||
} | |||
void SDisplay::keyEvent(rdr::U32 key, bool down) { | |||
void SDisplay::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) { | |||
// - Check that the SDesktop doesn't need restarting | |||
if (isRestartRequired()) | |||
restartCore(); | |||
if (kbd) | |||
kbd->keyEvent(key, down); | |||
kbd->keyEvent(keysym, keycode, down); | |||
} | |||
bool SDisplay::checkLedState() { |
@@ -66,7 +66,7 @@ namespace rfb { | |||
virtual void start(VNCServer* vs); | |||
virtual void stop(); | |||
virtual void pointerEvent(const Point& pos, int buttonmask); | |||
virtual void keyEvent(rdr::U32 key, bool down); | |||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); | |||
virtual void clientCutText(const char* str, int len); | |||
virtual Point getFbSize(); | |||
@@ -321,7 +321,7 @@ win32::SKeyboard::SKeyboard() | |||
} | |||
void win32::SKeyboard::keyEvent(rdr::U32 keysym, bool down) | |||
void win32::SKeyboard::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) | |||
{ | |||
for (unsigned int i = 0; i < sizeof(keysymToAscii) / sizeof(keysymToAscii_t); i++) { | |||
if (keysymToAscii[i].keysym == keysym) { |
@@ -53,7 +53,7 @@ namespace rfb { | |||
class SKeyboard { | |||
public: | |||
SKeyboard(); | |||
void keyEvent(rdr::U32 key, bool down); | |||
void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); | |||
static BoolParameter deadKeyAware; | |||
private: | |||
std::map<rdr::U32,rdr::U8> vkMap; |