Переглянути джерело

Add client support for LED state sync

tags/v1.8.90
Pierre Ossman 7 роки тому
джерело
коміт
2fa63f8576

+ 5
- 0
common/rfb/CMsgHandler.cxx Переглянути файл

void CMsgHandler::framebufferUpdateEnd() void CMsgHandler::framebufferUpdateEnd()
{ {
} }

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

+ 2
- 0
common/rfb/CMsgHandler.h Переглянути файл

virtual void bell() = 0; virtual void bell() = 0;
virtual void serverCutText(const char* str, rdr::U32 len) = 0; virtual void serverCutText(const char* str, rdr::U32 len) = 0;


virtual void setLEDState(unsigned int state);

ConnParams cp; ConnParams cp;
}; };
} }

+ 12
- 0
common/rfb/CMsgReader.cxx Переглянути файл

case pseudoEncodingExtendedDesktopSize: case pseudoEncodingExtendedDesktopSize:
readExtendedDesktopSize(x, y, w, h); readExtendedDesktopSize(x, y, w, h);
break; break;
case pseudoEncodingLEDState:
readLEDState();
break;
default: default:
readRect(Rect(x, y, x+w, y+h), encoding); readRect(Rect(x, y, x+w, y+h), encoding);
break; break;


handler->setExtendedDesktopSize(x, y, w, h, layout); 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 Переглянути файл

void readSetCursorWithAlpha(int width, int height, const Point& hotspot); void readSetCursorWithAlpha(int width, int height, const Point& hotspot);
void readSetDesktopName(int x, int y, int w, int h); void readSetDesktopName(int x, int y, int w, int h);
void readExtendedDesktopSize(int x, int y, int w, int h); void readExtendedDesktopSize(int x, int y, int w, int h);
void readLEDState();


CMsgHandler* handler; CMsgHandler* handler;
rdr::InStream* is; rdr::InStream* is;

+ 2
- 0
common/rfb/CMsgWriter.cxx Переглянути файл

encodings[nEncodings++] = pseudoEncodingExtendedDesktopSize; encodings[nEncodings++] = pseudoEncodingExtendedDesktopSize;
if (cp->supportsDesktopRename) if (cp->supportsDesktopRename)
encodings[nEncodings++] = pseudoEncodingDesktopName; encodings[nEncodings++] = pseudoEncodingDesktopName;
if (cp->supportsLEDState)
encodings[nEncodings++] = pseudoEncodingLEDState;


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

+ 13
- 3
common/rfb/ConnParams.cxx Переглянути файл

#include <rdr/OutStream.h> #include <rdr/OutStream.h>
#include <rfb/Exception.h> #include <rfb/Exception.h>
#include <rfb/encodings.h> #include <rfb/encodings.h>
#include <rfb/ledStates.h>
#include <rfb/ConnParams.h> #include <rfb/ConnParams.h>
#include <rfb/util.h> #include <rfb/util.h>


supportsLocalCursorWithAlpha(false), supportsLocalCursorWithAlpha(false),
supportsDesktopResize(false), supportsExtendedDesktopSize(false), supportsDesktopResize(false), supportsExtendedDesktopSize(false),
supportsDesktopRename(false), supportsLastRect(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), compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
subsampling(subsampleUndefined), name_(0), verStrPos(0)
subsampling(subsampleUndefined), name_(0), verStrPos(0),
ledState_(ledUnknown)
{ {
setName(""); setName("");
cursor_ = new Cursor(0, 0, Point(), NULL); cursor_ = new Cursor(0, 0, Point(), NULL);
case pseudoEncodingLastRect: case pseudoEncodingLastRect:
supportsLastRect = true; supportsLastRect = true;
break; break;
case pseudoEncodingLEDState:
supportsLEDState = true;
break;
case pseudoEncodingFence: case pseudoEncodingFence:
supportsFence = true; supportsFence = true;
break; break;
encodings_.insert(encodings[i]); encodings_.insert(encodings[i]);
} }
} }

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

+ 5
- 0
common/rfb/ConnParams.h Переглянути файл



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


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

bool useCopyRect; bool useCopyRect;


bool supportsLocalCursor; bool supportsLocalCursor;
bool supportsExtendedDesktopSize; bool supportsExtendedDesktopSize;
bool supportsDesktopRename; bool supportsDesktopRename;
bool supportsLastRect; bool supportsLastRect;
bool supportsLEDState;


bool supportsSetDesktopSize; bool supportsSetDesktopSize;
bool supportsFence; bool supportsFence;
std::set<rdr::S32> encodings_; std::set<rdr::S32> encodings_;
char verStr[13]; char verStr[13];
int verStrPos; int verStrPos;
unsigned int ledState_;
}; };
} }
#endif #endif

+ 1
- 0
common/rfb/encodings.h Переглянути файл

const int pseudoEncodingXCursor = -240; const int pseudoEncodingXCursor = -240;
const int pseudoEncodingCursor = -239; const int pseudoEncodingCursor = -239;
const int pseudoEncodingDesktopSize = -223; const int pseudoEncodingDesktopSize = -223;
const int pseudoEncodingLEDState = -261;
const int pseudoEncodingExtendedDesktopSize = -308; const int pseudoEncodingExtendedDesktopSize = -308;
const int pseudoEncodingDesktopName = -307; const int pseudoEncodingDesktopName = -307;
const int pseudoEncodingFence = -312; const int pseudoEncodingFence = -312;

+ 30
- 0
common/rfb/ledStates.h Переглянути файл

/* 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 Переглянути файл

target_link_libraries(fbperf msimg32) target_link_libraries(fbperf msimg32)
endif() endif()
if(APPLE) 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() endif()

+ 9
- 0
vncviewer/CConn.cxx Переглянути файл

cp.supportsExtendedDesktopSize = true; cp.supportsExtendedDesktopSize = true;
cp.supportsDesktopRename = true; cp.supportsDesktopRename = true;


cp.supportsLEDState = true;

if (customCompressLevel) if (customCompressLevel)
cp.compressLevel = compressLevel; cp.compressLevel = compressLevel;
else else
} }
} }


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

desktop->setLEDState(state);
}



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



+ 2
- 0
vncviewer/CConn.h Переглянути файл



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


void setLEDState(unsigned int state);

private: private:


void resizeFramebuffer(); void resizeFramebuffer();

+ 3
- 1
vncviewer/CMakeLists.txt Переглянути файл

endif() endif()


if(APPLE) 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() endif()


install(TARGETS vncviewer DESTINATION ${BIN_DIR}) install(TARGETS vncviewer DESTINATION ${BIN_DIR})

+ 6
- 0
vncviewer/DesktopWindow.cxx Переглянути файл

} }




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


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

+ 3
- 0
vncviewer/DesktopWindow.h Переглянути файл

void setCursor(int width, int height, const rfb::Point& hotspot, void setCursor(int width, int height, const rfb::Point& hotspot,
const rdr::U8* data); const rdr::U8* data);


// Change client LED state
void setLEDState(unsigned int state);

// Fl_Window callback methods // Fl_Window callback methods
void draw(); void draw();
void resize(int x, int y, int w, int h); void resize(int x, int y, int w, int h);

+ 172
- 0
vncviewer/Viewport.cxx Переглянути файл

#include <rfb/CMsgWriter.h> #include <rfb/CMsgWriter.h>
#include <rfb/LogWriter.h> #include <rfb/LogWriter.h>
#include <rfb/Exception.h> #include <rfb/Exception.h>
#include <rfb/ledStates.h>


// FLTK can pull in the X11 headers on some systems // FLTK can pull in the X11 headers on some systems
#ifndef XK_VoidSymbol #ifndef XK_VoidSymbol
#include <rfb/XF86keysym.h> #include <rfb/XF86keysym.h>
#endif #endif


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

#ifndef NoSymbol #ifndef NoSymbol
#define NoSymbol 0 #define NoSymbol 0
#endif #endif
// Fake key presses use this value and above // Fake key presses use this value and above
static const int fakeKeyBase = 0x200; 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_) Viewport::Viewport(int w, int h, const rfb::PixelFormat& serverPF, CConn* cc_)
: Fl_Widget(0, 0, w, h), cc(cc_), frameBuffer(NULL), : Fl_Widget(0, 0, w, h), cc(cc_), frameBuffer(NULL),
lastPointerPos(0, 0), lastButtonMask(0), 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) void Viewport::draw(Surface* dst)
{ {
int X, Y, W, H; int X, Y, W, H;
return Fl_Widget::handle(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) void Viewport::handleClipboardChange(int source, void *data)
{ {
Viewport *self = (Viewport *)data; Viewport *self = (Viewport *)data;


keyCode = ((msg->lParam >> 16) & 0xff); 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 // Windows sets the scan code to 0x00 for multimedia keys, so we
// have to do a reverse lookup based on the vKey. // have to do a reverse lookup based on the vKey.
if (keyCode == 0x00) { if (keyCode == 0x00) {
isExtended = (msg->lParam & (1 << 24)) != 0; isExtended = (msg->lParam & (1 << 24)) != 0;


keyCode = ((msg->lParam >> 16) & 0xff); 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) if (keyCode == 0x00)
keyCode = MapVirtualKey(vKey, MAPVK_VK_TO_VSC); keyCode = MapVirtualKey(vKey, MAPVK_VK_TO_VSC);
if (isExtended) if (isExtended)

+ 5
- 0
vncviewer/Viewport.h Переглянути файл

void setCursor(int width, int height, const rfb::Point& hotspot, void setCursor(int width, int height, const rfb::Point& hotspot,
const rdr::U8* data); const rdr::U8* data);


// Change client LED state
void setLEDState(unsigned int state);

void draw(Surface* dst); void draw(Surface* dst);


// Fl_Widget callback methods // Fl_Widget callback methods


private: private:


unsigned int getModifierMask(unsigned int keysym);

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


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

+ 3
- 0
vncviewer/cocoa.h Переглянути файл

int cocoa_event_keycode(const void *event); int cocoa_event_keycode(const void *event);
int cocoa_event_keysym(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 #endif

+ 50
- 0
vncviewer/cocoa.mm Переглянути файл

#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import <Carbon/Carbon.h> #import <Carbon/Carbon.h>


#include <IOKit/hidsystem/IOHIDLib.h>
#include <IOKit/hidsystem/IOHIDParameter.h>

#define XK_LATIN1 #define XK_LATIN1
#define XK_MISCELLANY #define XK_MISCELLANY
#define XK_XKB_KEYS #define XK_XKB_KEYS


return ucs2keysym([chars characterAtIndex:0]); 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);
}

Завантаження…
Відмінити
Зберегти