Browse Source

Add client support for LED state sync

tags/v1.8.90
Pierre Ossman 7 years ago
parent
commit
2fa63f8576

+ 5
- 0
common/rfb/CMsgHandler.cxx View File

@@ -82,3 +82,8 @@ void CMsgHandler::framebufferUpdateStart()
void CMsgHandler::framebufferUpdateEnd()
{
}

void CMsgHandler::setLEDState(unsigned int state)
{
cp.setLEDState(state);
}

+ 2
- 0
common/rfb/CMsgHandler.h View File

@@ -69,6 +69,8 @@ namespace rfb {
virtual void bell() = 0;
virtual void serverCutText(const char* str, rdr::U32 len) = 0;

virtual void setLEDState(unsigned int state);

ConnParams cp;
};
}

+ 12
- 0
common/rfb/CMsgReader.cxx View File

@@ -109,6 +109,9 @@ void CMsgReader::readMsg()
case pseudoEncodingExtendedDesktopSize:
readExtendedDesktopSize(x, y, w, h);
break;
case pseudoEncodingLEDState:
readLEDState();
break;
default:
readRect(Rect(x, y, x+w, y+h), encoding);
break;
@@ -382,3 +385,12 @@ void CMsgReader::readExtendedDesktopSize(int x, int y, int w, int h)

handler->setExtendedDesktopSize(x, y, w, h, layout);
}

void CMsgReader::readLEDState()
{
rdr::U8 state;

state = is->readU8();

handler->setLEDState(state);
}

+ 1
- 0
common/rfb/CMsgReader.h View File

@@ -65,6 +65,7 @@ namespace rfb {
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;

+ 2
- 0
common/rfb/CMsgWriter.cxx View File

@@ -82,6 +82,8 @@ void CMsgWriter::writeSetEncodings(int preferredEncoding, bool useCopyRect)
encodings[nEncodings++] = pseudoEncodingExtendedDesktopSize;
if (cp->supportsDesktopRename)
encodings[nEncodings++] = pseudoEncodingDesktopName;
if (cp->supportsLEDState)
encodings[nEncodings++] = pseudoEncodingLEDState;

encodings[nEncodings++] = pseudoEncodingLastRect;
encodings[nEncodings++] = pseudoEncodingContinuousUpdates;

+ 13
- 3
common/rfb/ConnParams.cxx View File

@@ -22,6 +22,7 @@
#include <rdr/OutStream.h>
#include <rfb/Exception.h>
#include <rfb/encodings.h>
#include <rfb/ledStates.h>
#include <rfb/ConnParams.h>
#include <rfb/util.h>

@@ -34,10 +35,11 @@ ConnParams::ConnParams()
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);
@@ -141,6 +143,9 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
case pseudoEncodingLastRect:
supportsLastRect = true;
break;
case pseudoEncodingLEDState:
supportsLEDState = true;
break;
case pseudoEncodingFence:
supportsFence = true;
break;
@@ -183,3 +188,8 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
encodings_.insert(encodings[i]);
}
}

void ConnParams::setLEDState(unsigned int state)
{
ledState_ = state;
}

+ 5
- 0
common/rfb/ConnParams.h View File

@@ -84,6 +84,9 @@ namespace rfb {

void setEncodings(int nEncodings, const rdr::S32* encodings);

unsigned int ledState() { return ledState_; }
void setLEDState(unsigned int state);

bool useCopyRect;

bool supportsLocalCursor;
@@ -93,6 +96,7 @@ namespace rfb {
bool supportsExtendedDesktopSize;
bool supportsDesktopRename;
bool supportsLastRect;
bool supportsLEDState;

bool supportsSetDesktopSize;
bool supportsFence;
@@ -111,6 +115,7 @@ namespace rfb {
std::set<rdr::S32> encodings_;
char verStr[13];
int verStrPos;
unsigned int ledState_;
};
}
#endif

+ 1
- 0
common/rfb/encodings.h View File

@@ -34,6 +34,7 @@ namespace rfb {
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;

+ 30
- 0
common/rfb/ledStates.h View File

@@ -0,0 +1,30 @@
/* 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

+ 3
- 1
tests/CMakeLists.txt View File

@@ -40,5 +40,7 @@ if(WIN32)
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()

+ 9
- 0
vncviewer/CConn.cxx View File

@@ -92,6 +92,8 @@ CConn::CConn(const char* vncServerName, network::Socket* socket=NULL)
cp.supportsExtendedDesktopSize = true;
cp.supportsDesktopRename = true;

cp.supportsLEDState = true;

if (customCompressLevel)
cp.compressLevel = compressLevel;
else
@@ -503,6 +505,13 @@ void CConn::fence(rdr::U32 flags, unsigned len, const char data[])
}
}

void CConn::setLEDState(unsigned int state)
{
CConnection::setLEDState(state);

desktop->setLEDState(state);
}


////////////////////// Internal methods //////////////////////


+ 2
- 0
vncviewer/CConn.h View File

@@ -74,6 +74,8 @@ public:

void fence(rdr::U32 flags, unsigned len, const char data[]);

void setLEDState(unsigned int state);

private:

void resizeFramebuffer();

+ 3
- 1
vncviewer/CMakeLists.txt View File

@@ -53,7 +53,9 @@ if(WIN32)
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})

+ 6
- 0
vncviewer/DesktopWindow.cxx View File

@@ -395,6 +395,12 @@ void DesktopWindow::draw()
}


void DesktopWindow::setLEDState(unsigned int state)
{
viewport->setLEDState(state);
}


void DesktopWindow::resize(int x, int y, int w, int h)
{
bool resizing;

+ 3
- 0
vncviewer/DesktopWindow.h View File

@@ -66,6 +66,9 @@ public:
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);

+ 172
- 0
vncviewer/Viewport.cxx View File

@@ -28,6 +28,7 @@
#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
@@ -41,6 +42,10 @@
#include <rfb/XF86keysym.h>
#endif

#if ! (defined(WIN32) || defined(__APPLE__))
#include <X11/XKBlib.h>
#endif

#ifndef NoSymbol
#define NoSymbol 0
#endif
@@ -92,6 +97,11 @@ enum { ID_EXIT, ID_FULLSCREEN, ID_MINIMIZE, ID_RESIZE,
// 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),
@@ -218,6 +228,108 @@ void Viewport::setCursor(int width, int height, const Point& hotspot,
}


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;
@@ -352,6 +464,55 @@ int Viewport::handle(int event)
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;
@@ -577,6 +738,11 @@ int Viewport::handleSystemEvent(void *event, void *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) {
@@ -620,6 +786,12 @@ int Viewport::handleSystemEvent(void *event, void *data)
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)

+ 5
- 0
vncviewer/Viewport.h View File

@@ -47,6 +47,9 @@ public:
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
@@ -59,6 +62,8 @@ public:

private:

unsigned int getModifierMask(unsigned int keysym);

static void handleClipboardChange(int source, void *data);

void handlePointerEvent(const rfb::Point& pos, int buttonMask);

+ 3
- 0
vncviewer/cocoa.h View File

@@ -33,4 +33,7 @@ int cocoa_is_key_press(const void *event);
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

+ 50
- 0
vncviewer/cocoa.mm View File

@@ -27,6 +27,9 @@
#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
@@ -406,3 +409,50 @@ int cocoa_event_keysym(const void *event)

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);
}

Loading…
Cancel
Save