void CMsgHandler::framebufferUpdateEnd()
{
}
+
+void CMsgHandler::setLEDState(unsigned int state)
+{
+ cp.setLEDState(state);
+}
virtual void bell() = 0;
virtual void serverCutText(const char* str, rdr::U32 len) = 0;
+ virtual void setLEDState(unsigned int state);
+
ConnParams cp;
};
}
case pseudoEncodingExtendedDesktopSize:
readExtendedDesktopSize(x, y, w, h);
break;
+ case pseudoEncodingLEDState:
+ readLEDState();
+ break;
default:
readRect(Rect(x, y, x+w, y+h), encoding);
break;
handler->setExtendedDesktopSize(x, y, w, h, layout);
}
+
+void CMsgReader::readLEDState()
+{
+ rdr::U8 state;
+
+ state = is->readU8();
+
+ handler->setLEDState(state);
+}
void readSetCursorWithAlpha(int width, int height, const Point& hotspot);
void readSetDesktopName(int x, int y, int w, int h);
void readExtendedDesktopSize(int x, int y, int w, int h);
+ void readLEDState();
CMsgHandler* handler;
rdr::InStream* is;
encodings[nEncodings++] = pseudoEncodingExtendedDesktopSize;
if (cp->supportsDesktopRename)
encodings[nEncodings++] = pseudoEncodingDesktopName;
+ if (cp->supportsLEDState)
+ encodings[nEncodings++] = pseudoEncodingLEDState;
encodings[nEncodings++] = pseudoEncodingLastRect;
encodings[nEncodings++] = pseudoEncodingContinuousUpdates;
#include <rdr/OutStream.h>
#include <rfb/Exception.h>
#include <rfb/encodings.h>
+#include <rfb/ledStates.h>
#include <rfb/ConnParams.h>
#include <rfb/util.h>
supportsLocalCursorWithAlpha(false),
supportsDesktopResize(false), supportsExtendedDesktopSize(false),
supportsDesktopRename(false), supportsLastRect(false),
- supportsSetDesktopSize(false), supportsFence(false),
- supportsContinuousUpdates(false),
+ supportsLEDState(false), supportsSetDesktopSize(false),
+ supportsFence(false), supportsContinuousUpdates(false),
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
- subsampling(subsampleUndefined), name_(0), verStrPos(0)
+ subsampling(subsampleUndefined), name_(0), verStrPos(0),
+ ledState_(ledUnknown)
{
setName("");
cursor_ = new Cursor(0, 0, Point(), NULL);
case pseudoEncodingLastRect:
supportsLastRect = true;
break;
+ case pseudoEncodingLEDState:
+ supportsLEDState = true;
+ break;
case pseudoEncodingFence:
supportsFence = true;
break;
encodings_.insert(encodings[i]);
}
}
+
+void ConnParams::setLEDState(unsigned int state)
+{
+ ledState_ = state;
+}
void setEncodings(int nEncodings, const rdr::S32* encodings);
+ unsigned int ledState() { return ledState_; }
+ void setLEDState(unsigned int state);
+
bool useCopyRect;
bool supportsLocalCursor;
bool supportsExtendedDesktopSize;
bool supportsDesktopRename;
bool supportsLastRect;
+ bool supportsLEDState;
bool supportsSetDesktopSize;
bool supportsFence;
std::set<rdr::S32> encodings_;
char verStr[13];
int verStrPos;
+ unsigned int ledState_;
};
}
#endif
const int pseudoEncodingXCursor = -240;
const int pseudoEncodingCursor = -239;
const int pseudoEncodingDesktopSize = -223;
+ const int pseudoEncodingLEDState = -261;
const int pseudoEncodingExtendedDesktopSize = -308;
const int pseudoEncodingDesktopName = -307;
const int pseudoEncodingFence = -312;
--- /dev/null
+/* Copyright 2016 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_LEDSTATES_H__
+#define __RFB_LEDSTATES_H__
+
+namespace rfb {
+
+ const unsigned int ledScrollLock = 1 << 0;
+ const unsigned int ledNumLock = 1 << 1;
+ const unsigned int ledCapsLock = 1 << 2;
+
+ const unsigned int ledUnknown = (unsigned int)-1;
+}
+
+#endif
target_link_libraries(fbperf msimg32)
endif()
if(APPLE)
- target_link_libraries(fbperf "-framework Cocoa" "-framework Carbon")
+ target_link_libraries(fbperf "-framework Cocoa")
+ target_link_libraries(fbperf "-framework Carbon")
+ target_link_libraries(fbperf "-framework IOKit")
endif()
cp.supportsExtendedDesktopSize = true;
cp.supportsDesktopRename = true;
+ cp.supportsLEDState = true;
+
if (customCompressLevel)
cp.compressLevel = compressLevel;
else
}
}
+void CConn::setLEDState(unsigned int state)
+{
+ CConnection::setLEDState(state);
+
+ desktop->setLEDState(state);
+}
+
////////////////////// Internal methods //////////////////////
void fence(rdr::U32 flags, unsigned len, const char data[]);
+ void setLEDState(unsigned int state);
+
private:
void resizeFramebuffer();
endif()
if(APPLE)
- target_link_libraries(vncviewer "-framework Cocoa" "-framework Carbon")
+ target_link_libraries(vncviewer "-framework Cocoa")
+ target_link_libraries(vncviewer "-framework Carbon")
+ target_link_libraries(vncviewer "-framework IOKit")
endif()
install(TARGETS vncviewer DESTINATION ${BIN_DIR})
}
+void DesktopWindow::setLEDState(unsigned int state)
+{
+ viewport->setLEDState(state);
+}
+
+
void DesktopWindow::resize(int x, int y, int w, int h)
{
bool resizing;
void setCursor(int width, int height, const rfb::Point& hotspot,
const rdr::U8* data);
+ // Change client LED state
+ void setLEDState(unsigned int state);
+
// Fl_Window callback methods
void draw();
void resize(int x, int y, int w, int h);
#include <rfb/CMsgWriter.h>
#include <rfb/LogWriter.h>
#include <rfb/Exception.h>
+#include <rfb/ledStates.h>
// FLTK can pull in the X11 headers on some systems
#ifndef XK_VoidSymbol
#include <rfb/XF86keysym.h>
#endif
+#if ! (defined(WIN32) || defined(__APPLE__))
+#include <X11/XKBlib.h>
+#endif
+
#ifndef NoSymbol
#define NoSymbol 0
#endif
// Fake key presses use this value and above
static const int fakeKeyBase = 0x200;
+// Used to detect fake input (0xaa is not a real key)
+#ifdef WIN32
+static const WORD SCAN_FAKE = 0xaa;
+#endif
+
Viewport::Viewport(int w, int h, const rfb::PixelFormat& serverPF, CConn* cc_)
: Fl_Widget(0, 0, w, h), cc(cc_), frameBuffer(NULL),
lastPointerPos(0, 0), lastButtonMask(0),
}
+void Viewport::setLEDState(unsigned int state)
+{
+ Fl_Widget *focus;
+
+ vlog.debug("Got server LED state: 0x%08x", state);
+
+ focus = Fl::grab();
+ if (!focus)
+ focus = Fl::focus();
+ if (!focus)
+ return;
+
+ if (focus != this)
+ return;
+
+#if defined(WIN32)
+ INPUT input[6];
+ UINT count;
+ UINT ret;
+
+ memset(input, 0, sizeof(input));
+ count = 0;
+
+ if (!!(state & ledCapsLock) != !!(GetKeyState(VK_CAPITAL) & 0x1)) {
+ input[count].type = input[count+1].type = INPUT_KEYBOARD;
+ input[count].ki.wVk = input[count+1].ki.wVk = VK_CAPITAL;
+ input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
+ input[count].ki.dwFlags = 0;
+ input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
+ count += 2;
+ }
+
+ if (!!(state & ledNumLock) != !!(GetKeyState(VK_NUMLOCK) & 0x1)) {
+ input[count].type = input[count+1].type = INPUT_KEYBOARD;
+ input[count].ki.wVk = input[count+1].ki.wVk = VK_NUMLOCK;
+ input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
+ input[count].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
+ input[count+1].ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
+ count += 2;
+ }
+
+ if (!!(state & ledScrollLock) != !!(GetKeyState(VK_SCROLL) & 0x1)) {
+ input[count].type = input[count+1].type = INPUT_KEYBOARD;
+ input[count].ki.wVk = input[count+1].ki.wVk = VK_SCROLL;
+ input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
+ input[count].ki.dwFlags = 0;
+ input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
+ count += 2;
+ }
+
+ if (count == 0)
+ return;
+
+ ret = SendInput(count, input, sizeof(*input));
+ if (ret < count)
+ vlog.error(_("Failed to update keyboard LED state: %lu"), GetLastError());
+#elif defined(__APPLE__)
+ int ret;
+
+ ret = cocoa_set_caps_lock_state(state & ledCapsLock);
+ if (ret != 0) {
+ vlog.error(_("Failed to update keyboard LED state: %d"), ret);
+ return;
+ }
+
+ ret = cocoa_set_num_lock_state(state & ledNumLock);
+ if (ret != 0) {
+ vlog.error(_("Failed to update keyboard LED state: %d"), ret);
+ return;
+ }
+
+ // No support for Scroll Lock //
+
+#else
+ unsigned int affect, values;
+ unsigned int mask;
+
+ Bool ret;
+
+ affect = values = 0;
+
+ affect |= LockMask;
+ if (state & ledCapsLock)
+ values |= LockMask;
+
+ mask = getModifierMask(XK_Num_Lock);
+ affect |= mask;
+ if (state & ledNumLock)
+ values |= mask;
+
+ mask = getModifierMask(XK_Scroll_Lock);
+ affect |= mask;
+ if (state & ledScrollLock)
+ values |= mask;
+
+ ret = XkbLockModifiers(fl_display, XkbUseCoreKbd, affect, values);
+ if (!ret)
+ vlog.error(_("Failed to update keyboard LED state"));
+#endif
+}
+
+
void Viewport::draw(Surface* dst)
{
int X, Y, W, H;
return Fl_Widget::handle(event);
}
+
+#if ! (defined(WIN32) || defined(__APPLE__))
+unsigned int Viewport::getModifierMask(unsigned int keysym)
+{
+ XkbDescPtr xkb;
+ unsigned int mask, keycode;
+ XkbAction *act;
+
+ mask = 0;
+
+ xkb = XkbGetMap(fl_display, XkbAllComponentsMask, XkbUseCoreKbd);
+ if (xkb == NULL)
+ return 0;
+
+ for (keycode = xkb->min_key_code; keycode <= xkb->max_key_code; keycode++) {
+ unsigned int state_out;
+ KeySym ks;
+
+ XkbTranslateKeyCode(xkb, keycode, 0, &state_out, &ks);
+ if (ks == NoSymbol)
+ continue;
+
+ if (ks == keysym)
+ break;
+ }
+
+ // KeySym not mapped?
+ if (keycode > xkb->max_key_code)
+ goto out;
+
+ act = XkbKeyAction(xkb, keycode, 0);
+ if (act == NULL)
+ goto out;
+ if (act->type != XkbSA_LockMods)
+ goto out;
+
+ if (act->mods.flags & XkbSA_UseModMapMods)
+ mask = xkb->map->modmap[keycode];
+ else
+ mask = act->mods.mask;
+
+out:
+ XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
+
+ return mask;
+}
+#endif
+
+
void Viewport::handleClipboardChange(int source, void *data)
{
Viewport *self = (Viewport *)data;
keyCode = ((msg->lParam >> 16) & 0xff);
+ if (keyCode == SCAN_FAKE) {
+ vlog.debug("Ignoring fake key press (virtual key 0x%02x)", vKey);
+ return 1;
+ }
+
// Windows sets the scan code to 0x00 for multimedia keys, so we
// have to do a reverse lookup based on the vKey.
if (keyCode == 0x00) {
isExtended = (msg->lParam & (1 << 24)) != 0;
keyCode = ((msg->lParam >> 16) & 0xff);
+
+ if (keyCode == SCAN_FAKE) {
+ vlog.debug("Ignoring fake key release (virtual key 0x%02x)", vKey);
+ return 1;
+ }
+
if (keyCode == 0x00)
keyCode = MapVirtualKey(vKey, MAPVK_VK_TO_VSC);
if (isExtended)
void setCursor(int width, int height, const rfb::Point& hotspot,
const rdr::U8* data);
+ // Change client LED state
+ void setLEDState(unsigned int state);
+
void draw(Surface* dst);
// Fl_Widget callback methods
private:
+ unsigned int getModifierMask(unsigned int keysym);
+
static void handleClipboardChange(int source, void *data);
void handlePointerEvent(const rfb::Point& pos, int buttonMask);
int cocoa_event_keycode(const void *event);
int cocoa_event_keysym(const void *event);
+int cocoa_set_caps_lock_state(bool on);
+int cocoa_set_num_lock_state(bool on);
+
#endif
#import <Cocoa/Cocoa.h>
#import <Carbon/Carbon.h>
+#include <IOKit/hidsystem/IOHIDLib.h>
+#include <IOKit/hidsystem/IOHIDParameter.h>
+
#define XK_LATIN1
#define XK_MISCELLANY
#define XK_XKB_KEYS
return ucs2keysym([chars characterAtIndex:0]);
}
+
+static int cocoa_open_hid(io_connect_t *ioc)
+{
+ kern_return_t ret;
+ io_service_t ios;
+ CFMutableDictionaryRef mdict;
+
+ mdict = IOServiceMatching(kIOHIDSystemClass);
+ ios = IOServiceGetMatchingService(kIOMasterPortDefault,
+ (CFDictionaryRef) mdict);
+ if (!ios)
+ return KERN_FAILURE;
+
+ ret = IOServiceOpen(ios, mach_task_self(), kIOHIDParamConnectType, ioc);
+ IOObjectRelease(ios);
+ if (ret != KERN_SUCCESS)
+ return ret;
+
+ return KERN_SUCCESS;
+}
+
+static int cocoa_set_modifier_lock_state(int modifier, bool on)
+{
+ kern_return_t ret;
+ io_connect_t ioc;
+
+ ret = cocoa_open_hid(&ioc);
+ if (ret != KERN_SUCCESS)
+ return ret;
+
+ ret = IOHIDSetModifierLockState(ioc, modifier, on);
+ IOServiceClose(ioc);
+ if (ret != KERN_SUCCESS)
+ return ret;
+
+ return KERN_SUCCESS;
+}
+
+int cocoa_set_caps_lock_state(bool on)
+{
+ return cocoa_set_modifier_lock_state(kIOHIDCapsLockState, on);
+}
+
+int cocoa_set_num_lock_state(bool on)
+{
+ return cocoa_set_modifier_lock_state(kIOHIDNumLockState, on);
+}