#include <rfb/Encoder.h>
#include <rfb/SMsgWriter.h>
#include <rfb/LogWriter.h>
+#include <rfb/ledStates.h>
using namespace rfb;
nRectsInUpdate(0), nRectsInHeader(0),
needSetDesktopSize(false), needExtendedDesktopSize(false),
needSetDesktopName(false), needSetCursor(false),
- needSetXCursor(false), needSetCursorWithAlpha(false)
+ needSetXCursor(false), needSetCursorWithAlpha(false),
+ needLEDState(false)
{
}
return true;
}
+bool SMsgWriter::writeLEDState()
+{
+ if (!cp->supportsLEDState)
+ return false;
+ if (cp->ledState() == ledUnknown)
+ return false;
+
+ needLEDState = true;
+
+ return true;
+}
+
bool SMsgWriter::needFakeUpdate()
{
if (needSetDesktopName)
return true;
if (needSetCursor || needSetXCursor || needSetCursorWithAlpha)
return true;
+ if (needLEDState)
+ return true;
if (needNoDataUpdate())
return true;
nRects++;
if (needSetCursorWithAlpha)
nRects++;
+ if (needLEDState)
+ nRects++;
}
os->writeU16(nRects);
writeSetDesktopNameRect(cp->name());
needSetDesktopName = false;
}
+
+ if (needLEDState) {
+ writeLEDStateRect(cp->ledState());
+ needLEDState = false;
+ }
}
void SMsgWriter::writeNoDataRects()
data += 4;
}
}
+
+void SMsgWriter::writeLEDStateRect(rdr::U8 state)
+{
+ if (!cp->supportsLEDState)
+ throw Exception("Client does not support LED state updates");
+ if (cp->ledState() == ledUnknown)
+ throw Exception("Server does not support LED state updates");
+ if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+ throw Exception("SMsgWriter::writeLEDStateRect: nRects out of sync");
+
+ os->writeS16(0);
+ os->writeS16(0);
+ os->writeU16(0);
+ os->writeU16(0);
+ os->writeU32(pseudoEncodingLEDState);
+ os->writeU8(state);
+}
}
+void VNCSConnectionST::setLEDStateOrClose(unsigned int state)
+{
+ try {
+ setLEDState(state);
+ } catch(rdr::Exception& e) {
+ close(e.str());
+ }
+}
+
+
int VNCSConnectionST::checkIdleTimeout()
{
int idleTimeout = rfb::Server::idleTimeout;
cp.height = server->pb->height();
cp.screenLayout = server->screenLayout;
cp.setName(server->getName());
+ cp.setLEDState(server->ledState);
// - Set the default pixel format
cp.setPF(server->pb->getPF());
vlog.debug("Ignoring lock key (e.g. caps lock)");
return;
}
- // Always ignore ScrollLock though as we don't have a heuristic
- // for that
- if (key == XK_Scroll_Lock) {
- vlog.debug("Ignoring lock key (e.g. caps lock)");
- return;
- }
- if (down && (server->ledState != ledUnknown)) {
- // 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))) {
- bool uppercase, shift, lock;
-
- uppercase = (key >= XK_A) && (key <= 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);
- }
+ // Lock key heuristics
+ // (only for clients that do not support the LED state extension)
+ if (!cp.supportsLEDState) {
+ // Always ignore ScrollLock as we don't have a heuristic
+ // for that
+ if (key == XK_Scroll_Lock) {
+ vlog.debug("Ignoring lock key (e.g. caps lock)");
+ return;
}
- // 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)) {
- bool number, shift, lock;
-
- number = ((key >= XK_KP_0) && (key <= XK_KP_9)) ||
- (key == XK_KP_Separator) || (key == XK_KP_Decimal);
- shift = pressedKeys.find(XK_Shift_L) != pressedKeys.end() ||
- pressedKeys.find(XK_Shift_R) != pressedKeys.end();
- lock = server->ledState & ledNumLock;
-
- if (shift) {
- // We don't know the appropriate NumLock state for when Shift
- // is pressed as it could be one of:
- //
- // a) A Unix client where Shift negates NumLock
- //
- // b) A Windows client where Shift only cancels NumLock
- //
- // c) A macOS client where Shift doesn't have any effect
- //
- } 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);
+ if (down && (server->ledState != ledUnknown)) {
+ // 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))) {
+ bool uppercase, shift, lock;
+
+ uppercase = (key >= XK_A) && (key <= 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);
+ }
+ }
+
+ // 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)) {
+ bool number, shift, lock;
+
+ number = ((key >= XK_KP_0) && (key <= XK_KP_9)) ||
+ (key == XK_KP_Separator) || (key == XK_KP_Decimal);
+ shift = pressedKeys.find(XK_Shift_L) != pressedKeys.end() ||
+ pressedKeys.find(XK_Shift_R) != pressedKeys.end();
+ lock = server->ledState & ledNumLock;
+
+ if (shift) {
+ // We don't know the appropriate NumLock state for when Shift
+ // is pressed as it could be one of:
+ //
+ // a) A Unix client where Shift negates NumLock
+ //
+ // b) A Windows client where Shift only cancels NumLock
+ //
+ // c) A macOS client where Shift doesn't have any effect
+ //
+ } 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);
+ }
}
}
}
writer()->writeEndOfContinuousUpdates();
}
+void VNCSConnectionST::supportsLEDState()
+{
+ writer()->writeLEDState();
+}
+
bool VNCSConnectionST::handleTimeout(Timer* t)
{
writeFramebufferUpdate();
}
+void VNCSConnectionST::setLEDState(unsigned int ledstate)
+{
+ if (state() != RFBSTATE_NORMAL)
+ return;
+
+ cp.setLEDState(ledstate);
+
+ if (!writer()->writeLEDState()) {
+ // No client support
+ return;
+ }
+
+ writeFramebufferUpdate();
+}
+
void VNCSConnectionST::setSocketTimeouts()
{
int timeoutms = rfb::Server::clientWaitTimeMillis;