]> source.dussan.org Git - tigervnc.git/commitdiff
Send lock LED state from server to client
authorPierre Ossman <ossman@cendio.se>
Mon, 12 Dec 2016 15:59:15 +0000 (16:59 +0100)
committerPierre Ossman <ossman@cendio.se>
Thu, 24 Aug 2017 10:38:05 +0000 (12:38 +0200)
common/rfb/SMsgHandler.cxx
common/rfb/SMsgHandler.h
common/rfb/SMsgWriter.cxx
common/rfb/SMsgWriter.h
common/rfb/VNCSConnectionST.cxx
common/rfb/VNCSConnectionST.h
common/rfb/VNCServerST.cxx

index 388b21f821d1216a88fe9b34c10e0f3d163529fd..8e48c6738120f423d9ce087241b81f3835f4a4bf 100644 (file)
@@ -41,10 +41,11 @@ void SMsgHandler::setPixelFormat(const PixelFormat& pf)
 
 void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings)
 {
-  bool firstFence, firstContinuousUpdates;
+  bool firstFence, firstContinuousUpdates, firstLEDState;
 
   firstFence = !cp.supportsFence;
   firstContinuousUpdates = !cp.supportsContinuousUpdates;
+  firstLEDState = !cp.supportsLEDState;
 
   cp.setEncodings(nEncodings, encodings);
 
@@ -54,6 +55,8 @@ void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings)
     supportsFence();
   if (cp.supportsContinuousUpdates && firstContinuousUpdates)
     supportsContinuousUpdates();
+  if (cp.supportsLEDState && firstLEDState)
+    supportsLEDState();
 }
 
 void SMsgHandler::supportsLocalCursor()
@@ -68,6 +71,10 @@ void SMsgHandler::supportsContinuousUpdates()
 {
 }
 
+void SMsgHandler::supportsLEDState()
+{
+}
+
 void SMsgHandler::setDesktopSize(int fb_width, int fb_height,
                                  const ScreenSet& layout)
 {
index 74509e0b3ee369c3e2ba5f6f284498b011978c4d..cf6b6b3b78bf95edf88b667aa9d682fad5ddfb79 100644 (file)
@@ -74,6 +74,12 @@ namespace rfb {
     // this point if it is supported.
     virtual void supportsContinuousUpdates();
 
+    // supportsLEDState() is called the first time we detect that the
+    // client supports the LED state extension. A LEDState message
+    // should be sent back to the client to inform it of the current
+    // server state.
+    virtual void supportsLEDState();
+
     ConnParams cp;
   };
 }
index bc3f43987c09ad9e68fc221e863c7792e4a07c64..d8adfbc1c693b08a5701139b1fd6e8721644fe1b 100644 (file)
@@ -27,6 +27,7 @@
 #include <rfb/Encoder.h>
 #include <rfb/SMsgWriter.h>
 #include <rfb/LogWriter.h>
+#include <rfb/ledStates.h>
 
 using namespace rfb;
 
@@ -37,7 +38,8 @@ SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
     nRectsInUpdate(0), nRectsInHeader(0),
     needSetDesktopSize(false), needExtendedDesktopSize(false),
     needSetDesktopName(false), needSetCursor(false),
-    needSetXCursor(false), needSetCursorWithAlpha(false)
+    needSetXCursor(false), needSetCursorWithAlpha(false),
+    needLEDState(false)
 {
 }
 
@@ -193,12 +195,26 @@ bool SMsgWriter::writeSetCursorWithAlpha()
   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;
 
@@ -247,6 +263,8 @@ void SMsgWriter::writeFramebufferUpdateStart(int nRects)
       nRects++;
     if (needSetCursorWithAlpha)
       nRects++;
+    if (needLEDState)
+      nRects++;
   }
 
   os->writeU16(nRects);
@@ -362,6 +380,11 @@ void SMsgWriter::writePseudoRects()
     writeSetDesktopNameRect(cp->name());
     needSetDesktopName = false;
   }
+
+  if (needLEDState) {
+    writeLEDStateRect(cp->ledState());
+    needLEDState = false;
+  }
 }
 
 void SMsgWriter::writeNoDataRects()
@@ -525,3 +548,20 @@ void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
     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);
+}
index 548b8e8e8e0bcf2540dcfc6a8e7d9bc5c1ebf500..890b2b5bb82d537705a04f0723684b5ea71bf7af 100644 (file)
@@ -82,6 +82,9 @@ namespace rfb {
     bool writeSetXCursor();
     bool writeSetCursorWithAlpha();
 
+    // Same for LED state message
+    bool writeLEDState();
+
     // needFakeUpdate() returns true when an immediate update is needed in
     // order to flush out pseudo-rectangles to the client.
     bool needFakeUpdate();
@@ -131,6 +134,7 @@ namespace rfb {
     void writeSetCursorWithAlphaRect(int width, int height,
                                      int hotspotX, int hotspotY,
                                      const rdr::U8* data);
+    void writeLEDStateRect(rdr::U8 state);
 
     ConnParams* cp;
     rdr::OutStream* os;
@@ -145,6 +149,7 @@ namespace rfb {
     bool needSetCursor;
     bool needSetXCursor;
     bool needSetCursorWithAlpha;
+    bool needLEDState;
 
     typedef struct {
       rdr::U16 reason, result;
index de41e8fc8ccfe33f6cd47686a5d513329ab2ff10..232776fc3bc946e2f45d87926265b7418ac9ad73 100644 (file)
@@ -313,6 +313,16 @@ void VNCSConnectionST::setCursorOrClose()
 }
 
 
+void VNCSConnectionST::setLEDStateOrClose(unsigned int state)
+{
+  try {
+    setLEDState(state);
+  } catch(rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+
 int VNCSConnectionST::checkIdleTimeout()
 {
   int idleTimeout = rfb::Server::idleTimeout;
@@ -418,6 +428,7 @@ void VNCSConnectionST::authSuccess()
   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());
@@ -570,61 +581,66 @@ void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) {
     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);
+        }
       }
     }
   }
@@ -818,6 +834,11 @@ void VNCSConnectionST::supportsContinuousUpdates()
   writer()->writeEndOfContinuousUpdates();
 }
 
+void VNCSConnectionST::supportsLEDState()
+{
+  writer()->writeLEDState();
+}
+
 
 bool VNCSConnectionST::handleTimeout(Timer* t)
 {
@@ -1230,6 +1251,21 @@ void VNCSConnectionST::setDesktopName(const char *name)
   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;
index 74a6946d84e1c9a3aacf063ba1e27e4b3b12d566..8f33962da16799704e002a8daee70bbf0993eb92 100644 (file)
@@ -78,6 +78,7 @@ namespace rfb {
     void bellOrClose();
     void serverCutTextOrClose(const char *str, int len);
     void setDesktopNameOrClose(const char *name);
+    void setLEDStateOrClose(unsigned int state);
 
     // checkIdleTimeout() returns the number of milliseconds left until the
     // idle timeout expires.  If it has expired, the connection is closed and
@@ -146,6 +147,7 @@ namespace rfb {
     virtual void supportsLocalCursor();
     virtual void supportsFence();
     virtual void supportsContinuousUpdates();
+    virtual void supportsLEDState();
 
     // setAccessRights() allows a security package to limit the access rights
     // of a VNCSConnectioST to the server.  These access rights are applied
@@ -174,6 +176,7 @@ namespace rfb {
     void screenLayoutChange(rdr::U16 reason);
     void setCursor();
     void setDesktopName(const char *name);
+    void setLEDState(unsigned int state);
     void setSocketTimeouts();
 
     network::Socket* sock;
index 28f6a62be66026113fdfcf81539c4abcc2cc4452..43e8f3edafb2c3292ed15e3b55e58d22058399c4 100644 (file)
@@ -461,7 +461,17 @@ void VNCServerST::setCursorPos(const Point& pos)
 
 void VNCServerST::setLEDState(unsigned int state)
 {
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+
+  if (state == ledState)
+    return;
+
   ledState = state;
+
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->setLEDStateOrClose(state);
+  }
 }
 
 // Other public methods