void CMsgHandler::framebufferUpdateEnd() | void CMsgHandler::framebufferUpdateEnd() | ||||
{ | { | ||||
} | } | ||||
void CMsgHandler::setLEDState(unsigned int state) | |||||
{ | |||||
cp.setLEDState(state); | |||||
} |
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; | ||||
}; | }; | ||||
} | } |
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); | |||||
} |
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; |
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; |
#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; | |||||
} |
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 |
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; |
/* 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) | 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() |
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 ////////////////////// | ||||
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(); |
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}) |
} | } | ||||
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; |
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); |
#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) |
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); |
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 |
#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); | |||||
} |