mirror of
https://github.com/kasmtech/KasmVNC.git
synced 2025-08-14 18:25:02 +02:00
Initial commit
This commit is contained in:
1
unix/xserver/hw/vnc/.gitignore
vendored
Normal file
1
unix/xserver/hw/vnc/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
Xvnc
|
689
unix/xserver/hw/vnc/Input.c
Normal file
689
unix/xserver/hw/vnc/Input.c
Normal file
@ -0,0 +1,689 @@
|
||||
/* Copyright (C) 2009 TightVNC Team
|
||||
* Copyright (C) 2009, 2014 Red Hat, Inc.
|
||||
* Copyright 2013-2015 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_DIX_CONFIG_H
|
||||
#include <dix-config.h>
|
||||
#endif
|
||||
|
||||
#include "xorg-version.h"
|
||||
|
||||
#include "Input.h"
|
||||
#include "vncExtInit.h"
|
||||
#include "RFBGlue.h"
|
||||
|
||||
#include "inputstr.h"
|
||||
#if XORG >= 110
|
||||
#include "inpututils.h"
|
||||
#endif
|
||||
#include "mi.h"
|
||||
#include "mipointer.h"
|
||||
#include "exevents.h"
|
||||
#include "scrnintstr.h"
|
||||
#include "xkbsrv.h"
|
||||
#include "xkbstr.h"
|
||||
#include "xserver-properties.h"
|
||||
extern _X_EXPORT DevPrivateKey CoreDevicePrivateKey;
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
extern const unsigned short code_map_qnum_to_xorgevdev[];
|
||||
extern const unsigned int code_map_qnum_to_xorgevdev_len;
|
||||
extern const unsigned short code_map_qnum_to_xorgkbd[];
|
||||
extern const unsigned int code_map_qnum_to_xorgkbd_len;
|
||||
|
||||
#define BUTTONS 7
|
||||
|
||||
/* Event queue is shared between all devices. */
|
||||
#if XORG < 111
|
||||
static EventList *eventq = NULL;
|
||||
#endif
|
||||
|
||||
DeviceIntPtr vncKeyboardDev;
|
||||
DeviceIntPtr vncPointerDev;
|
||||
|
||||
static int oldButtonMask;
|
||||
static int cursorPosX, cursorPosY;
|
||||
|
||||
static const unsigned short *codeMap;
|
||||
static unsigned int codeMapLen;
|
||||
|
||||
static KeySym pressedKeys[256];
|
||||
|
||||
static int vncPointerProc(DeviceIntPtr pDevice, int onoff);
|
||||
static void vncKeyboardBell(int percent, DeviceIntPtr device,
|
||||
void * ctrl, int class);
|
||||
static int vncKeyboardProc(DeviceIntPtr pDevice, int onoff);
|
||||
|
||||
static void vncKeysymKeyboardEvent(KeySym keysym, int down);
|
||||
|
||||
#define LOG_NAME "Input"
|
||||
|
||||
#define LOG_ERROR(...) vncLogError(LOG_NAME, __VA_ARGS__)
|
||||
#define LOG_STATUS(...) vncLogStatus(LOG_NAME, __VA_ARGS__)
|
||||
#define LOG_INFO(...) vncLogInfo(LOG_NAME, __VA_ARGS__)
|
||||
#define LOG_DEBUG(...) vncLogDebug(LOG_NAME, __VA_ARGS__)
|
||||
|
||||
/*
|
||||
* Init input device.
|
||||
* This has to be called after core pointer/keyboard
|
||||
* initialization which unfortunately is after extensions
|
||||
* initialization (which means we cannot call it in
|
||||
* vncExtensionInit(). Check InitExtensions(),
|
||||
* InitCoreDevices() and InitInput() calls in dix/main.c.
|
||||
* Instead we call it from XserverDesktop at an appropriate
|
||||
* time.
|
||||
*/
|
||||
void vncInitInputDevice(void)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
if ((vncPointerDev != NULL) || (vncKeyboardDev != NULL))
|
||||
return;
|
||||
|
||||
/*
|
||||
* On Linux we try to provide the same key codes as Xorg with
|
||||
* the evdev driver. On other platforms we mimic the older
|
||||
* Xorg KBD driver.
|
||||
*/
|
||||
#ifdef __linux__
|
||||
codeMap = code_map_qnum_to_xorgevdev;
|
||||
codeMapLen = code_map_qnum_to_xorgevdev_len;
|
||||
#else
|
||||
codeMap = code_map_qnum_to_xorgkbd;
|
||||
codeMapLen = code_map_qnum_to_xorgkbd_len;
|
||||
#endif
|
||||
|
||||
for (i = 0;i < 256;i++)
|
||||
pressedKeys[i] = NoSymbol;
|
||||
|
||||
ret = AllocDevicePair(serverClient, "KasmVNC",
|
||||
&vncPointerDev, &vncKeyboardDev,
|
||||
vncPointerProc, vncKeyboardProc,
|
||||
FALSE);
|
||||
|
||||
if (ret != Success)
|
||||
FatalError("Failed to initialize KasmVNC input devices\n");
|
||||
|
||||
if (ActivateDevice(vncPointerDev, TRUE) != Success ||
|
||||
ActivateDevice(vncKeyboardDev, TRUE) != Success)
|
||||
FatalError("Failed to activate KasmVNC devices\n");
|
||||
|
||||
if (!EnableDevice(vncPointerDev, TRUE) ||
|
||||
!EnableDevice(vncKeyboardDev, TRUE))
|
||||
FatalError("Failed to activate KasmVNC devices\n");
|
||||
|
||||
#if XORG < 111
|
||||
/* eventq is never free()-ed because it exists during server life. */
|
||||
if (eventq == NULL)
|
||||
GetEventList(&eventq);
|
||||
#endif
|
||||
|
||||
vncPrepareInputDevices();
|
||||
}
|
||||
|
||||
#if XORG < 111
|
||||
static void enqueueEvents(DeviceIntPtr dev, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
/*
|
||||
* Passing arguments in global variable eventq is probably not
|
||||
* good programming practise but in this case it is safe and
|
||||
* clear.
|
||||
*/
|
||||
mieqEnqueue(dev, (InternalEvent *) (eventq + i)->event);
|
||||
}
|
||||
}
|
||||
#endif /* XORG < 111 */
|
||||
|
||||
void vncPointerButtonAction(int buttonMask)
|
||||
{
|
||||
int i;
|
||||
#if XORG < 111
|
||||
int n;
|
||||
#endif
|
||||
#if XORG >= 110
|
||||
ValuatorMask mask;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < BUTTONS; i++) {
|
||||
if ((buttonMask ^ oldButtonMask) & (1 << i)) {
|
||||
int action = (buttonMask & (1<<i)) ?
|
||||
ButtonPress : ButtonRelease;
|
||||
#if XORG < 110
|
||||
n = GetPointerEvents(eventq, vncPointerDev,
|
||||
action, i + 1,
|
||||
POINTER_RELATIVE, 0, 0, NULL);
|
||||
enqueueEvents(vncPointerDev, n);
|
||||
#elif XORG < 111
|
||||
valuator_mask_set_range(&mask, 0, 0, NULL);
|
||||
n = GetPointerEvents(eventq, vncPointerDev,
|
||||
action, i + 1,
|
||||
POINTER_RELATIVE, &mask);
|
||||
enqueueEvents(vncPointerDev, n);
|
||||
#else
|
||||
valuator_mask_set_range(&mask, 0, 0, NULL);
|
||||
QueuePointerEvents(vncPointerDev, action, i + 1,
|
||||
POINTER_RELATIVE, &mask);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
oldButtonMask = buttonMask;
|
||||
}
|
||||
|
||||
void vncPointerMove(int x, int y)
|
||||
{
|
||||
int valuators[2];
|
||||
#if XORG < 111
|
||||
int n;
|
||||
#endif
|
||||
#if XORG >= 110
|
||||
ValuatorMask mask;
|
||||
#endif
|
||||
|
||||
if (cursorPosX == x && cursorPosY == y)
|
||||
return;
|
||||
|
||||
valuators[0] = x;
|
||||
valuators[1] = y;
|
||||
#if XORG < 110
|
||||
n = GetPointerEvents(eventq, vncPointerDev, MotionNotify, 0,
|
||||
POINTER_ABSOLUTE, 0, 2, valuators);
|
||||
enqueueEvents(vncPointerDev, n);
|
||||
#elif XORG < 111
|
||||
valuator_mask_set_range(&mask, 0, 2, valuators);
|
||||
n = GetPointerEvents(eventq, vncPointerDev, MotionNotify, 0,
|
||||
POINTER_ABSOLUTE, &mask);
|
||||
enqueueEvents(vncPointerDev, n);
|
||||
#else
|
||||
valuator_mask_set_range(&mask, 0, 2, valuators);
|
||||
QueuePointerEvents(vncPointerDev, MotionNotify, 0,
|
||||
POINTER_ABSOLUTE, &mask);
|
||||
#endif
|
||||
|
||||
cursorPosX = x;
|
||||
cursorPosY = y;
|
||||
}
|
||||
|
||||
void vncGetPointerPos(int *x, int *y)
|
||||
{
|
||||
if (vncPointerDev != NULL) {
|
||||
ScreenPtr ptrScreen;
|
||||
|
||||
miPointerGetPosition(vncPointerDev, &cursorPosX, &cursorPosY);
|
||||
|
||||
/* Pointer coordinates are screen relative */
|
||||
ptrScreen = miPointerGetScreen(vncPointerDev);
|
||||
cursorPosX += ptrScreen->x;
|
||||
cursorPosY += ptrScreen->y;
|
||||
}
|
||||
|
||||
*x = cursorPosX;
|
||||
*y = cursorPosY;
|
||||
}
|
||||
|
||||
static int vncPointerProc(DeviceIntPtr pDevice, int onoff)
|
||||
{
|
||||
BYTE map[BUTTONS + 1];
|
||||
DevicePtr pDev = (DevicePtr)pDevice;
|
||||
int i;
|
||||
/*
|
||||
* NOTE: map[] array is one element longer than btn_labels[] array. This
|
||||
* is not a bug.
|
||||
*/
|
||||
Atom btn_labels[BUTTONS];
|
||||
Atom axes_labels[2];
|
||||
|
||||
switch (onoff) {
|
||||
case DEVICE_INIT:
|
||||
for (i = 0; i < BUTTONS + 1; i++)
|
||||
map[i] = i;
|
||||
|
||||
btn_labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT);
|
||||
btn_labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE);
|
||||
btn_labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT);
|
||||
btn_labels[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP);
|
||||
btn_labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN);
|
||||
btn_labels[5] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_LEFT);
|
||||
btn_labels[6] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_RIGHT);
|
||||
|
||||
axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X);
|
||||
axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y);
|
||||
|
||||
InitPointerDeviceStruct(pDev, map, BUTTONS, btn_labels,
|
||||
(PtrCtrlProcPtr)NoopDDA,
|
||||
GetMotionHistorySize(),
|
||||
2, axes_labels);
|
||||
break;
|
||||
case DEVICE_ON:
|
||||
pDev->on = TRUE;
|
||||
break;
|
||||
case DEVICE_OFF:
|
||||
pDev->on = FALSE;
|
||||
break;
|
||||
case DEVICE_CLOSE:
|
||||
vncPointerDev = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
static void vncKeyboardBell(int percent, DeviceIntPtr device,
|
||||
void * ctrl, int class)
|
||||
{
|
||||
if (percent > 0)
|
||||
vncBell();
|
||||
}
|
||||
|
||||
static void vncKeyboardCtrl(DeviceIntPtr pDevice, KeybdCtrl *ctrl)
|
||||
{
|
||||
vncSetLEDState(ctrl->leds);
|
||||
}
|
||||
|
||||
static int vncKeyboardProc(DeviceIntPtr pDevice, int onoff)
|
||||
{
|
||||
DevicePtr pDev = (DevicePtr)pDevice;
|
||||
|
||||
switch (onoff) {
|
||||
case DEVICE_INIT:
|
||||
InitKeyboardDeviceStruct(pDevice, NULL, vncKeyboardBell,
|
||||
vncKeyboardCtrl);
|
||||
break;
|
||||
case DEVICE_ON:
|
||||
pDev->on = TRUE;
|
||||
break;
|
||||
case DEVICE_OFF:
|
||||
pDev->on = FALSE;
|
||||
break;
|
||||
case DEVICE_CLOSE:
|
||||
vncKeyboardDev = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
static inline void pressKey(DeviceIntPtr dev, int kc, Bool down, const char *msg)
|
||||
{
|
||||
int action;
|
||||
#if XORG < 111
|
||||
unsigned int n;
|
||||
#endif
|
||||
|
||||
if (msg != NULL)
|
||||
LOG_DEBUG("%s %d %s", msg, kc, down ? "down" : "up");
|
||||
|
||||
action = down ? KeyPress : KeyRelease;
|
||||
#if XORG < 111
|
||||
n = GetKeyboardEvents(eventq, dev, action, kc);
|
||||
enqueueEvents(dev, n);
|
||||
#elif XORG < 118
|
||||
QueueKeyboardEvents(dev, action, kc, NULL);
|
||||
#else
|
||||
QueueKeyboardEvents(dev, action, kc);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* vncKeyboardEvent() - add X11 events for the given RFB key event
|
||||
*/
|
||||
void vncKeyboardEvent(KeySym keysym, unsigned xtcode, int down)
|
||||
{
|
||||
/* Simple case: the client has specified the key */
|
||||
if (xtcode && xtcode < codeMapLen) {
|
||||
int keycode;
|
||||
|
||||
keycode = codeMap[xtcode];
|
||||
if (!keycode) {
|
||||
/*
|
||||
* Figure something out based on keysym if we
|
||||
* cannot find a mapping.
|
||||
*/
|
||||
if (keysym)
|
||||
vncKeysymKeyboardEvent(keysym, down);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We update the state table in case we get a mix of
|
||||
* events with and without key codes.
|
||||
*/
|
||||
if (down)
|
||||
pressedKeys[keycode] = keysym;
|
||||
else
|
||||
pressedKeys[keycode] = NoSymbol;
|
||||
|
||||
pressKey(vncKeyboardDev, keycode, down, "raw keycode");
|
||||
mieqProcessInputEvents();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Advanced case: We have to figure out a sequence of keys that
|
||||
* result in the given keysym
|
||||
*/
|
||||
if (keysym)
|
||||
vncKeysymKeyboardEvent(keysym, down);
|
||||
}
|
||||
|
||||
/* altKeysym is a table of alternative keysyms which have the same meaning. */
|
||||
|
||||
static struct altKeysym_t {
|
||||
KeySym a, b;
|
||||
} altKeysym[] = {
|
||||
{ XK_Shift_L, XK_Shift_R },
|
||||
{ XK_Control_L, XK_Control_R },
|
||||
{ XK_Meta_L, XK_Meta_R },
|
||||
{ XK_Alt_L, XK_Alt_R },
|
||||
{ XK_Super_L, XK_Super_R },
|
||||
{ XK_Hyper_L, XK_Hyper_R },
|
||||
{ XK_KP_Space, XK_space },
|
||||
{ XK_KP_Tab, XK_Tab },
|
||||
{ XK_KP_Enter, XK_Return },
|
||||
{ XK_KP_F1, XK_F1 },
|
||||
{ XK_KP_F2, XK_F2 },
|
||||
{ XK_KP_F3, XK_F3 },
|
||||
{ XK_KP_F4, XK_F4 },
|
||||
{ XK_KP_Home, XK_Home },
|
||||
{ XK_KP_Left, XK_Left },
|
||||
{ XK_KP_Up, XK_Up },
|
||||
{ XK_KP_Right, XK_Right },
|
||||
{ XK_KP_Down, XK_Down },
|
||||
{ XK_KP_Page_Up, XK_Page_Up },
|
||||
{ XK_KP_Page_Down, XK_Page_Down },
|
||||
{ XK_KP_End, XK_End },
|
||||
{ XK_KP_Begin, XK_Begin },
|
||||
{ XK_KP_Insert, XK_Insert },
|
||||
{ XK_KP_Delete, XK_Delete },
|
||||
{ XK_KP_Equal, XK_equal },
|
||||
{ XK_KP_Multiply, XK_asterisk },
|
||||
{ XK_KP_Add, XK_plus },
|
||||
{ XK_KP_Separator, XK_comma },
|
||||
{ XK_KP_Subtract, XK_minus },
|
||||
{ XK_KP_Decimal, XK_period },
|
||||
{ XK_KP_Divide, XK_slash },
|
||||
{ XK_KP_0, XK_0 },
|
||||
{ XK_KP_1, XK_1 },
|
||||
{ XK_KP_2, XK_2 },
|
||||
{ XK_KP_3, XK_3 },
|
||||
{ XK_KP_4, XK_4 },
|
||||
{ XK_KP_5, XK_5 },
|
||||
{ XK_KP_6, XK_6 },
|
||||
{ XK_KP_7, XK_7 },
|
||||
{ XK_KP_8, XK_8 },
|
||||
{ XK_KP_9, XK_9 },
|
||||
{ XK_ISO_Level3_Shift, XK_Mode_switch },
|
||||
};
|
||||
|
||||
/*
|
||||
* vncKeysymKeyboardEvent() - work out the best keycode corresponding
|
||||
* to the keysym sent by the viewer. This is basically impossible in
|
||||
* the general case, but we make a best effort by assuming that all
|
||||
* useful keysyms can be reached using just the Shift and
|
||||
* Level 3 (AltGr) modifiers. For core keyboards this is basically
|
||||
* always true, and should be true for most sane, western XKB layouts.
|
||||
*/
|
||||
static void vncKeysymKeyboardEvent(KeySym keysym, int down)
|
||||
{
|
||||
int i;
|
||||
unsigned state, new_state;
|
||||
KeyCode keycode;
|
||||
|
||||
unsigned level_three_mask;
|
||||
KeyCode shift_press, level_three_press;
|
||||
KeyCode shift_release[8], level_three_release[8];
|
||||
size_t shift_release_count, level_three_release_count;
|
||||
|
||||
/*
|
||||
* Release events must match the press event, so look up what
|
||||
* keycode we sent for the press.
|
||||
*/
|
||||
if (!down) {
|
||||
for (i = 0;i < 256;i++) {
|
||||
if (pressedKeys[i] == keysym) {
|
||||
pressedKeys[i] = NoSymbol;
|
||||
pressKey(vncKeyboardDev, i, FALSE, "keycode");
|
||||
mieqProcessInputEvents();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This can happen quite often as we ignore some
|
||||
* key presses.
|
||||
*/
|
||||
LOG_DEBUG("Unexpected release of keysym 0x%x", keysym);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since we are checking the current state to determine if we need
|
||||
* to fake modifiers, we must make sure that everything put on the
|
||||
* input queue is processed before we start. Otherwise, shift may be
|
||||
* stuck down.
|
||||
*/
|
||||
mieqProcessInputEvents();
|
||||
|
||||
state = vncGetKeyboardState();
|
||||
|
||||
keycode = vncKeysymToKeycode(keysym, state, &new_state);
|
||||
|
||||
/* Try some equivalent keysyms if we couldn't find a perfect match */
|
||||
if (keycode == 0) {
|
||||
for (i = 0;i < sizeof(altKeysym)/sizeof(altKeysym[0]);i++) {
|
||||
KeySym altsym;
|
||||
|
||||
if (altKeysym[i].a == keysym)
|
||||
altsym = altKeysym[i].b;
|
||||
else if (altKeysym[i].b == keysym)
|
||||
altsym = altKeysym[i].a;
|
||||
else
|
||||
continue;
|
||||
|
||||
keycode = vncKeysymToKeycode(altsym, state, &new_state);
|
||||
if (keycode != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* No matches. Will have to add a new entry... */
|
||||
if (keycode == 0) {
|
||||
keycode = vncAddKeysym(keysym, state);
|
||||
if (keycode == 0) {
|
||||
LOG_ERROR("Failure adding new keysym 0x%x", keysym);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Added unknown keysym 0x%x to keycode %d",
|
||||
keysym, keycode);
|
||||
|
||||
/*
|
||||
* The state given to addKeysym() is just a hint and
|
||||
* the actual result might still require some state
|
||||
* changes.
|
||||
*/
|
||||
keycode = vncKeysymToKeycode(keysym, state, &new_state);
|
||||
if (keycode == 0) {
|
||||
LOG_ERROR("Newly added keysym 0x%x cannot be generated", keysym);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* X11 generally lets shift toggle the keys on the numeric pad
|
||||
* the same way NumLock does. This is however not the case on
|
||||
* other systems like Windows. As a result, some applications
|
||||
* get confused when we do a fake shift to get the same effect
|
||||
* that having NumLock active would produce.
|
||||
*
|
||||
* Until we have proper NumLock synchronisation (so we can
|
||||
* avoid faking shift), we try to avoid the fake shifts if we
|
||||
* can use an alternative keysym.
|
||||
*/
|
||||
if (((state & ShiftMask) != (new_state & ShiftMask)) &&
|
||||
vncGetAvoidShiftNumLock() && vncIsAffectedByNumLock(keycode)) {
|
||||
KeyCode keycode2;
|
||||
unsigned new_state2;
|
||||
|
||||
LOG_DEBUG("Finding alternative to keysym 0x%x to avoid fake shift for numpad", keysym);
|
||||
|
||||
for (i = 0;i < sizeof(altKeysym)/sizeof(altKeysym[0]);i++) {
|
||||
KeySym altsym;
|
||||
|
||||
if (altKeysym[i].a == keysym)
|
||||
altsym = altKeysym[i].b;
|
||||
else if (altKeysym[i].b == keysym)
|
||||
altsym = altKeysym[i].a;
|
||||
else
|
||||
continue;
|
||||
|
||||
keycode2 = vncKeysymToKeycode(altsym, state, &new_state2);
|
||||
if (keycode2 == 0)
|
||||
continue;
|
||||
|
||||
if (((state & ShiftMask) != (new_state2 & ShiftMask)) &&
|
||||
vncIsAffectedByNumLock(keycode2))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == sizeof(altKeysym)/sizeof(altKeysym[0]))
|
||||
LOG_DEBUG("No alternative keysym found");
|
||||
else {
|
||||
keycode = keycode2;
|
||||
new_state = new_state2;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "Shifted Tab" is a bit of a mess. Some systems have varying,
|
||||
* special keysyms for this symbol. VNC mandates that clients
|
||||
* should always send the plain XK_Tab keysym and the server
|
||||
* should deduce the meaning based on current Shift state.
|
||||
* To comply with this, we will find the keycode that sends
|
||||
* XK_Tab, and make sure that Shift isn't cleared. This can
|
||||
* possibly result in a different keysym than XK_Tab, but that
|
||||
* is the desired behaviour.
|
||||
*
|
||||
* Note: We never get ISO_Left_Tab here because it's already
|
||||
* been translated in VNCSConnectionST.
|
||||
*/
|
||||
if (keysym == XK_Tab && (state & ShiftMask))
|
||||
new_state |= ShiftMask;
|
||||
|
||||
/*
|
||||
* We need a bigger state change than just shift,
|
||||
* so we need to know what the mask is for level 3 shifts.
|
||||
*/
|
||||
if ((new_state & ~ShiftMask) != (state & ~ShiftMask))
|
||||
level_three_mask = vncGetLevelThreeMask();
|
||||
else
|
||||
level_three_mask = 0;
|
||||
|
||||
shift_press = level_three_press = 0;
|
||||
shift_release_count = level_three_release_count = 0;
|
||||
|
||||
/* Need a fake press or release of shift? */
|
||||
if (!(state & ShiftMask) && (new_state & ShiftMask)) {
|
||||
shift_press = vncPressShift();
|
||||
if (shift_press == 0) {
|
||||
LOG_ERROR("Unable to find a modifier key for Shift");
|
||||
return;
|
||||
}
|
||||
|
||||
pressKey(vncKeyboardDev, shift_press, TRUE, "temp shift");
|
||||
} else if ((state & ShiftMask) && !(new_state & ShiftMask)) {
|
||||
shift_release_count = vncReleaseShift(shift_release,
|
||||
sizeof(shift_release)/sizeof(*shift_release));
|
||||
if (shift_release_count == 0) {
|
||||
LOG_ERROR("Unable to find the modifier key(s) for releasing Shift");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0;i < shift_release_count;i++)
|
||||
pressKey(vncKeyboardDev, shift_release[i], FALSE, "temp shift");
|
||||
}
|
||||
|
||||
/* Need a fake press or release of level three shift? */
|
||||
if (!(state & level_three_mask) && (new_state & level_three_mask)) {
|
||||
level_three_press = vncPressLevelThree();
|
||||
if (level_three_press == 0) {
|
||||
LOG_ERROR("Unable to find a modifier key for ISO_Level3_Shift/Mode_Switch");
|
||||
return;
|
||||
}
|
||||
|
||||
pressKey(vncKeyboardDev, level_three_press, TRUE, "temp level 3 shift");
|
||||
} else if ((state & level_three_mask) && !(new_state & level_three_mask)) {
|
||||
level_three_release_count = vncReleaseLevelThree(level_three_release,
|
||||
sizeof(level_three_release)/sizeof(*level_three_release));
|
||||
if (level_three_release_count == 0) {
|
||||
LOG_ERROR("Unable to find the modifier key(s) for releasing ISO_Level3_Shift/Mode_Switch");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0;i < level_three_release_count;i++)
|
||||
pressKey(vncKeyboardDev, level_three_release[i], FALSE, "temp level 3 shift");
|
||||
}
|
||||
|
||||
/* Now press the actual key */
|
||||
pressKey(vncKeyboardDev, keycode, TRUE, "keycode");
|
||||
|
||||
/* And store the mapping so that we can do a proper release later */
|
||||
for (i = 0;i < 256;i++) {
|
||||
if (i == keycode)
|
||||
continue;
|
||||
if (pressedKeys[i] == keysym) {
|
||||
LOG_ERROR("Keysym 0x%x generated by both keys %d and %d", keysym, i, keycode);
|
||||
pressedKeys[i] = NoSymbol;
|
||||
}
|
||||
}
|
||||
|
||||
pressedKeys[keycode] = keysym;
|
||||
|
||||
/* Undo any fake level three shift */
|
||||
if (level_three_press != 0)
|
||||
pressKey(vncKeyboardDev, level_three_press, FALSE, "temp level 3 shift");
|
||||
else if (level_three_release_count != 0) {
|
||||
for (i = 0;i < level_three_release_count;i++)
|
||||
pressKey(vncKeyboardDev, level_three_release[i], TRUE, "temp level 3 shift");
|
||||
}
|
||||
|
||||
/* Undo any fake shift */
|
||||
if (shift_press != 0)
|
||||
pressKey(vncKeyboardDev, shift_press, FALSE, "temp shift");
|
||||
else if (shift_release_count != 0) {
|
||||
for (i = 0;i < shift_release_count;i++)
|
||||
pressKey(vncKeyboardDev, shift_release[i], TRUE, "temp shift");
|
||||
}
|
||||
|
||||
/*
|
||||
* When faking a modifier we are putting a keycode (which can
|
||||
* currently activate the desired modifier) on the input
|
||||
* queue. A future modmap change can change the mapping so
|
||||
* that this keycode means something else entirely. Guard
|
||||
* against this by processing the queue now.
|
||||
*/
|
||||
mieqProcessInputEvents();
|
||||
}
|
64
unix/xserver/hw/vnc/Input.h
Normal file
64
unix/xserver/hw/vnc/Input.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* Copyright (C) 2009 TightVNC Team
|
||||
* Copyright (C) 2009, 2010 Red Hat, Inc.
|
||||
* Copyright (C) 2009, 2010 TigerVNC Team
|
||||
* Copyright 2013-2015 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.
|
||||
*/
|
||||
|
||||
/* Make sure macro doesn't conflict with macro in include/input.h. */
|
||||
#ifndef INPUT_H_
|
||||
#define INPUT_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <X11/X.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void vncInitInputDevice(void);
|
||||
|
||||
void vncPointerButtonAction(int buttonMask);
|
||||
void vncPointerMove(int x, int y);
|
||||
void vncGetPointerPos(int *x, int *y);
|
||||
|
||||
void vncKeyboardEvent(KeySym keysym, unsigned xtcode, int down);
|
||||
|
||||
/* Backend dependent functions below here */
|
||||
|
||||
void vncPrepareInputDevices(void);
|
||||
|
||||
unsigned vncGetKeyboardState(void);
|
||||
unsigned vncGetLevelThreeMask(void);
|
||||
|
||||
KeyCode vncPressShift(void);
|
||||
size_t vncReleaseShift(KeyCode *keys, size_t maxKeys);
|
||||
|
||||
KeyCode vncPressLevelThree(void);
|
||||
size_t vncReleaseLevelThree(KeyCode *keys, size_t maxKeys);
|
||||
|
||||
KeyCode vncKeysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state);
|
||||
|
||||
int vncIsAffectedByNumLock(KeyCode keycode);
|
||||
|
||||
KeyCode vncAddKeysym(KeySym keysym, unsigned state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
646
unix/xserver/hw/vnc/InputXKB.c
Normal file
646
unix/xserver/hw/vnc/InputXKB.c
Normal file
@ -0,0 +1,646 @@
|
||||
/* Copyright (C) 2009 TightVNC Team
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
* Copyright 2013-2015 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_DIX_CONFIG_H
|
||||
#include <dix-config.h>
|
||||
#endif
|
||||
|
||||
#include "xorg-version.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
#include "xkbsrv.h"
|
||||
#include "xkbstr.h"
|
||||
#include "eventstr.h"
|
||||
#include "scrnintstr.h"
|
||||
#include "mi.h"
|
||||
|
||||
#include "Input.h"
|
||||
|
||||
#ifndef KEYBOARD_OR_FLOAT
|
||||
#define KEYBOARD_OR_FLOAT MASTER_KEYBOARD
|
||||
#endif
|
||||
|
||||
#if XORG < 118
|
||||
#if XORG < 110
|
||||
#define GetMaster(dev, type) ((dev)->u.master)
|
||||
#else
|
||||
#define GetMaster(dev, type) ((dev)->master)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern DeviceIntPtr vncKeyboardDev;
|
||||
|
||||
static void vncXkbProcessDeviceEvent(int screenNum,
|
||||
InternalEvent *event,
|
||||
DeviceIntPtr dev);
|
||||
|
||||
/* Stolen from libX11 */
|
||||
static Bool
|
||||
XkbTranslateKeyCode(register XkbDescPtr xkb, KeyCode key,
|
||||
register unsigned int mods, unsigned int *mods_rtrn,
|
||||
KeySym *keysym_rtrn)
|
||||
{
|
||||
XkbKeyTypeRec *type;
|
||||
int col,nKeyGroups;
|
||||
unsigned preserve,effectiveGroup;
|
||||
KeySym *syms;
|
||||
|
||||
if (mods_rtrn!=NULL)
|
||||
*mods_rtrn = 0;
|
||||
|
||||
nKeyGroups= XkbKeyNumGroups(xkb,key);
|
||||
if ((!XkbKeycodeInRange(xkb,key))||(nKeyGroups==0)) {
|
||||
if (keysym_rtrn!=NULL)
|
||||
*keysym_rtrn = NoSymbol;
|
||||
return False;
|
||||
}
|
||||
|
||||
syms = XkbKeySymsPtr(xkb,key);
|
||||
|
||||
/* find the offset of the effective group */
|
||||
col = 0;
|
||||
effectiveGroup= XkbGroupForCoreState(mods);
|
||||
if ( effectiveGroup>=nKeyGroups ) {
|
||||
unsigned groupInfo= XkbKeyGroupInfo(xkb,key);
|
||||
switch (XkbOutOfRangeGroupAction(groupInfo)) {
|
||||
default:
|
||||
effectiveGroup %= nKeyGroups;
|
||||
break;
|
||||
case XkbClampIntoRange:
|
||||
effectiveGroup = nKeyGroups-1;
|
||||
break;
|
||||
case XkbRedirectIntoRange:
|
||||
effectiveGroup = XkbOutOfRangeGroupNumber(groupInfo);
|
||||
if (effectiveGroup>=nKeyGroups)
|
||||
effectiveGroup= 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
col= effectiveGroup*XkbKeyGroupsWidth(xkb,key);
|
||||
type = XkbKeyKeyType(xkb,key,effectiveGroup);
|
||||
|
||||
preserve= 0;
|
||||
if (type->map) { /* find the column (shift level) within the group */
|
||||
register int i;
|
||||
register XkbKTMapEntryPtr entry;
|
||||
for (i=0,entry=type->map;i<type->map_count;i++,entry++) {
|
||||
if ((entry->active)&&((mods&type->mods.mask)==entry->mods.mask)) {
|
||||
col+= entry->level;
|
||||
if (type->preserve)
|
||||
preserve= type->preserve[i].mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (keysym_rtrn!=NULL)
|
||||
*keysym_rtrn= syms[col];
|
||||
if (mods_rtrn)
|
||||
*mods_rtrn= type->mods.mask&(~preserve);
|
||||
|
||||
return (syms[col]!=NoSymbol);
|
||||
}
|
||||
|
||||
static XkbAction *XkbKeyActionPtr(XkbDescPtr xkb, KeyCode key, unsigned int mods)
|
||||
{
|
||||
XkbKeyTypeRec *type;
|
||||
int col,nKeyGroups;
|
||||
unsigned effectiveGroup;
|
||||
XkbAction *acts;
|
||||
|
||||
if (!XkbKeyHasActions(xkb, key))
|
||||
return NULL;
|
||||
|
||||
nKeyGroups= XkbKeyNumGroups(xkb,key);
|
||||
if ((!XkbKeycodeInRange(xkb,key))||(nKeyGroups==0))
|
||||
return NULL;
|
||||
|
||||
acts = XkbKeyActionsPtr(xkb,key);
|
||||
|
||||
/* find the offset of the effective group */
|
||||
col = 0;
|
||||
effectiveGroup= XkbGroupForCoreState(mods);
|
||||
if ( effectiveGroup>=nKeyGroups ) {
|
||||
unsigned groupInfo= XkbKeyGroupInfo(xkb,key);
|
||||
switch (XkbOutOfRangeGroupAction(groupInfo)) {
|
||||
default:
|
||||
effectiveGroup %= nKeyGroups;
|
||||
break;
|
||||
case XkbClampIntoRange:
|
||||
effectiveGroup = nKeyGroups-1;
|
||||
break;
|
||||
case XkbRedirectIntoRange:
|
||||
effectiveGroup = XkbOutOfRangeGroupNumber(groupInfo);
|
||||
if (effectiveGroup>=nKeyGroups)
|
||||
effectiveGroup= 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
col= effectiveGroup*XkbKeyGroupsWidth(xkb,key);
|
||||
type = XkbKeyKeyType(xkb,key,effectiveGroup);
|
||||
|
||||
if (type->map) { /* find the column (shift level) within the group */
|
||||
register int i;
|
||||
register XkbKTMapEntryPtr entry;
|
||||
for (i=0,entry=type->map;i<type->map_count;i++,entry++) {
|
||||
if ((entry->active)&&((mods&type->mods.mask)==entry->mods.mask)) {
|
||||
col+= entry->level;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &acts[col];
|
||||
}
|
||||
|
||||
static unsigned XkbKeyEffectiveGroup(XkbDescPtr xkb, KeyCode key, unsigned int mods)
|
||||
{
|
||||
int nKeyGroups;
|
||||
unsigned effectiveGroup;
|
||||
|
||||
nKeyGroups= XkbKeyNumGroups(xkb,key);
|
||||
if ((!XkbKeycodeInRange(xkb,key))||(nKeyGroups==0))
|
||||
return 0;
|
||||
|
||||
effectiveGroup= XkbGroupForCoreState(mods);
|
||||
if ( effectiveGroup>=nKeyGroups ) {
|
||||
unsigned groupInfo= XkbKeyGroupInfo(xkb,key);
|
||||
switch (XkbOutOfRangeGroupAction(groupInfo)) {
|
||||
default:
|
||||
effectiveGroup %= nKeyGroups;
|
||||
break;
|
||||
case XkbClampIntoRange:
|
||||
effectiveGroup = nKeyGroups-1;
|
||||
break;
|
||||
case XkbRedirectIntoRange:
|
||||
effectiveGroup = XkbOutOfRangeGroupNumber(groupInfo);
|
||||
if (effectiveGroup>=nKeyGroups)
|
||||
effectiveGroup= 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return effectiveGroup;
|
||||
}
|
||||
|
||||
void vncPrepareInputDevices(void)
|
||||
{
|
||||
/*
|
||||
* Not ideal since these callbacks do not stack, but it's the only
|
||||
* decent way we can reliably catch events for both the slave and
|
||||
* master device.
|
||||
*/
|
||||
mieqSetHandler(ET_KeyPress, vncXkbProcessDeviceEvent);
|
||||
mieqSetHandler(ET_KeyRelease, vncXkbProcessDeviceEvent);
|
||||
}
|
||||
|
||||
unsigned vncGetKeyboardState(void)
|
||||
{
|
||||
DeviceIntPtr master;
|
||||
|
||||
master = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT);
|
||||
return XkbStateFieldFromRec(&master->key->xkbInfo->state);
|
||||
}
|
||||
|
||||
unsigned vncGetLevelThreeMask(void)
|
||||
{
|
||||
unsigned state;
|
||||
KeyCode keycode;
|
||||
XkbDescPtr xkb;
|
||||
XkbAction *act;
|
||||
|
||||
/* Group state is still important */
|
||||
state = vncGetKeyboardState();
|
||||
state &= ~0xff;
|
||||
|
||||
keycode = vncKeysymToKeycode(XK_ISO_Level3_Shift, state, NULL);
|
||||
if (keycode == 0) {
|
||||
keycode = vncKeysymToKeycode(XK_Mode_switch, state, NULL);
|
||||
if (keycode == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
|
||||
|
||||
act = XkbKeyActionPtr(xkb, keycode, state);
|
||||
if (act == NULL)
|
||||
return 0;
|
||||
if (act->type != XkbSA_SetMods)
|
||||
return 0;
|
||||
|
||||
if (act->mods.flags & XkbSA_UseModMapMods)
|
||||
return xkb->map->modmap[keycode];
|
||||
else
|
||||
return act->mods.mask;
|
||||
}
|
||||
|
||||
KeyCode vncPressShift(void)
|
||||
{
|
||||
unsigned state;
|
||||
|
||||
XkbDescPtr xkb;
|
||||
unsigned int key;
|
||||
|
||||
state = vncGetKeyboardState();
|
||||
if (state & ShiftMask)
|
||||
return 0;
|
||||
|
||||
xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
|
||||
for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
|
||||
XkbAction *act;
|
||||
unsigned char mask;
|
||||
|
||||
act = XkbKeyActionPtr(xkb, key, state);
|
||||
if (act == NULL)
|
||||
continue;
|
||||
|
||||
if (act->type != XkbSA_SetMods)
|
||||
continue;
|
||||
|
||||
if (act->mods.flags & XkbSA_UseModMapMods)
|
||||
mask = xkb->map->modmap[key];
|
||||
else
|
||||
mask = act->mods.mask;
|
||||
|
||||
if ((mask & ShiftMask) == ShiftMask)
|
||||
return key;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t vncReleaseShift(KeyCode *keys, size_t maxKeys)
|
||||
{
|
||||
size_t count;
|
||||
|
||||
unsigned state;
|
||||
|
||||
DeviceIntPtr master;
|
||||
XkbDescPtr xkb;
|
||||
unsigned int key;
|
||||
|
||||
state = vncGetKeyboardState();
|
||||
if (!(state & ShiftMask))
|
||||
return 0;
|
||||
|
||||
count = 0;
|
||||
|
||||
master = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT);
|
||||
xkb = master->key->xkbInfo->desc;
|
||||
for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
|
||||
XkbAction *act;
|
||||
unsigned char mask;
|
||||
|
||||
if (!key_is_down(master, key, KEY_PROCESSED))
|
||||
continue;
|
||||
|
||||
act = XkbKeyActionPtr(xkb, key, state);
|
||||
if (act == NULL)
|
||||
continue;
|
||||
|
||||
if (act->type != XkbSA_SetMods)
|
||||
continue;
|
||||
|
||||
if (act->mods.flags & XkbSA_UseModMapMods)
|
||||
mask = xkb->map->modmap[key];
|
||||
else
|
||||
mask = act->mods.mask;
|
||||
|
||||
if (!(mask & ShiftMask))
|
||||
continue;
|
||||
|
||||
if (count >= maxKeys)
|
||||
return 0;
|
||||
|
||||
keys[count++] = key;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
KeyCode vncPressLevelThree(void)
|
||||
{
|
||||
unsigned state, mask;
|
||||
|
||||
KeyCode keycode;
|
||||
XkbDescPtr xkb;
|
||||
XkbAction *act;
|
||||
|
||||
mask = vncGetLevelThreeMask();
|
||||
if (mask == 0)
|
||||
return 0;
|
||||
|
||||
state = vncGetKeyboardState();
|
||||
if (state & mask)
|
||||
return 0;
|
||||
|
||||
keycode = vncKeysymToKeycode(XK_ISO_Level3_Shift, state, NULL);
|
||||
if (keycode == 0) {
|
||||
keycode = vncKeysymToKeycode(XK_Mode_switch, state, NULL);
|
||||
if (keycode == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
|
||||
|
||||
act = XkbKeyActionPtr(xkb, keycode, state);
|
||||
if (act == NULL)
|
||||
return 0;
|
||||
if (act->type != XkbSA_SetMods)
|
||||
return 0;
|
||||
|
||||
return keycode;
|
||||
}
|
||||
|
||||
size_t vncReleaseLevelThree(KeyCode *keys, size_t maxKeys)
|
||||
{
|
||||
size_t count;
|
||||
|
||||
unsigned state, mask;
|
||||
|
||||
DeviceIntPtr master;
|
||||
XkbDescPtr xkb;
|
||||
unsigned int key;
|
||||
|
||||
mask = vncGetLevelThreeMask();
|
||||
if (mask == 0)
|
||||
return 0;
|
||||
|
||||
state = vncGetKeyboardState();
|
||||
if (!(state & mask))
|
||||
return 0;
|
||||
|
||||
count = 0;
|
||||
|
||||
master = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT);
|
||||
xkb = master->key->xkbInfo->desc;
|
||||
for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
|
||||
XkbAction *act;
|
||||
unsigned char key_mask;
|
||||
|
||||
if (!key_is_down(master, key, KEY_PROCESSED))
|
||||
continue;
|
||||
|
||||
act = XkbKeyActionPtr(xkb, key, state);
|
||||
if (act == NULL)
|
||||
continue;
|
||||
|
||||
if (act->type != XkbSA_SetMods)
|
||||
continue;
|
||||
|
||||
if (act->mods.flags & XkbSA_UseModMapMods)
|
||||
key_mask = xkb->map->modmap[key];
|
||||
else
|
||||
key_mask = act->mods.mask;
|
||||
|
||||
if (!(key_mask & mask))
|
||||
continue;
|
||||
|
||||
if (count >= maxKeys)
|
||||
return 0;
|
||||
|
||||
keys[count++] = key;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
KeyCode vncKeysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state)
|
||||
{
|
||||
XkbDescPtr xkb;
|
||||
unsigned int key;
|
||||
KeySym ks;
|
||||
unsigned level_three_mask;
|
||||
|
||||
if (new_state != NULL)
|
||||
*new_state = state;
|
||||
|
||||
xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
|
||||
for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
|
||||
unsigned int state_out;
|
||||
KeySym dummy;
|
||||
|
||||
XkbTranslateKeyCode(xkb, key, state, &state_out, &ks);
|
||||
if (ks == NoSymbol)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Despite every known piece of documentation on
|
||||
* XkbTranslateKeyCode() stating that mods_rtrn returns
|
||||
* the unconsumed modifiers, in reality it always
|
||||
* returns the _potentially consumed_ modifiers.
|
||||
*/
|
||||
state_out = state & ~state_out;
|
||||
if (state_out & LockMask)
|
||||
XkbConvertCase(ks, &dummy, &ks);
|
||||
|
||||
if (ks == keysym)
|
||||
return key;
|
||||
}
|
||||
|
||||
if (new_state == NULL)
|
||||
return 0;
|
||||
|
||||
*new_state = (state & ~ShiftMask) |
|
||||
((state & ShiftMask) ? 0 : ShiftMask);
|
||||
key = vncKeysymToKeycode(keysym, *new_state, NULL);
|
||||
if (key != 0)
|
||||
return key;
|
||||
|
||||
level_three_mask = vncGetLevelThreeMask();
|
||||
if (level_three_mask == 0)
|
||||
return 0;
|
||||
|
||||
*new_state = (state & ~level_three_mask) |
|
||||
((state & level_three_mask) ? 0 : level_three_mask);
|
||||
key = vncKeysymToKeycode(keysym, *new_state, NULL);
|
||||
if (key != 0)
|
||||
return key;
|
||||
|
||||
*new_state = (state & ~(ShiftMask | level_three_mask)) |
|
||||
((state & ShiftMask) ? 0 : ShiftMask) |
|
||||
((state & level_three_mask) ? 0 : level_three_mask);
|
||||
key = vncKeysymToKeycode(keysym, *new_state, NULL);
|
||||
if (key != 0)
|
||||
return key;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vncIsAffectedByNumLock(KeyCode keycode)
|
||||
{
|
||||
unsigned state;
|
||||
|
||||
KeyCode numlock_keycode;
|
||||
unsigned numlock_mask;
|
||||
|
||||
XkbDescPtr xkb;
|
||||
XkbAction *act;
|
||||
|
||||
unsigned group;
|
||||
XkbKeyTypeRec *type;
|
||||
|
||||
/* Group state is still important */
|
||||
state = vncGetKeyboardState();
|
||||
state &= ~0xff;
|
||||
|
||||
/*
|
||||
* Not sure if hunting for a virtual modifier called "NumLock",
|
||||
* or following the keysym Num_Lock is the best approach. We
|
||||
* try the latter.
|
||||
*/
|
||||
numlock_keycode = vncKeysymToKeycode(XK_Num_Lock, state, NULL);
|
||||
if (numlock_keycode == 0)
|
||||
return 0;
|
||||
|
||||
xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
|
||||
|
||||
act = XkbKeyActionPtr(xkb, numlock_keycode, state);
|
||||
if (act == NULL)
|
||||
return 0;
|
||||
if (act->type != XkbSA_LockMods)
|
||||
return 0;
|
||||
|
||||
if (act->mods.flags & XkbSA_UseModMapMods)
|
||||
numlock_mask = xkb->map->modmap[keycode];
|
||||
else
|
||||
numlock_mask = act->mods.mask;
|
||||
|
||||
group = XkbKeyEffectiveGroup(xkb, keycode, state);
|
||||
type = XkbKeyKeyType(xkb, keycode, group);
|
||||
if ((type->mods.mask & numlock_mask) == 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
KeyCode vncAddKeysym(KeySym keysym, unsigned state)
|
||||
{
|
||||
DeviceIntPtr master;
|
||||
XkbDescPtr xkb;
|
||||
unsigned int key;
|
||||
|
||||
XkbEventCauseRec cause;
|
||||
XkbChangesRec changes;
|
||||
|
||||
int types[1];
|
||||
KeySym *syms;
|
||||
KeySym upper, lower;
|
||||
|
||||
master = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT);
|
||||
xkb = master->key->xkbInfo->desc;
|
||||
for (key = xkb->max_key_code; key >= xkb->min_key_code; key--) {
|
||||
if (XkbKeyNumGroups(xkb, key) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (key < xkb->min_key_code)
|
||||
return 0;
|
||||
|
||||
memset(&changes, 0, sizeof(changes));
|
||||
memset(&cause, 0, sizeof(cause));
|
||||
|
||||
XkbSetCauseUnknown(&cause);
|
||||
|
||||
/*
|
||||
* Tools like xkbcomp get confused if there isn't a name
|
||||
* assigned to the keycode we're trying to use.
|
||||
*/
|
||||
if (xkb->names && xkb->names->keys &&
|
||||
(xkb->names->keys[key].name[0] == '\0')) {
|
||||
xkb->names->keys[key].name[0] = 'I';
|
||||
xkb->names->keys[key].name[1] = '0' + (key / 100) % 10;
|
||||
xkb->names->keys[key].name[2] = '0' + (key / 10) % 10;
|
||||
xkb->names->keys[key].name[3] = '0' + (key / 1) % 10;
|
||||
|
||||
changes.names.changed |= XkbKeyNamesMask;
|
||||
changes.names.first_key = key;
|
||||
changes.names.num_keys = 1;
|
||||
}
|
||||
|
||||
/* FIXME: Verify that ONE_LEVEL/ALPHABETIC isn't screwed up */
|
||||
|
||||
/*
|
||||
* For keysyms that are affected by Lock, we are better off
|
||||
* using ALPHABETIC rather than ONE_LEVEL as the latter
|
||||
* generally cannot produce lower case when Lock is active.
|
||||
*/
|
||||
XkbConvertCase(keysym, &lower, &upper);
|
||||
if (upper == lower)
|
||||
types[XkbGroup1Index] = XkbOneLevelIndex;
|
||||
else
|
||||
types[XkbGroup1Index] = XkbAlphabeticIndex;
|
||||
|
||||
XkbChangeTypesOfKey(xkb, key, 1, XkbGroup1Mask, types, &changes.map);
|
||||
|
||||
syms = XkbKeySymsPtr(xkb,key);
|
||||
if (upper == lower)
|
||||
syms[0] = keysym;
|
||||
else {
|
||||
syms[0] = lower;
|
||||
syms[1] = upper;
|
||||
}
|
||||
|
||||
changes.map.changed |= XkbKeySymsMask;
|
||||
changes.map.first_key_sym = key;
|
||||
changes.map.num_key_syms = 1;
|
||||
|
||||
XkbSendNotification(master, &changes, &cause);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
static void vncXkbProcessDeviceEvent(int screenNum,
|
||||
InternalEvent *event,
|
||||
DeviceIntPtr dev)
|
||||
{
|
||||
unsigned int backupctrls;
|
||||
XkbControlsPtr ctrls;
|
||||
|
||||
if (event->device_event.sourceid != vncKeyboardDev->id) {
|
||||
dev->public.processInputProc(event, dev);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to bypass AccessX since it is timing sensitive and
|
||||
* the network can cause fake event delays.
|
||||
*/
|
||||
ctrls = dev->key->xkbInfo->desc->ctrls;
|
||||
backupctrls = ctrls->enabled_ctrls;
|
||||
ctrls->enabled_ctrls &= ~XkbAllFilteredEventsMask;
|
||||
|
||||
/*
|
||||
* This flag needs to be set for key repeats to be properly
|
||||
* respected.
|
||||
*/
|
||||
if ((event->device_event.type == ET_KeyPress) &&
|
||||
key_is_down(dev, event->device_event.detail.key, KEY_PROCESSED))
|
||||
event->device_event.key_repeat = TRUE;
|
||||
|
||||
dev->public.processInputProc(event, dev);
|
||||
|
||||
ctrls->enabled_ctrls = backupctrls;
|
||||
}
|
73
unix/xserver/hw/vnc/Makefile.am
Normal file
73
unix/xserver/hw/vnc/Makefile.am
Normal file
@ -0,0 +1,73 @@
|
||||
KASMVNC_SRCDIR=${top_srcdir}/../..
|
||||
KASMVNC_BUILDDIR=${KASMVNC_SRCDIR}
|
||||
|
||||
RFB_LIB=$(KASMVNC_BUILDDIR)/common/rfb/librfb.la
|
||||
RDR_LIB=$(KASMVNC_BUILDDIR)/common/rdr/librdr.la
|
||||
OS_LIB=$(KASMVNC_BUILDDIR)/common/os/libos.la
|
||||
NETWORK_LIB=$(KASMVNC_BUILDDIR)/common/network/libnetwork.la
|
||||
XREGION_LIB=$(KASMVNC_BUILDDIR)/common/Xregion/libXregion.la
|
||||
UNIXCOMMON_LIB=$(KASMVNC_BUILDDIR)/unix/common/libunixcommon.la
|
||||
COMMON_LIBS=$(NETWORK_LIB) $(RFB_LIB) $(RDR_LIB) $(XREGION_LIB) $(OS_LIB) $(UNIXCOMMON_LIB)
|
||||
|
||||
noinst_LTLIBRARIES = libvnccommon.la
|
||||
|
||||
HDRS = vncExtInit.h vncHooks.h \
|
||||
vncBlockHandler.h vncSelection.h \
|
||||
XorgGlue.h XserverDesktop.h xorg-version.h \
|
||||
Input.h RFBGlue.h
|
||||
|
||||
libvnccommon_la_SOURCES = $(HDRS) \
|
||||
vncExt.c vncExtInit.cc vncHooks.c vncSelection.c \
|
||||
vncBlockHandler.c XorgGlue.c RandrGlue.c RFBGlue.cc XserverDesktop.cc \
|
||||
Input.c InputXKB.c qnum_to_xorgevdev.c qnum_to_xorgkbd.c
|
||||
|
||||
libvnccommon_la_CPPFLAGS = -DVENDOR_RELEASE="$(VENDOR_RELEASE)" -I$(KASMVNC_SRCDIR)/unix/common \
|
||||
-DVENDOR_STRING="\"$(VENDOR_STRING)\"" -I$(KASMVNC_SRCDIR)/common -UHAVE_CONFIG_H \
|
||||
-I$(KASMVNC_SRCDIR)/unix/vncconfig $(XVNC_CPPFLAGS) ${XSERVERLIBS_CFLAGS} -I$(includedir) \
|
||||
-I$(top_srcdir)/include
|
||||
|
||||
bin_PROGRAMS = Xvnc
|
||||
|
||||
man1_MANS = Xvnc.man
|
||||
|
||||
Xvnc_SOURCES = xvnc.c \
|
||||
$(top_srcdir)/Xi/stubs.c $(top_srcdir)/mi/miinitext.c \
|
||||
$(top_srcdir)/fb/fbcmap_mi.c buildtime.c
|
||||
|
||||
# Xvnc contains no C++ sources so automake doesn't understand that we
|
||||
# need to use the C++ compiler to link things. This is the upstream
|
||||
# recommendation for coaxing automake.
|
||||
nodist_EXTRA_Xvnc_SOURCES = dummy.cxx
|
||||
|
||||
Xvnc_CPPFLAGS = $(XVNC_CPPFLAGS) -DKASMVNC -DNO_MODULE_EXTS \
|
||||
-UHAVE_CONFIG_H \
|
||||
-DXFree86Server -DVENDOR_RELEASE="$(VENDOR_RELEASE)" \
|
||||
-DVENDOR_STRING="\"$(VENDOR_STRING)\"" -I$(KASMVNC_SRCDIR)/common -I$(KASMVNC_SRCDIR)/unix/common \
|
||||
-I$(top_srcdir)/include ${XSERVERLIBS_CFLAGS} -I$(includedir)
|
||||
|
||||
Xvnc_LDADD = $(XVNC_LIBS) libvnccommon.la $(COMMON_LIBS) \
|
||||
$(XSERVER_LIBS) $(XSERVER_SYS_LIBS) $(XVNC_SYS_LIBS) -lX11 -lwebp -lssl -lcrypto
|
||||
|
||||
Xvnc_LDFLAGS = $(LD_EXPORT_SYMBOLS_FLAG) -fopenmp
|
||||
|
||||
libvnc_la_LTLIBRARIES = libvnc.la
|
||||
libvnc_ladir = $(moduledir)/extensions
|
||||
|
||||
libvnc_la_SOURCES = vncModule.c
|
||||
|
||||
# See Xvnc magic above
|
||||
nodist_EXTRA_libvnc_la_SOURCES = dummy.cxx
|
||||
|
||||
libvnc_la_CPPFLAGS = $(XVNC_CPPFLAGS) -I$(KASMVNC_SRCDIR)/common -UHAVE_CONFIG_H \
|
||||
-I$(KASMVNC_SRCDIR)/unix/common \
|
||||
-I$(top_srcdir)/hw/xfree86/common \
|
||||
-I$(top_srcdir)/hw/xfree86/os-support \
|
||||
-I$(top_srcdir)/hw/xfree86/os-support/bus \
|
||||
-I$(top_srcdir)/include \
|
||||
${XSERVERLIBS_CFLAGS} -I$(includedir)
|
||||
|
||||
libvnc_la_LDFLAGS = -module -avoid-version -Wl,-z,now
|
||||
|
||||
libvnc_la_LIBADD = libvnccommon.la $(COMMON_LIBS)
|
||||
|
||||
EXTRA_DIST = Xvnc.man
|
212
unix/xserver/hw/vnc/RFBGlue.cc
Normal file
212
unix/xserver/hw/vnc/RFBGlue.cc
Normal file
@ -0,0 +1,212 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011-2015 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.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <network/TcpSocket.h>
|
||||
#include <rfb/Configuration.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/Logger_stdio.h>
|
||||
#include <rfb/Logger_syslog.h>
|
||||
|
||||
#include "RFBGlue.h"
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
// Loggers used by C code must be created here
|
||||
static LogWriter inputLog("Input");
|
||||
static LogWriter selectionLog("Selection");
|
||||
|
||||
void vncInitRFB(void)
|
||||
{
|
||||
rfb::initStdIOLoggers();
|
||||
rfb::initSyslogLogger();
|
||||
rfb::LogWriter::setLogParams("*:stderr:30");
|
||||
rfb::Configuration::enableServerParams();
|
||||
}
|
||||
|
||||
void vncLogError(const char *name, const char *format, ...)
|
||||
{
|
||||
LogWriter *vlog;
|
||||
va_list ap;
|
||||
vlog = LogWriter::getLogWriter(name);
|
||||
if (vlog == NULL)
|
||||
return;
|
||||
va_start(ap, format);
|
||||
vlog->verror(format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void vncLogStatus(const char *name, const char *format, ...)
|
||||
{
|
||||
LogWriter *vlog;
|
||||
va_list ap;
|
||||
vlog = LogWriter::getLogWriter(name);
|
||||
if (vlog == NULL)
|
||||
return;
|
||||
va_start(ap, format);
|
||||
vlog->vstatus(format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void vncLogInfo(const char *name, const char *format, ...)
|
||||
{
|
||||
LogWriter *vlog;
|
||||
va_list ap;
|
||||
vlog = LogWriter::getLogWriter(name);
|
||||
if (vlog == NULL)
|
||||
return;
|
||||
va_start(ap, format);
|
||||
vlog->vinfo(format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void vncLogDebug(const char *name, const char *format, ...)
|
||||
{
|
||||
LogWriter *vlog;
|
||||
va_list ap;
|
||||
vlog = LogWriter::getLogWriter(name);
|
||||
if (vlog == NULL)
|
||||
return;
|
||||
va_start(ap, format);
|
||||
vlog->vdebug(format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
int vncSetParam(const char *name, const char *value)
|
||||
{
|
||||
if (value != NULL)
|
||||
return rfb::Configuration::setParam(name, value);
|
||||
else {
|
||||
VoidParameter *param;
|
||||
param = rfb::Configuration::getParam(name);
|
||||
if (param == NULL)
|
||||
return false;
|
||||
return param->setParam();
|
||||
}
|
||||
}
|
||||
|
||||
int vncSetParamSimple(const char *nameAndValue)
|
||||
{
|
||||
return rfb::Configuration::setParam(nameAndValue);
|
||||
}
|
||||
|
||||
char* vncGetParam(const char *name)
|
||||
{
|
||||
rfb::VoidParameter *param;
|
||||
char *value;
|
||||
char *ret;
|
||||
|
||||
// Hack to avoid exposing password!
|
||||
if (strcasecmp(name, "Password") == 0)
|
||||
return NULL;
|
||||
|
||||
param = rfb::Configuration::getParam(name);
|
||||
if (param == NULL)
|
||||
return NULL;
|
||||
|
||||
value = param->getValueStr();
|
||||
if (value == NULL)
|
||||
return NULL;
|
||||
|
||||
ret = strdup(value);
|
||||
|
||||
delete [] value;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* vncGetParamDesc(const char *name)
|
||||
{
|
||||
rfb::VoidParameter *param;
|
||||
|
||||
param = rfb::Configuration::getParam(name);
|
||||
if (param == NULL)
|
||||
return NULL;
|
||||
|
||||
return param->getDescription();
|
||||
}
|
||||
|
||||
int vncGetParamCount(void)
|
||||
{
|
||||
int count;
|
||||
|
||||
count = 0;
|
||||
for (ParameterIterator i; i.param; i.next())
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
char *vncGetParamList(void)
|
||||
{
|
||||
int len;
|
||||
char *data, *ptr;
|
||||
|
||||
len = 0;
|
||||
|
||||
for (ParameterIterator i; i.param; i.next()) {
|
||||
int l = strlen(i.param->getName());
|
||||
if (l <= 255)
|
||||
len += l + 1;
|
||||
}
|
||||
|
||||
data = (char*)malloc(len + 1);
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
|
||||
ptr = data;
|
||||
for (ParameterIterator i; i.param; i.next()) {
|
||||
int l = strlen(i.param->getName());
|
||||
if (l <= 255) {
|
||||
*ptr++ = l;
|
||||
memcpy(ptr, i.param->getName(), l);
|
||||
ptr += l;
|
||||
}
|
||||
}
|
||||
*ptr = '\0';
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void vncListParams(int width, int nameWidth)
|
||||
{
|
||||
rfb::Configuration::listParams(width, nameWidth);
|
||||
}
|
||||
|
||||
int vncGetSocketPort(int fd)
|
||||
{
|
||||
return network::getSockPort(fd);
|
||||
}
|
||||
|
||||
int vncIsTCPPortUsed(int port)
|
||||
{
|
||||
try {
|
||||
// Attempt to create TCPListeners on that port.
|
||||
std::list<network::SocketListener*> dummy;
|
||||
network::createTcpListeners (&dummy, 0, port);
|
||||
while (!dummy.empty()) {
|
||||
delete dummy.back();
|
||||
dummy.pop_back();
|
||||
}
|
||||
} catch (rdr::Exception& e) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
56
unix/xserver/hw/vnc/RFBGlue.h
Normal file
56
unix/xserver/hw/vnc/RFBGlue.h
Normal file
@ -0,0 +1,56 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011-2015 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_GLUE_H
|
||||
#define RFB_GLUE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void vncInitRFB(void);
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define __printf_attr(a, b) __attribute__((__format__ (__printf__, a, b)))
|
||||
#else
|
||||
# define __printf_attr(a, b)
|
||||
#endif // __GNUC__
|
||||
|
||||
void vncLogError(const char *name, const char *format, ...) __printf_attr(2, 3);
|
||||
void vncLogStatus(const char *name, const char *format, ...) __printf_attr(2, 3);
|
||||
void vncLogInfo(const char *name, const char *format, ...) __printf_attr(2, 3);
|
||||
void vncLogDebug(const char *name, const char *format, ...) __printf_attr(2, 3);
|
||||
|
||||
int vncSetParam(const char *name, const char *value);
|
||||
int vncSetParamSimple(const char *nameAndValue);
|
||||
char* vncGetParam(const char *name);
|
||||
const char* vncGetParamDesc(const char *name);
|
||||
|
||||
int vncGetParamCount(void);
|
||||
char *vncGetParamList(void);
|
||||
void vncListParams(int width, int nameWidth);
|
||||
|
||||
int vncGetSocketPort(int fd);
|
||||
int vncIsTCPPortUsed(int port);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
360
unix/xserver/hw/vnc/RandrGlue.c
Normal file
360
unix/xserver/hw/vnc/RandrGlue.c
Normal file
@ -0,0 +1,360 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011-2015 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_DIX_CONFIG_H
|
||||
#include <dix-config.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "scrnintstr.h"
|
||||
#include "randrstr.h"
|
||||
|
||||
#include "RandrGlue.h"
|
||||
#include "XorgGlue.h"
|
||||
|
||||
static int scrIdx;
|
||||
|
||||
void vncSetGlueContext(int screenIndex);
|
||||
|
||||
void vncSetGlueContext(int screenIndex)
|
||||
{
|
||||
scrIdx = screenIndex;
|
||||
}
|
||||
|
||||
int vncGetScreenWidth(void)
|
||||
{
|
||||
return screenInfo.screens[scrIdx]->width;
|
||||
}
|
||||
|
||||
int vncGetScreenHeight(void)
|
||||
{
|
||||
return screenInfo.screens[scrIdx]->height;
|
||||
}
|
||||
|
||||
int vncRandRIsValidScreenSize(int width, int height)
|
||||
{
|
||||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
|
||||
|
||||
if (width < rp->minWidth || rp->maxWidth < width)
|
||||
return 0;
|
||||
if (height < rp->minHeight || rp->maxHeight < height)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vncRandRResizeScreen(int width, int height)
|
||||
{
|
||||
ScreenPtr pScreen = screenInfo.screens[scrIdx];
|
||||
|
||||
/* Try to retain DPI when we resize */
|
||||
return RRScreenSizeSet(pScreen, width, height,
|
||||
pScreen->mmWidth * width / pScreen->width,
|
||||
pScreen->mmHeight * height / pScreen->height);
|
||||
}
|
||||
|
||||
void vncRandRUpdateSetTime(void)
|
||||
{
|
||||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
|
||||
rp->lastSetTime = currentTime;
|
||||
}
|
||||
|
||||
int vncRandRHasOutputClones(void)
|
||||
{
|
||||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
|
||||
for (int i = 0;i < rp->numCrtcs;i++) {
|
||||
if (rp->crtcs[i]->numOutputs > 1)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vncRandRGetOutputCount(void)
|
||||
{
|
||||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
|
||||
return rp->numOutputs;
|
||||
}
|
||||
|
||||
int vncRandRGetAvailableOutputs(void)
|
||||
{
|
||||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
|
||||
|
||||
int availableOutputs;
|
||||
RRCrtcPtr *usedCrtcs;
|
||||
int numUsed;
|
||||
|
||||
int i, j, k;
|
||||
|
||||
usedCrtcs = malloc(sizeof(RRCrtcPtr) * rp->numCrtcs);
|
||||
if (usedCrtcs == NULL)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* This gets slightly complicated because we might need to hook a CRTC
|
||||
* up to the output, but also check that we don't try to use the same
|
||||
* CRTC for multiple outputs.
|
||||
*/
|
||||
availableOutputs = 0;
|
||||
numUsed = 0;
|
||||
for (i = 0;i < rp->numOutputs;i++) {
|
||||
RROutputPtr output;
|
||||
|
||||
output = rp->outputs[i];
|
||||
|
||||
if (output->crtc != NULL)
|
||||
availableOutputs++;
|
||||
else {
|
||||
for (j = 0;j < output->numCrtcs;j++) {
|
||||
if (output->crtcs[j]->numOutputs != 0)
|
||||
continue;
|
||||
|
||||
for (k = 0;k < numUsed;k++) {
|
||||
if (usedCrtcs[k] == output->crtcs[j])
|
||||
break;
|
||||
}
|
||||
if (k != numUsed)
|
||||
continue;
|
||||
|
||||
availableOutputs++;
|
||||
|
||||
usedCrtcs[numUsed] = output->crtcs[j];
|
||||
numUsed++;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(usedCrtcs);
|
||||
|
||||
return availableOutputs;
|
||||
}
|
||||
|
||||
char *vncRandRGetOutputName(int outputIdx)
|
||||
{
|
||||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
|
||||
return strdup(rp->outputs[outputIdx]->name);
|
||||
}
|
||||
|
||||
int vncRandRIsOutputEnabled(int outputIdx)
|
||||
{
|
||||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
|
||||
|
||||
if (rp->outputs[outputIdx]->crtc == NULL)
|
||||
return 0;
|
||||
if (rp->outputs[outputIdx]->crtc->mode == NULL)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int vncRandRIsOutputUsable(int outputIdx)
|
||||
{
|
||||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
|
||||
|
||||
RROutputPtr output;
|
||||
int i;
|
||||
|
||||
output = rp->outputs[outputIdx];
|
||||
if (output->crtc != NULL)
|
||||
return 1;
|
||||
|
||||
/* Any unused CRTCs? */
|
||||
for (i = 0;i < output->numCrtcs;i++) {
|
||||
if (output->crtcs[i]->numOutputs == 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vncRandRIsOutputConnected(int outputIdx)
|
||||
{
|
||||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
|
||||
|
||||
RROutputPtr output;
|
||||
|
||||
output = rp->outputs[outputIdx];
|
||||
return (output->connection == RR_Connected);
|
||||
}
|
||||
|
||||
static RRModePtr vncRandRGetMatchingMode(int outputIdx, int width, int height)
|
||||
{
|
||||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
|
||||
|
||||
RROutputPtr output;
|
||||
|
||||
output = rp->outputs[outputIdx];
|
||||
|
||||
if (output->crtc != NULL) {
|
||||
unsigned int swap;
|
||||
switch (output->crtc->rotation) {
|
||||
case RR_Rotate_90:
|
||||
case RR_Rotate_270:
|
||||
swap = width;
|
||||
width = height;
|
||||
height = swap;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < output->numModes; i++) {
|
||||
if ((output->modes[i]->mode.width == width) &&
|
||||
(output->modes[i]->mode.height == height))
|
||||
return output->modes[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int vncRandRCheckOutputMode(int outputIdx, int width, int height)
|
||||
{
|
||||
if (vncRandRGetMatchingMode(outputIdx, width, height) != NULL)
|
||||
return 1;
|
||||
if (vncRandRCanCreateModes())
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vncRandRDisableOutput(int outputIdx)
|
||||
{
|
||||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
|
||||
RRCrtcPtr crtc;
|
||||
int i;
|
||||
RROutputPtr *outputs;
|
||||
int numOutputs = 0;
|
||||
RRModePtr mode;
|
||||
int ret;
|
||||
|
||||
crtc = rp->outputs[outputIdx]->crtc;
|
||||
if (crtc == NULL)
|
||||
return 1;
|
||||
|
||||
/* Remove this output from the CRTC configuration */
|
||||
outputs = malloc(crtc->numOutputs * sizeof(RROutputPtr));
|
||||
if (!outputs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < crtc->numOutputs; i++) {
|
||||
if (rp->outputs[outputIdx] != crtc->outputs[i]) {
|
||||
outputs[numOutputs++] = crtc->outputs[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (numOutputs == 0) {
|
||||
mode = NULL;
|
||||
} else {
|
||||
mode = crtc->mode;
|
||||
}
|
||||
|
||||
ret = RRCrtcSet(crtc, mode, crtc->x, crtc->y, crtc->rotation, numOutputs, outputs);
|
||||
free(outputs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int vncRandRGetOutputId(int outputIdx)
|
||||
{
|
||||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
|
||||
return rp->outputs[outputIdx]->id;
|
||||
}
|
||||
|
||||
int vncRandRGetOutputDimensions(int outputIdx,
|
||||
int *x, int *y, int *width, int *height)
|
||||
{
|
||||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
|
||||
RRCrtcPtr crtc;
|
||||
int swap;
|
||||
*x = *y = *width = *height = 0;
|
||||
|
||||
crtc = rp->outputs[outputIdx]->crtc;
|
||||
if (crtc == NULL || !crtc->mode)
|
||||
return 1;
|
||||
|
||||
*x = crtc->x;
|
||||
*y = crtc->y;
|
||||
*width = crtc->mode->mode.width;
|
||||
*height = crtc->mode->mode.height;
|
||||
|
||||
switch (crtc->rotation & 0xf) {
|
||||
case RR_Rotate_90:
|
||||
case RR_Rotate_270:
|
||||
swap = *width;
|
||||
*width = *height;
|
||||
*height = swap;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vncRandRReconfigureOutput(int outputIdx, int x, int y,
|
||||
int width, int height)
|
||||
{
|
||||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]);
|
||||
|
||||
RROutputPtr output;
|
||||
RRCrtcPtr crtc;
|
||||
RRModePtr mode;
|
||||
|
||||
int i;
|
||||
|
||||
output = rp->outputs[outputIdx];
|
||||
crtc = output->crtc;
|
||||
|
||||
/* Need a CRTC? */
|
||||
if (crtc == NULL) {
|
||||
for (i = 0;i < output->numCrtcs;i++) {
|
||||
if (output->crtcs[i]->numOutputs != 0)
|
||||
continue;
|
||||
|
||||
crtc = output->crtcs[i];
|
||||
break;
|
||||
}
|
||||
|
||||
/* Couldn't find one... */
|
||||
if (crtc == NULL)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Make sure we have the mode we want */
|
||||
mode = vncRandRGetMatchingMode(outputIdx, width, height);
|
||||
if (mode == NULL) {
|
||||
mode = vncRandRCreateMode(output, width, height);
|
||||
if (mode == NULL)
|
||||
return 0;
|
||||
}
|
||||
mode = vncRandRSetPreferredMode(output, mode);
|
||||
if (mode == NULL)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure new mode and position */
|
||||
return RRCrtcSet(crtc, mode, x, y, crtc->rotation, 1, &output);
|
||||
}
|
||||
|
||||
int vncRandRCanCreateOutputs(int extraOutputs)
|
||||
{
|
||||
return vncRandRCanCreateScreenOutputs(scrIdx, extraOutputs);
|
||||
}
|
||||
|
||||
int vncRandRCreateOutputs(int extraOutputs)
|
||||
{
|
||||
return vncRandRCreateScreenOutputs(scrIdx, extraOutputs);
|
||||
}
|
116
unix/xserver/hw/vnc/XorgGlue.c
Normal file
116
unix/xserver/hw/vnc/XorgGlue.c
Normal file
@ -0,0 +1,116 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011-2015 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_DIX_CONFIG_H
|
||||
#include <dix-config.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "scrnintstr.h"
|
||||
#ifdef RANDR
|
||||
#include "randrstr.h"
|
||||
#endif
|
||||
|
||||
#include "XorgGlue.h"
|
||||
|
||||
const char *vncGetDisplay(void)
|
||||
{
|
||||
return display;
|
||||
}
|
||||
|
||||
unsigned long vncGetServerGeneration(void)
|
||||
{
|
||||
return serverGeneration;
|
||||
}
|
||||
|
||||
void vncFatalError(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
char buffer[4096];
|
||||
|
||||
va_start(args, format);
|
||||
vsnprintf(buffer, sizeof(buffer), format, args);
|
||||
va_end(args);
|
||||
|
||||
FatalError("%s", buffer);
|
||||
}
|
||||
|
||||
int vncGetScreenCount(void)
|
||||
{
|
||||
return screenInfo.numScreens;
|
||||
}
|
||||
|
||||
void vncGetScreenFormat(int scrIdx, int *depth, int *bpp,
|
||||
int *trueColour, int *bigEndian,
|
||||
int *redMask, int *greenMask, int *blueMask)
|
||||
{
|
||||
int i;
|
||||
VisualPtr vis = NULL;
|
||||
|
||||
assert(depth);
|
||||
assert(bpp);
|
||||
assert(trueColour);
|
||||
assert(bigEndian);
|
||||
assert(redMask);
|
||||
assert(greenMask);
|
||||
assert(blueMask);
|
||||
|
||||
*depth = screenInfo.screens[scrIdx]->rootDepth;
|
||||
|
||||
for (i = 0; i < screenInfo.numPixmapFormats; i++) {
|
||||
if (screenInfo.formats[i].depth == *depth) {
|
||||
*bpp = screenInfo.formats[i].bitsPerPixel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == screenInfo.numPixmapFormats)
|
||||
FatalError("No pixmap format for root depth\n");
|
||||
|
||||
*bigEndian = (screenInfo.imageByteOrder == MSBFirst);
|
||||
|
||||
for (i = 0; i < screenInfo.screens[scrIdx]->numVisuals; i++) {
|
||||
if (screenInfo.screens[scrIdx]->visuals[i].vid ==
|
||||
screenInfo.screens[scrIdx]->rootVisual) {
|
||||
vis = &screenInfo.screens[scrIdx]->visuals[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == screenInfo.screens[scrIdx]->numVisuals)
|
||||
FatalError("No visual record for root visual\n");
|
||||
|
||||
*trueColour = (vis->class == TrueColor);
|
||||
|
||||
*redMask = vis->redMask;
|
||||
*greenMask = vis->greenMask;
|
||||
*blueMask = vis->blueMask;
|
||||
}
|
||||
|
||||
int vncGetScreenX(int scrIdx)
|
||||
{
|
||||
return screenInfo.screens[scrIdx]->x;
|
||||
}
|
||||
|
||||
int vncGetScreenY(int scrIdx)
|
||||
{
|
||||
return screenInfo.screens[scrIdx]->y;
|
||||
}
|
||||
|
61
unix/xserver/hw/vnc/XorgGlue.h
Normal file
61
unix/xserver/hw/vnc/XorgGlue.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011-2015 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 XORG_GLUE_H
|
||||
#define XORG_GLUE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define __printf_attr(a, b) __attribute__((__format__ (__printf__, a, b)))
|
||||
# define __noreturn_attr __attribute__((noreturn))
|
||||
#else
|
||||
# define __printf_attr(a, b)
|
||||
# define __noreturn_attr
|
||||
#endif // __GNUC__
|
||||
|
||||
const char *vncGetDisplay(void);
|
||||
unsigned long vncGetServerGeneration(void);
|
||||
|
||||
void vncFatalError(const char *format, ...) __printf_attr(1, 2) __noreturn_attr;
|
||||
|
||||
int vncGetScreenCount(void);
|
||||
|
||||
void vncGetScreenFormat(int scrIdx, int *depth, int *bpp,
|
||||
int *trueColour, int *bigEndian,
|
||||
int *redMask, int *greenMask, int *blueMask);
|
||||
|
||||
int vncGetScreenX(int scrIdx);
|
||||
int vncGetScreenY(int scrIdx);
|
||||
|
||||
// These hide in xvnc.c or vncModule.c
|
||||
void vncClientGone(int fd);
|
||||
int vncRandRCanCreateScreenOutputs(int scrIdx, int extraOutputs);
|
||||
int vncRandRCreateScreenOutputs(int scrIdx, int extraOutputs);
|
||||
int vncRandRCanCreateModes(void);
|
||||
void* vncRandRCreateMode(void* output, int width, int height);
|
||||
void* vncRandRSetPreferredMode(void* output, void* mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
606
unix/xserver/hw/vnc/XserverDesktop.cc
Normal file
606
unix/xserver/hw/vnc/XserverDesktop.cc
Normal file
@ -0,0 +1,606 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2009-2017 Pierre Ossman for Cendio AB
|
||||
* Copyright 2014 Brian P. Hinz
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
//
|
||||
// XserverDesktop.cxx
|
||||
//
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include <network/Socket.h>
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/VNCServerST.h>
|
||||
#include <rfb/HTTPServer.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/Configuration.h>
|
||||
#include <rfb/ServerCore.h>
|
||||
|
||||
#include "XserverDesktop.h"
|
||||
#include "vncBlockHandler.h"
|
||||
#include "vncExtInit.h"
|
||||
#include "vncHooks.h"
|
||||
#include "vncSelection.h"
|
||||
#include "XorgGlue.h"
|
||||
#include "Input.h"
|
||||
|
||||
extern "C" {
|
||||
void vncSetGlueContext(int screenIndex);
|
||||
}
|
||||
|
||||
using namespace rfb;
|
||||
using namespace network;
|
||||
|
||||
static LogWriter vlog("XserverDesktop");
|
||||
|
||||
BoolParameter rawKeyboard("RawKeyboard",
|
||||
"Send keyboard events straight through and "
|
||||
"avoid mapping them to the current keyboard "
|
||||
"layout", false);
|
||||
IntParameter queryConnectTimeout("QueryConnectTimeout",
|
||||
"Number of seconds to show the "
|
||||
"Accept Connection dialog before "
|
||||
"rejecting the connection",
|
||||
10);
|
||||
|
||||
class FileHTTPServer : public rfb::HTTPServer {
|
||||
public:
|
||||
FileHTTPServer(XserverDesktop* d) : desktop(d) {}
|
||||
virtual ~FileHTTPServer() {}
|
||||
|
||||
virtual rdr::InStream* getFile(const char* name, const char** contentType,
|
||||
int* contentLength, time_t* lastModified)
|
||||
{
|
||||
if (name[0] != '/' || strstr(name, "..") != 0) {
|
||||
vlog.info("http request was for invalid file name");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(name, "/") == 0) name = "/index.vnc";
|
||||
|
||||
CharArray httpDirStr(httpDir.getData());
|
||||
CharArray fname(strlen(httpDirStr.buf)+strlen(name)+1);
|
||||
sprintf(fname.buf, "%s%s", httpDirStr.buf, name);
|
||||
int fd = open(fname.buf, O_RDONLY);
|
||||
if (fd < 0) return 0;
|
||||
rdr::InStream* is = new rdr::FdInStream(fd, -1, 0, true);
|
||||
*contentType = guessContentType(name, *contentType);
|
||||
if (strlen(name) > 4 && strcasecmp(&name[strlen(name)-4], ".vnc") == 0) {
|
||||
is = new rdr::SubstitutingInStream(is, desktop, 20);
|
||||
*contentType = "text/html";
|
||||
} else {
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == 0) {
|
||||
*contentLength = st.st_size;
|
||||
*lastModified = st.st_mtime;
|
||||
}
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
XserverDesktop* desktop;
|
||||
};
|
||||
|
||||
|
||||
XserverDesktop::XserverDesktop(int screenIndex_,
|
||||
std::list<network::SocketListener*> listeners_,
|
||||
std::list<network::SocketListener*> httpListeners_,
|
||||
const char* name, const rfb::PixelFormat &pf,
|
||||
int width, int height,
|
||||
void* fbptr, int stride)
|
||||
: screenIndex(screenIndex_),
|
||||
server(0), httpServer(0),
|
||||
listeners(listeners_), httpListeners(httpListeners_),
|
||||
directFbptr(true),
|
||||
queryConnectId(0), queryConnectTimer(this)
|
||||
{
|
||||
format = pf;
|
||||
|
||||
server = new VNCServerST(name, this);
|
||||
setFramebuffer(width, height, fbptr, stride);
|
||||
server->setQueryConnectionHandler(this);
|
||||
|
||||
if (!httpListeners.empty ())
|
||||
httpServer = new FileHTTPServer(this);
|
||||
|
||||
for (std::list<SocketListener*>::iterator i = listeners.begin();
|
||||
i != listeners.end();
|
||||
i++) {
|
||||
vncSetNotifyFd((*i)->getFd(), screenIndex, true, false);
|
||||
}
|
||||
|
||||
for (std::list<SocketListener*>::iterator i = httpListeners.begin();
|
||||
i != httpListeners.end();
|
||||
i++) {
|
||||
vncSetNotifyFd((*i)->getFd(), screenIndex, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
XserverDesktop::~XserverDesktop()
|
||||
{
|
||||
while (!listeners.empty()) {
|
||||
vncRemoveNotifyFd(listeners.back()->getFd());
|
||||
delete listeners.back();
|
||||
listeners.pop_back();
|
||||
}
|
||||
while (!httpListeners.empty()) {
|
||||
vncRemoveNotifyFd(listeners.back()->getFd());
|
||||
delete httpListeners.back();
|
||||
httpListeners.pop_back();
|
||||
}
|
||||
if (!directFbptr)
|
||||
delete [] data;
|
||||
delete httpServer;
|
||||
delete server;
|
||||
}
|
||||
|
||||
void XserverDesktop::blockUpdates()
|
||||
{
|
||||
server->blockUpdates();
|
||||
}
|
||||
|
||||
void XserverDesktop::unblockUpdates()
|
||||
{
|
||||
server->unblockUpdates();
|
||||
}
|
||||
|
||||
void XserverDesktop::setFramebuffer(int w, int h, void* fbptr, int stride_)
|
||||
{
|
||||
ScreenSet layout;
|
||||
|
||||
width_ = w;
|
||||
height_ = h;
|
||||
|
||||
if (!directFbptr) {
|
||||
delete [] data;
|
||||
directFbptr = true;
|
||||
}
|
||||
|
||||
if (!fbptr) {
|
||||
fbptr = new rdr::U8[w * h * (format.bpp/8)];
|
||||
stride_ = w;
|
||||
directFbptr = false;
|
||||
}
|
||||
|
||||
data = (rdr::U8*)fbptr;
|
||||
stride = stride_;
|
||||
|
||||
vncSetGlueContext(screenIndex);
|
||||
layout = ::computeScreenLayout(&outputIdMap);
|
||||
|
||||
server->setPixelBuffer(this, layout);
|
||||
}
|
||||
|
||||
void XserverDesktop::refreshScreenLayout()
|
||||
{
|
||||
vncSetGlueContext(screenIndex);
|
||||
server->setScreenLayout(::computeScreenLayout(&outputIdMap));
|
||||
}
|
||||
|
||||
char* XserverDesktop::substitute(const char* varName)
|
||||
{
|
||||
if (strcmp(varName, "$$") == 0) {
|
||||
return rfb::strDup("$");
|
||||
}
|
||||
if (strcmp(varName, "$PORT") == 0) {
|
||||
char* str = new char[10];
|
||||
sprintf(str, "%d", listeners.empty () ? 0 : (*listeners.begin ())->getMyPort());
|
||||
return str;
|
||||
}
|
||||
if (strcmp(varName, "$WIDTH") == 0) {
|
||||
char* str = new char[10];
|
||||
sprintf(str, "%d", width());
|
||||
return str;
|
||||
}
|
||||
if (strcmp(varName, "$HEIGHT") == 0) {
|
||||
char* str = new char[10];
|
||||
sprintf(str, "%d", height());
|
||||
return str;
|
||||
}
|
||||
if (strcmp(varName, "$APPLETWIDTH") == 0) {
|
||||
char* str = new char[10];
|
||||
sprintf(str, "%d", width());
|
||||
return str;
|
||||
}
|
||||
if (strcmp(varName, "$APPLETHEIGHT") == 0) {
|
||||
char* str = new char[10];
|
||||
sprintf(str, "%d", height());
|
||||
return str;
|
||||
}
|
||||
if (strcmp(varName, "$DESKTOP") == 0) {
|
||||
return rfb::strDup(server->getName());
|
||||
}
|
||||
if (strcmp(varName, "$DISPLAY") == 0) {
|
||||
struct utsname uts;
|
||||
uname(&uts);
|
||||
char* str = new char[256];
|
||||
strncpy(str, uts.nodename, 240);
|
||||
str[239] = '\0'; /* Ensure string is zero-terminated */
|
||||
strcat(str, ":");
|
||||
strncat(str, vncGetDisplay(), 10);
|
||||
return str;
|
||||
}
|
||||
if (strcmp(varName, "$USER") == 0) {
|
||||
struct passwd* user = getpwuid(getuid());
|
||||
return rfb::strDup(user ? user->pw_name : "?");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
rfb::VNCServerST::queryResult
|
||||
XserverDesktop::queryConnection(network::Socket* sock,
|
||||
const char* userName,
|
||||
char** reason)
|
||||
{
|
||||
int count;
|
||||
|
||||
if (queryConnectTimer.isStarted()) {
|
||||
*reason = strDup("Another connection is currently being queried.");
|
||||
return rfb::VNCServerST::REJECT;
|
||||
}
|
||||
|
||||
count = vncNotifyQueryConnect();
|
||||
if (count == 0) {
|
||||
*reason = strDup("Unable to query the local user to accept the connection.");
|
||||
return rfb::VNCServerST::REJECT;
|
||||
}
|
||||
|
||||
queryConnectAddress.replaceBuf(sock->getPeerAddress());
|
||||
if (!userName)
|
||||
userName = "(anonymous)";
|
||||
queryConnectUsername.replaceBuf(strDup(userName));
|
||||
queryConnectId = (uint32_t)(intptr_t)sock;
|
||||
queryConnectSocket = sock;
|
||||
|
||||
queryConnectTimer.start(queryConnectTimeout * 1000);
|
||||
|
||||
return rfb::VNCServerST::PENDING;
|
||||
}
|
||||
|
||||
void XserverDesktop::bell()
|
||||
{
|
||||
server->bell();
|
||||
}
|
||||
|
||||
void XserverDesktop::setLEDState(unsigned int state)
|
||||
{
|
||||
server->setLEDState(state);
|
||||
}
|
||||
|
||||
void XserverDesktop::serverCutText(const char* str, int len)
|
||||
{
|
||||
try {
|
||||
server->serverCutText(str, len);
|
||||
} catch (rdr::Exception& e) {
|
||||
vlog.error("XserverDesktop::serverCutText: %s",e.str());
|
||||
}
|
||||
}
|
||||
|
||||
void XserverDesktop::setDesktopName(const char* name)
|
||||
{
|
||||
try {
|
||||
server->setName(name);
|
||||
} catch (rdr::Exception& e) {
|
||||
vlog.error("XserverDesktop::setDesktopName: %s",e.str());
|
||||
}
|
||||
}
|
||||
|
||||
void XserverDesktop::setCursor(int width, int height, int hotX, int hotY,
|
||||
const unsigned char *rgbaData)
|
||||
{
|
||||
rdr::U8* cursorData;
|
||||
|
||||
rdr::U8 *out;
|
||||
const unsigned char *in;
|
||||
|
||||
cursorData = new rdr::U8[width * height * 4];
|
||||
|
||||
// Un-premultiply alpha
|
||||
in = rgbaData;
|
||||
out = cursorData;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
rdr::U8 alpha;
|
||||
|
||||
alpha = in[3];
|
||||
if (alpha == 0)
|
||||
alpha = 1; // Avoid division by zero
|
||||
|
||||
*out++ = (unsigned)*in++ * 255/alpha;
|
||||
*out++ = (unsigned)*in++ * 255/alpha;
|
||||
*out++ = (unsigned)*in++ * 255/alpha;
|
||||
*out++ = *in++;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
server->setCursor(width, height, Point(hotX, hotY), cursorData);
|
||||
} catch (rdr::Exception& e) {
|
||||
vlog.error("XserverDesktop::setCursor: %s",e.str());
|
||||
}
|
||||
|
||||
delete [] cursorData;
|
||||
}
|
||||
|
||||
void XserverDesktop::add_changed(const rfb::Region ®ion)
|
||||
{
|
||||
try {
|
||||
server->add_changed(region);
|
||||
} catch (rdr::Exception& e) {
|
||||
vlog.error("XserverDesktop::add_changed: %s",e.str());
|
||||
}
|
||||
}
|
||||
|
||||
void XserverDesktop::add_copied(const rfb::Region &dest, const rfb::Point &delta)
|
||||
{
|
||||
try {
|
||||
server->add_copied(dest, delta);
|
||||
} catch (rdr::Exception& e) {
|
||||
vlog.error("XserverDesktop::add_copied: %s",e.str());
|
||||
}
|
||||
}
|
||||
|
||||
void XserverDesktop::handleSocketEvent(int fd, bool read, bool write)
|
||||
{
|
||||
try {
|
||||
if (read) {
|
||||
if (handleListenerEvent(fd, &listeners, server))
|
||||
return;
|
||||
if (handleListenerEvent(fd, &httpListeners, httpServer))
|
||||
return;
|
||||
}
|
||||
|
||||
if (handleSocketEvent(fd, server, read, write))
|
||||
return;
|
||||
if (handleSocketEvent(fd, httpServer, read, write))
|
||||
return;
|
||||
|
||||
vlog.error("Cannot find file descriptor for socket event");
|
||||
} catch (rdr::Exception& e) {
|
||||
vlog.error("XserverDesktop::handleSocketEvent: %s",e.str());
|
||||
}
|
||||
}
|
||||
|
||||
bool XserverDesktop::handleListenerEvent(int fd,
|
||||
std::list<SocketListener*>* sockets,
|
||||
SocketServer* sockserv)
|
||||
{
|
||||
std::list<SocketListener*>::iterator i;
|
||||
|
||||
for (i = sockets->begin(); i != sockets->end(); i++) {
|
||||
if ((*i)->getFd() == fd)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == sockets->end())
|
||||
return false;
|
||||
|
||||
Socket* sock = (*i)->accept();
|
||||
sock->outStream().setBlocking(false);
|
||||
vlog.debug("new client, sock %d", sock->getFd());
|
||||
sockserv->addSocket(sock);
|
||||
vncSetNotifyFd(sock->getFd(), screenIndex, true, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XserverDesktop::handleSocketEvent(int fd,
|
||||
SocketServer* sockserv,
|
||||
bool read, bool write)
|
||||
{
|
||||
std::list<Socket*> sockets;
|
||||
std::list<Socket*>::iterator i;
|
||||
|
||||
sockserv->getSockets(&sockets);
|
||||
for (i = sockets.begin(); i != sockets.end(); i++) {
|
||||
if ((*i)->getFd() == fd)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == sockets.end())
|
||||
return false;
|
||||
|
||||
if (read)
|
||||
sockserv->processSocketReadEvent(*i);
|
||||
|
||||
if (write)
|
||||
sockserv->processSocketWriteEvent(*i);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void XserverDesktop::blockHandler(int* timeout)
|
||||
{
|
||||
// We don't have a good callback for when we can init input devices[1],
|
||||
// so we abuse the fact that this routine will be called first thing
|
||||
// once the dix is done initialising.
|
||||
// [1] Technically Xvnc has InitInput(), but libvnc.so has nothing.
|
||||
vncInitInputDevice();
|
||||
|
||||
try {
|
||||
std::list<Socket*> sockets;
|
||||
std::list<Socket*>::iterator i;
|
||||
server->getSockets(&sockets);
|
||||
for (i = sockets.begin(); i != sockets.end(); i++) {
|
||||
int fd = (*i)->getFd();
|
||||
if ((*i)->isShutdown()) {
|
||||
vlog.debug("client gone, sock %d",fd);
|
||||
vncRemoveNotifyFd(fd);
|
||||
server->removeSocket(*i);
|
||||
vncClientGone(fd);
|
||||
delete (*i);
|
||||
} else {
|
||||
/* Update existing NotifyFD to listen for write (or not) */
|
||||
vncSetNotifyFd(fd, screenIndex, true, (*i)->outStream().bufferUsage() > 0);
|
||||
}
|
||||
}
|
||||
if (httpServer) {
|
||||
httpServer->getSockets(&sockets);
|
||||
for (i = sockets.begin(); i != sockets.end(); i++) {
|
||||
int fd = (*i)->getFd();
|
||||
if ((*i)->isShutdown()) {
|
||||
vlog.debug("http client gone, sock %d",fd);
|
||||
vncRemoveNotifyFd(fd);
|
||||
httpServer->removeSocket(*i);
|
||||
delete (*i);
|
||||
} else {
|
||||
/* Update existing NotifyFD to listen for write (or not) */
|
||||
vncSetNotifyFd(fd, screenIndex, true, (*i)->outStream().bufferUsage() > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We are responsible for propagating mouse movement between clients
|
||||
int cursorX, cursorY;
|
||||
vncGetPointerPos(&cursorX, &cursorY);
|
||||
cursorX -= vncGetScreenX(screenIndex);
|
||||
cursorY -= vncGetScreenY(screenIndex);
|
||||
if (oldCursorPos.x != cursorX || oldCursorPos.y != cursorY) {
|
||||
oldCursorPos.x = cursorX;
|
||||
oldCursorPos.y = cursorY;
|
||||
server->setCursorPos(oldCursorPos);
|
||||
}
|
||||
|
||||
// Trigger timers and check when the next will expire
|
||||
int nextTimeout = server->checkTimeouts();
|
||||
if (nextTimeout > 0 && (*timeout == -1 || nextTimeout < *timeout))
|
||||
*timeout = nextTimeout;
|
||||
} catch (rdr::Exception& e) {
|
||||
vlog.error("XserverDesktop::blockHandler: %s",e.str());
|
||||
}
|
||||
}
|
||||
|
||||
void XserverDesktop::addClient(Socket* sock, bool reverse)
|
||||
{
|
||||
vlog.debug("new client, sock %d reverse %d",sock->getFd(),reverse);
|
||||
sock->outStream().setBlocking(false);
|
||||
server->addSocket(sock, reverse);
|
||||
vncSetNotifyFd(sock->getFd(), screenIndex, true, false);
|
||||
}
|
||||
|
||||
void XserverDesktop::disconnectClients()
|
||||
{
|
||||
vlog.debug("disconnecting all clients");
|
||||
return server->closeClients("Disconnection from server end");
|
||||
}
|
||||
|
||||
|
||||
void XserverDesktop::getQueryConnect(uint32_t* opaqueId,
|
||||
const char** address,
|
||||
const char** username,
|
||||
int *timeout)
|
||||
{
|
||||
*opaqueId = queryConnectId;
|
||||
|
||||
if (!queryConnectTimer.isStarted()) {
|
||||
*address = "";
|
||||
*username = "";
|
||||
*timeout = 0;
|
||||
} else {
|
||||
*address = queryConnectAddress.buf;
|
||||
*username = queryConnectUsername.buf;
|
||||
*timeout = queryConnectTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
void XserverDesktop::approveConnection(uint32_t opaqueId, bool accept,
|
||||
const char* rejectMsg)
|
||||
{
|
||||
if (queryConnectId == opaqueId) {
|
||||
server->approveConnection(queryConnectSocket, accept, rejectMsg);
|
||||
queryConnectId = 0;
|
||||
queryConnectTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// SDesktop callbacks
|
||||
|
||||
|
||||
void XserverDesktop::pointerEvent(const Point& pos, int buttonMask)
|
||||
{
|
||||
vncPointerMove(pos.x + vncGetScreenX(screenIndex),
|
||||
pos.y + vncGetScreenY(screenIndex));
|
||||
vncPointerButtonAction(buttonMask);
|
||||
}
|
||||
|
||||
void XserverDesktop::clientCutText(const char* str, int len)
|
||||
{
|
||||
vncClientCutText(str, len);
|
||||
}
|
||||
|
||||
unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height,
|
||||
const rfb::ScreenSet& layout)
|
||||
{
|
||||
char buffer[2048];
|
||||
vlog.debug("Got request for framebuffer resize to %dx%d",
|
||||
fb_width, fb_height);
|
||||
layout.print(buffer, sizeof(buffer));
|
||||
vlog.debug("%s", buffer);
|
||||
|
||||
vncSetGlueContext(screenIndex);
|
||||
return ::setScreenLayout(fb_width, fb_height, layout, &outputIdMap);
|
||||
}
|
||||
|
||||
void XserverDesktop::grabRegion(const rfb::Region& region)
|
||||
{
|
||||
if (directFbptr)
|
||||
return;
|
||||
|
||||
std::vector<rfb::Rect> rects;
|
||||
std::vector<rfb::Rect>::iterator i;
|
||||
region.get_rects(&rects);
|
||||
for (i = rects.begin(); i != rects.end(); i++) {
|
||||
rdr::U8 *buffer;
|
||||
int stride;
|
||||
|
||||
buffer = getBufferRW(*i, &stride);
|
||||
vncGetScreenImage(screenIndex, i->tl.x, i->tl.y, i->width(), i->height(),
|
||||
(char*)buffer, stride * format.bpp/8);
|
||||
commitBufferRW(*i);
|
||||
}
|
||||
}
|
||||
|
||||
void XserverDesktop::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
|
||||
{
|
||||
if (!rawKeyboard)
|
||||
keycode = 0;
|
||||
|
||||
vncKeyboardEvent(keysym, keycode, down);
|
||||
}
|
||||
|
||||
bool XserverDesktop::handleTimeout(Timer* t)
|
||||
{
|
||||
if (t == &queryConnectTimer) {
|
||||
server->approveConnection(queryConnectSocket, false,
|
||||
"The attempt to prompt the user to "
|
||||
"accept the connection failed");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
139
unix/xserver/hw/vnc/XserverDesktop.h
Normal file
139
unix/xserver/hw/vnc/XserverDesktop.h
Normal file
@ -0,0 +1,139 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2009-2015 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.
|
||||
*/
|
||||
//
|
||||
// XserverDesktop.h
|
||||
//
|
||||
|
||||
#ifndef __XSERVERDESKTOP_H__
|
||||
#define __XSERVERDESKTOP_H__
|
||||
|
||||
#ifdef HAVE_DIX_CONFIG_H
|
||||
#include <dix-config.h>
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <rfb/SDesktop.h>
|
||||
#include <rfb/HTTPServer.h>
|
||||
#include <rfb/PixelBuffer.h>
|
||||
#include <rfb/Configuration.h>
|
||||
#include <rfb/VNCServerST.h>
|
||||
#include <rdr/SubstitutingInStream.h>
|
||||
#include <unixcommon.h>
|
||||
#include "Input.h"
|
||||
|
||||
namespace rfb {
|
||||
class VNCServerST;
|
||||
}
|
||||
|
||||
namespace network { class SocketListener; class Socket; class SocketServer; }
|
||||
|
||||
class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer,
|
||||
public rdr::Substitutor,
|
||||
public rfb::VNCServerST::QueryConnectionHandler,
|
||||
public rfb::Timer::Callback {
|
||||
public:
|
||||
|
||||
XserverDesktop(int screenIndex,
|
||||
std::list<network::SocketListener*> listeners_,
|
||||
std::list<network::SocketListener*> httpListeners_,
|
||||
const char* name, const rfb::PixelFormat &pf,
|
||||
int width, int height, void* fbptr, int stride);
|
||||
virtual ~XserverDesktop();
|
||||
|
||||
// methods called from X server code
|
||||
void blockUpdates();
|
||||
void unblockUpdates();
|
||||
void setFramebuffer(int w, int h, void* fbptr, int stride);
|
||||
void refreshScreenLayout();
|
||||
void bell();
|
||||
void setLEDState(unsigned int state);
|
||||
void serverCutText(const char* str, int len);
|
||||
void setDesktopName(const char* name);
|
||||
void setCursor(int width, int height, int hotX, int hotY,
|
||||
const unsigned char *rgbaData);
|
||||
void add_changed(const rfb::Region ®ion);
|
||||
void add_copied(const rfb::Region &dest, const rfb::Point &delta);
|
||||
void handleSocketEvent(int fd, bool read, bool write);
|
||||
void blockHandler(int* timeout);
|
||||
void addClient(network::Socket* sock, bool reverse);
|
||||
void disconnectClients();
|
||||
|
||||
// QueryConnect methods called from X server code
|
||||
// getQueryConnect()
|
||||
// Returns information about the currently waiting query
|
||||
// (or an id of 0 if there is none waiting)
|
||||
void getQueryConnect(uint32_t* opaqueId, const char** address,
|
||||
const char** username, int *timeout);
|
||||
|
||||
// approveConnection()
|
||||
// Used by X server code to supply the result of a query.
|
||||
void approveConnection(uint32_t opaqueId, bool accept,
|
||||
const char* rejectMsg=0);
|
||||
|
||||
// rfb::SDesktop callbacks
|
||||
virtual void pointerEvent(const rfb::Point& pos, int buttonMask);
|
||||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
|
||||
virtual void clientCutText(const char* str, int len);
|
||||
virtual unsigned int setScreenLayout(int fb_width, int fb_height,
|
||||
const rfb::ScreenSet& layout);
|
||||
|
||||
// rfb::PixelBuffer callbacks
|
||||
virtual void grabRegion(const rfb::Region& r);
|
||||
|
||||
// rdr::Substitutor callback
|
||||
virtual char* substitute(const char* varName);
|
||||
|
||||
// rfb::VNCServerST::QueryConnectionHandler callback
|
||||
virtual rfb::VNCServerST::queryResult queryConnection(network::Socket* sock,
|
||||
const char* userName,
|
||||
char** reason);
|
||||
|
||||
protected:
|
||||
bool handleListenerEvent(int fd,
|
||||
std::list<network::SocketListener*>* sockets,
|
||||
network::SocketServer* sockserv);
|
||||
bool handleSocketEvent(int fd,
|
||||
network::SocketServer* sockserv,
|
||||
bool read, bool write);
|
||||
|
||||
virtual bool handleTimeout(rfb::Timer* t);
|
||||
|
||||
private:
|
||||
|
||||
int screenIndex;
|
||||
rfb::VNCServerST* server;
|
||||
rfb::HTTPServer* httpServer;
|
||||
std::list<network::SocketListener*> listeners;
|
||||
std::list<network::SocketListener*> httpListeners;
|
||||
bool directFbptr;
|
||||
|
||||
uint32_t queryConnectId;
|
||||
network::Socket* queryConnectSocket;
|
||||
rfb::CharArray queryConnectAddress;
|
||||
rfb::CharArray queryConnectUsername;
|
||||
rfb::Timer queryConnectTimer;
|
||||
|
||||
OutputIdMap outputIdMap;
|
||||
|
||||
rfb::Point oldCursorPos;
|
||||
};
|
||||
#endif
|
524
unix/xserver/hw/vnc/Xvnc.man
Normal file
524
unix/xserver/hw/vnc/Xvnc.man
Normal file
@ -0,0 +1,524 @@
|
||||
.TH Xvnc 1 "" "KasmVNC" "Virtual Network Computing"
|
||||
.SH NAME
|
||||
Xvnc \- the X VNC server
|
||||
.SH SYNOPSIS
|
||||
.B Xvnc
|
||||
.RI [ options ]
|
||||
.RI : display#
|
||||
.SH DESCRIPTION
|
||||
.B Xvnc
|
||||
is the X VNC (Virtual Network Computing) server. It is based on a standard X
|
||||
server, but it has a "virtual" screen rather than a physical one. X
|
||||
applications display themselves on it as if it were a normal X display, but
|
||||
they can only be accessed via a VNC viewer - see \fBvncviewer\fP(1).
|
||||
|
||||
So Xvnc is really two servers in one. To the applications it is an X server,
|
||||
and to the remote VNC users it is a VNC server. By convention we have arranged
|
||||
that the VNC server display number will be the same as the X server display
|
||||
number, which means you can use eg. snoopy:2 to refer to display 2 on machine
|
||||
"snoopy" in both the X world and the VNC world.
|
||||
|
||||
The best way of starting \fBXvnc\fP is via the \fBvncserver\fP script. This
|
||||
sets up the environment appropriately and runs some X applications to get you
|
||||
going. See the manual page for \fBvncserver\fP(1) for more information.
|
||||
|
||||
.SH OPTIONS
|
||||
.B Xvnc
|
||||
takes lots of options - running \fBXvnc -help\fP gives a list. Many of these
|
||||
are standard X server options, which are described in the \fBXserver\fP(1)
|
||||
manual page. In addition to options which can only be set via the
|
||||
command-line, there are also "parameters" which can be set both via the
|
||||
command-line and through the \fBvncconfig\fP(1) program.
|
||||
|
||||
.TP
|
||||
.B \-geometry \fIwidth\fPx\fIheight\fP
|
||||
Specify the size of the desktop to be created. Default is 1024x768.
|
||||
.
|
||||
.TP
|
||||
.B \-depth \fIdepth\fP
|
||||
Specify the pixel depth in bits of the desktop to be created. Default is 24,
|
||||
other possible values are 8, 15, and 16 - anything else is likely to cause
|
||||
strange behaviour by applications.
|
||||
.
|
||||
.TP
|
||||
.B \-pixelformat \fIformat\fP
|
||||
Specify pixel format for server to use (BGRnnn or RGBnnn). The default for
|
||||
depth 8 is BGR233 (meaning the most significant two bits represent blue, the
|
||||
next three green, and the least significant three represent red), the default
|
||||
for depth 16 is RGB565 and for depth 24 is RGB888.
|
||||
.
|
||||
.TP
|
||||
.B \-interface \fIIP address\fP
|
||||
Listen on interface. By default Xvnc listens on all available interfaces.
|
||||
.
|
||||
.TP
|
||||
.B \-inetd
|
||||
This significantly changes Xvnc's behaviour so that it can be launched from
|
||||
inetd. See the section below on usage with inetd.
|
||||
.
|
||||
.TP
|
||||
.B \-help
|
||||
List all the options and parameters
|
||||
|
||||
.SH PARAMETERS
|
||||
VNC parameters can be set both via the command-line and through the
|
||||
\fBvncconfig\fP(1) program, and with a VNC-enabled Xorg server via Options
|
||||
entries in the xorg.conf file.
|
||||
|
||||
Parameters can be turned on with -\fIparam\fP or off with
|
||||
-\fIparam\fP=0. Parameters which take a value can be specified as
|
||||
-\fIparam\fP \fIvalue\fP. Other valid forms are \fIparam\fP\fB=\fP\fIvalue\fP
|
||||
-\fIparam\fP=\fIvalue\fP --\fIparam\fP=\fIvalue\fP. Parameter names are
|
||||
case-insensitive.
|
||||
|
||||
.TP
|
||||
.B \-desktop \fIdesktop-name\fP
|
||||
Each desktop has a name which may be displayed by the viewer. It defaults to
|
||||
"x11".
|
||||
.
|
||||
.TP
|
||||
.B \-rfbport \fIport\fP
|
||||
Specifies the TCP port on which Xvnc listens for connections from viewers (the
|
||||
protocol used in VNC is called RFB - "remote framebuffer"). The default is
|
||||
5900 plus the display number.
|
||||
.
|
||||
.TP
|
||||
.B \-UseIPv4
|
||||
Use IPv4 for incoming and outgoing connections. Default is on.
|
||||
.
|
||||
.TP
|
||||
.B \-UseIPv6
|
||||
Use IPv6 for incoming and outgoing connections. Default is on.
|
||||
.
|
||||
.TP
|
||||
.B \-rfbunixpath \fIpath\fP
|
||||
Specifies the path of a Unix domain socket on which Xvnc listens for
|
||||
connections from viewers, instead of listening on a TCP port.
|
||||
.
|
||||
.TP
|
||||
.B \-rfbunixmode \fImode\fP
|
||||
Specifies the mode of the Unix domain socket. The default is 0600.
|
||||
.
|
||||
.TP
|
||||
.B \-rfbwait \fItime\fP, \-ClientWaitTimeMillis \fItime\fP
|
||||
Time in milliseconds to wait for a viewer which is blocking the server. This is
|
||||
necessary because the server is single-threaded and sometimes blocks until the
|
||||
viewer has finished sending or receiving a message - note that this does not
|
||||
mean an update will be aborted after this time. Default is 20000 (20 seconds).
|
||||
.
|
||||
.TP
|
||||
.B \-httpd \fIdirectory\fP
|
||||
Run a mini-HTTP server which serves files from the given directory. Normally
|
||||
the directory will contain the kasmweb client. It will use the websocket port.
|
||||
.
|
||||
.TP
|
||||
.B \-rfbauth \fIpasswd-file\fP, \-PasswordFile \fIpasswd-file\fP
|
||||
Password file for VNC authentication. There is no default, you should
|
||||
specify the password file explicitly. Password file should be created with
|
||||
the \fBvncpasswd\fP(1) utility. The file is accessed each time a connection
|
||||
comes in, so it can be changed on the fly.
|
||||
.
|
||||
.TP
|
||||
.B \-AcceptCutText
|
||||
Accept clipboard updates from clients. Default is on.
|
||||
.
|
||||
.TP
|
||||
.B \-MaxCutText \fIbytes\fP
|
||||
The maximum size of a clipboard update that will be accepted from a client.
|
||||
Default is \fB262144\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-SendCutText
|
||||
Send clipboard changes to clients. Default is on.
|
||||
.
|
||||
.TP
|
||||
.B \-SendPrimary
|
||||
Send the primary selection and cut buffer to the server as well as the
|
||||
clipboard selection. Default is on.
|
||||
.
|
||||
.TP
|
||||
.B \-AcceptPointerEvents
|
||||
Accept pointer press and release events from clients. Default is on.
|
||||
.
|
||||
.TP
|
||||
.B \-AcceptKeyEvents
|
||||
Accept key press and release events from clients. Default is on.
|
||||
.
|
||||
.TP
|
||||
.B \-AcceptSetDesktopSize
|
||||
Accept requests to resize the size of the desktop. Default is on.
|
||||
.
|
||||
.TP
|
||||
.B \-DisconnectClients
|
||||
Disconnect existing clients if an incoming connection is non-shared. Default is
|
||||
on. If \fBDisconnectClients\fP is false, then a new non-shared connection will
|
||||
be refused while there is a client active. When combined with
|
||||
\fBNeverShared\fP this means only one client is allowed at a time.
|
||||
.
|
||||
.TP
|
||||
.B \-NeverShared
|
||||
Never treat incoming connections as shared, regardless of the client-specified
|
||||
setting. Default is off.
|
||||
.
|
||||
.TP
|
||||
.B \-AlwaysShared
|
||||
Always treat incoming connections as shared, regardless of the client-specified
|
||||
setting. Default is off.
|
||||
.
|
||||
.TP
|
||||
.B \-Protocol3.3
|
||||
Always use protocol version 3.3 for backwards compatibility with badly-behaved
|
||||
clients. Default is off.
|
||||
.
|
||||
.TP
|
||||
.B \-FrameRate \fIfps\fP
|
||||
The maximum number of updates per second sent to each client. If the screen
|
||||
updates any faster then those changes will be aggregated and sent in a single
|
||||
update to the client. Note that this only controls the maximum rate and a
|
||||
client may get a lower rate when resources are limited. Default is \fB60\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-DynamicQualityMin \fImin\fP
|
||||
The minimum quality to with dynamic JPEG quality scaling. The accepted values
|
||||
are 0-9 where 0 is low and 9 is high, with the same meaning as the client-side
|
||||
-quality parameter. Default is \fB7\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-DynamicQualityMax \fImax\fP
|
||||
The maximum quality to use with dynamic JPEG quality scaling. Setting this to
|
||||
zero disables dynamic JPEG quality scaling. The accepted values are 0-9 where 0
|
||||
is low and 9 is high, with the same meaning as the client-side -quality parameter.
|
||||
Default is \fB8\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-TreatLossless \fIquality\fP
|
||||
Treat lossy quality levels above and including this as lossless, without
|
||||
sending lossless updates for them. 0-9, 10 disables this.
|
||||
Default is \fB10\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-PreferBandwidth
|
||||
Prefer bandwidth over quality, and set various options for lower bandwidth use.
|
||||
The default is off, aka to prefer quality. You can override individual values
|
||||
by setting them after this switch on the command line. This switch sets the
|
||||
following:
|
||||
.br
|
||||
- dynamic JPEG quality range 2-9
|
||||
.br
|
||||
- TreatLossless 8
|
||||
.
|
||||
.TP
|
||||
.B \-RectThreads \fInum\fP
|
||||
Use this many threads to compress rects in parallel. Default \fB0\fP (automatic),
|
||||
set to \fB1\fP to disable.
|
||||
.
|
||||
.TP
|
||||
.B \-JpegVideoQuality \fInum\fP
|
||||
The JPEG quality to use when in video mode.
|
||||
Default \fB-1\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-WebpVideoQuality \fInum\fP
|
||||
The WEBP quality to use when in video mode.
|
||||
Default \fB-1\fP.
|
||||
.B \-MaxVideoResolution \fI1920x1080\fP
|
||||
When in video mode, downscale the screen to max this size. Keeps aspect ratio.
|
||||
Default \fB1920x1080\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-VideoTime \fIseconds\fP
|
||||
High rate of change must happen for this many seconds to switch to video mode.
|
||||
Default \fB5\fP, set \fB0\fP to always enable.
|
||||
.
|
||||
.TP
|
||||
.B \-VideoOutTime \fIseconds\fP
|
||||
The rate of change must be below the VideoArea threshold for this many seconds
|
||||
to switch out of video mode.
|
||||
Default \fB3\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-VideoArea \fIpercentage\fP
|
||||
High rate of change must happen for this % of the screen to switch to video mode.
|
||||
Default \fB45\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-PrintVideoArea
|
||||
Print the detected video area % value.
|
||||
Default off.
|
||||
.
|
||||
.TP
|
||||
.B \-VideoScaling \fItype\fP
|
||||
Scaling method to use when in downscaled video mode. 0 = nearest, 1 = bilinear,
|
||||
2 = progressive bilinear.
|
||||
Default \fB2\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-CompareFB \fImode\fP
|
||||
Perform pixel comparison on framebuffer to reduce unnecessary updates. Can
|
||||
be either \fB0\fP (off), \fB1\fP (always) or \fB2\fP (auto). Default is
|
||||
\fB2\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-ZlibLevel \fIlevel\fP
|
||||
Zlib compression level for ZRLE encoding (it does not affect Tight encoding).
|
||||
Acceptable values are between 0 and 9. Default is to use the standard
|
||||
compression level provided by the \fBzlib\fP(3) compression library.
|
||||
.
|
||||
.TP
|
||||
.B \-ImprovedHextile
|
||||
Use improved compression algorithm for Hextile encoding which achieves better
|
||||
compression ratios by the cost of using slightly more CPU time. Default is
|
||||
on.
|
||||
.
|
||||
.TP
|
||||
.B \-IgnoreClientSettingsKasm
|
||||
Ignore the additional client settings exposed in Kasm. Default off.
|
||||
Kasm exposes a few settings to the client the standard VNC does not.
|
||||
This param lets the server ignore those.
|
||||
.
|
||||
.TP
|
||||
.B \-DLP_ClipSendMax \fIbytes\fP
|
||||
Limit clipboard bytes to send to clients in one transaction. Default 10,000.
|
||||
0 disables the limit, use \fBSendCutText\fP to disable clipboard sending entirely.
|
||||
.
|
||||
.TP
|
||||
.B \-DLP_ClipAcceptMax \fIbytes\fP
|
||||
Limit clipboard bytes to receive from clients in one transaction. Default 10,000.
|
||||
0 disables the limit, use \fBAcceptCutText\fP to disable clipboard receiving entirely.
|
||||
.
|
||||
.TP
|
||||
.B \-DLP_ClipDelay \fIms\fP
|
||||
This many milliseconds must pass between clipboard actions. Default 1000.
|
||||
.
|
||||
.TP
|
||||
.B \-DLP_KeyRateLimit \fIkeys-per-second\fP
|
||||
Reject keyboard presses over this many per second. Default 0 (disabled).
|
||||
.
|
||||
.TP
|
||||
.B \-DLP_Log \fIoff/info/verbose\fP
|
||||
Log clipboard and keyboard actions. Info logs just clipboard direction and size,
|
||||
verbose adds the contents for both.
|
||||
.
|
||||
.TP
|
||||
.B \-noWebsocket
|
||||
Disable websockets and expose a traditional VNC port (5901, etc.).
|
||||
.
|
||||
.TP
|
||||
.B \-websocketPort \fIport\fP
|
||||
Listen for websocket connections on this port, default 6800.
|
||||
.
|
||||
.TP
|
||||
.B \-cert \fIpath\fP
|
||||
SSL pem cert to use for websocket connections, default empty/not used.
|
||||
.
|
||||
.TP
|
||||
.B \-sslOnly
|
||||
Require SSL for websocket connections. Default off, non-SSL allowed.
|
||||
.
|
||||
.TP
|
||||
.B \-basicAuth \fIuser:pass\fP
|
||||
Username and password for websocket connections. Default empty, no authentication required.
|
||||
.
|
||||
.TP
|
||||
.B \-SecurityTypes \fIsec-types\fP
|
||||
Specify which security scheme to use for incoming connections. Valid values
|
||||
are a comma separated list of \fBNone\fP, \fBVncAuth\fP, \fBPlain\fP,
|
||||
\fBTLSNone\fP, \fBTLSVnc\fP, \fBTLSPlain\fP, \fBX509None\fP, \fBX509Vnc\fP
|
||||
and \fBX509Plain\fP. Default is \fBVncAuth,TLSVnc\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-Password \fIpassword\fP
|
||||
Obfuscated binary encoding of the password which clients must supply to
|
||||
access the server. Using this parameter is insecure, use \fBPasswordFile\fP
|
||||
parameter instead.
|
||||
.
|
||||
.TP
|
||||
.B \-PlainUsers \fIuser-list\fP
|
||||
A comma separated list of user names that are allowed to authenticate via
|
||||
any of the "Plain" security types (Plain, TLSPlain, etc.). Specify \fB*\fP
|
||||
to allow any user to authenticate using this security type. Default is to
|
||||
deny all users.
|
||||
.
|
||||
.TP
|
||||
.B \-pam_service \fIname\fP, \-PAMService \fIname\fP
|
||||
PAM service name to use when authentication users using any of the "Plain"
|
||||
security types. Default is \fBvnc\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-X509Cert \fIpath\fP
|
||||
Path to a X509 certificate in PEM format to be used for all X509 based
|
||||
security types (X509None, X509Vnc, etc.).
|
||||
.
|
||||
.TP
|
||||
.B \-X509Key \fIpath\fP
|
||||
Private key counter part to the certificate given in \fBX509Cert\fP. Must
|
||||
also be in PEM format.
|
||||
.
|
||||
.TP
|
||||
.B \-GnuTLSPriority \fIpriority\fP
|
||||
GnuTLS priority string that controls the TLS session’s handshake algorithms.
|
||||
See the GnuTLS manual for possible values. Default is \fBNORMAL\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-BlacklistThreshold \fIcount\fP
|
||||
The number of unauthenticated connection attempts allowed from any individual
|
||||
host before that host is black-listed. Default is 5.
|
||||
.
|
||||
.TP
|
||||
.B \-BlacklistTimeout \fIseconds\fP
|
||||
The initial timeout applied when a host is first black-listed. The host
|
||||
cannot re-attempt a connection until the timeout expires. Default is 10.
|
||||
.
|
||||
.TP
|
||||
.B \-IdleTimeout \fIseconds\fP
|
||||
The number of seconds after which an idle VNC connection will be dropped.
|
||||
Default is 0, which means that idle connections will never be dropped.
|
||||
.
|
||||
.TP
|
||||
.B \-MaxDisconnectionTime \fIseconds\fP
|
||||
Terminate when no client has been connected for \fIN\fP seconds. Default is
|
||||
0.
|
||||
.
|
||||
.TP
|
||||
.B \-MaxConnectionTime \fIseconds\fP
|
||||
Terminate when a client has been connected for \fIN\fP seconds. Default is
|
||||
0.
|
||||
.
|
||||
.TP
|
||||
.B \-MaxIdleTime \fIseconds\fP
|
||||
Terminate after \fIN\fP seconds of user inactivity. Default is 0.
|
||||
.
|
||||
.TP
|
||||
.B \-QueryConnect
|
||||
Prompts the user of the desktop to explicitly accept or reject incoming
|
||||
connections. Default is off.
|
||||
|
||||
The \fBvncconfig\fP(1) program must be running on the desktop in order for
|
||||
QueryConnect to be supported.
|
||||
.
|
||||
.TP
|
||||
.B \-QueryConnectTimeout \fIseconds\fP
|
||||
Number of seconds to show the Accept Connection dialog before rejecting the
|
||||
connection. Default is \fB10\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-localhost
|
||||
Only allow connections from the same machine. Useful if you use SSH and want to
|
||||
stop non-SSH connections from any other hosts.
|
||||
.
|
||||
.TP
|
||||
.B \-Log \fIlogname\fP:\fIdest\fP:\fIlevel\fP
|
||||
Configures the debug log settings. \fIdest\fP can currently be \fBstderr\fP,
|
||||
\fBstdout\fP or \fBsyslog\fP, and \fIlevel\fP is between 0 and 100, 100 meaning
|
||||
most verbose output. \fIlogname\fP is usually \fB*\fP meaning all, but you can
|
||||
target a specific source file if you know the name of its "LogWriter". Default
|
||||
is \fB*:stderr:30\fP.
|
||||
.
|
||||
.TP
|
||||
.B \-RemapKeys \fImapping
|
||||
Sets up a keyboard mapping.
|
||||
.I mapping
|
||||
is a comma-separated string of character mappings, each of the form
|
||||
.IR char -> char ,
|
||||
or
|
||||
.IR char <> char ,
|
||||
where
|
||||
.I char
|
||||
is a hexadecimal keysym. For example, to exchange the " and @ symbols you would specify the following:
|
||||
|
||||
.RS 10
|
||||
RemapKeys=0x22<>0x40
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.B \-AvoidShiftNumLock
|
||||
Key affected by NumLock often require a fake Shift to be inserted in order
|
||||
for the correct symbol to be generated. Turning on this option avoids these
|
||||
extra fake Shift events but may result in a slightly different symbol
|
||||
(e.g. a Return instead of a keypad Enter).
|
||||
.
|
||||
.TP
|
||||
.B \-RawKeyboard
|
||||
Send keyboard events straight through and avoid mapping them to the current
|
||||
keyboard layout. This effectively makes the keyboard behave according to the
|
||||
layout configured on the server instead of the layout configured on the
|
||||
client. Default is off.
|
||||
.
|
||||
.TP
|
||||
.B \-AllowOverride
|
||||
Comma separated list of parameters that can be modified using VNC extension.
|
||||
Parameters can be modified for example using \fBvncconfig\fP(1) program from
|
||||
inside a running session.
|
||||
|
||||
Allowing override of parameters such as \fBPAMService\fP or \fBPasswordFile\fP
|
||||
can negatively impact security if Xvnc runs under different user than the
|
||||
programs allowed to override the parameters.
|
||||
|
||||
When \fBNoClipboard\fP parameter is set, allowing override of \fBSendCutText\fP
|
||||
and \fBAcceptCutText\fP has no effect.
|
||||
|
||||
Default is \fBdesktop,AcceptPointerEvents,SendCutText,AcceptCutText,SendPrimary,SetPrimary\fP.
|
||||
|
||||
.SH USAGE WITH INETD
|
||||
By configuring the \fBinetd\fP(1) service appropriately, Xvnc can be launched
|
||||
on demand when a connection comes in, rather than having to be started
|
||||
manually. When given the \fB-inetd\fP option, instead of listening for TCP
|
||||
connections on a given port it uses its standard input and standard output.
|
||||
There are two modes controlled by the wait/nowait entry in the inetd.conf file.
|
||||
|
||||
In the nowait mode, Xvnc uses its standard input and output directly as the
|
||||
connection to a viewer. It never has a listening socket, so cannot accept
|
||||
further connections from viewers (it can however connect out to listening
|
||||
viewers by use of the vncconfig program). Further viewer connections to the
|
||||
same TCP port result in inetd spawning off a new Xvnc to deal with each
|
||||
connection. When the connection to the viewer dies, the Xvnc and any
|
||||
associated X clients die. This behaviour is most useful when combined with the
|
||||
XDMCP options -query and -once. An typical example in inetd.conf might be (all
|
||||
on one line):
|
||||
|
||||
5950 stream tcp nowait nobody /usr/local/bin/Xvnc Xvnc -inetd -query
|
||||
localhost -once securitytypes=none
|
||||
|
||||
In this example a viewer connection to :50 will result in a new Xvnc for that
|
||||
connection which should display the standard XDM login screen on that machine.
|
||||
Because the user needs to login via XDM, it is usually OK to accept connections
|
||||
without a VNC password in this case.
|
||||
|
||||
In the wait mode, when the first connection comes in, inetd gives the listening
|
||||
socket to Xvnc. This means that for a given TCP port, there is only ever one
|
||||
Xvnc at a time. Further viewer connections to the same port are accepted by
|
||||
the same Xvnc in the normal way. Even when the original connection is broken,
|
||||
the Xvnc will continue to run. If this is used with the XDMCP options -query
|
||||
and -once, the Xvnc and associated X clients will die when the user logs out of
|
||||
the X session in the normal way. It is important to use a VNC password in this
|
||||
case. A typical entry in inetd.conf might be:
|
||||
|
||||
5951 stream tcp wait james /usr/local/bin/Xvnc Xvnc -inetd -query localhost -once passwordFile=/home/james/.vnc/passwd
|
||||
|
||||
In fact typically, you would have one entry for each user who uses VNC
|
||||
regularly, each of whom has their own dedicated TCP port which they use. In
|
||||
this example, when user "james" connects to :51, he enters his VNC password,
|
||||
then gets the XDM login screen where he logs in in the normal way. However,
|
||||
unlike the previous example, if he disconnects, the session remains persistent,
|
||||
and when he reconnects he will get the same session back again. When he logs
|
||||
out of the X session, the Xvnc will die, but of course a new one will be
|
||||
created automatically the next time he connects.
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR vncconfig (1),
|
||||
.BR vncpasswd (1),
|
||||
.BR vncserver (1),
|
||||
.BR vncviewer (1),
|
||||
.BR Xserver (1),
|
||||
.BR inetd (1)
|
||||
.br
|
||||
http://kasmweb.com
|
||||
|
||||
.SH AUTHOR
|
||||
Tristan Richardson, RealVNC Ltd. and others.
|
||||
|
||||
VNC was originally developed by the RealVNC team while at Olivetti
|
||||
Research Ltd / AT&T Laboratories Cambridge. TightVNC additions were
|
||||
implemented by Constantin Kaplinsky. Many other people have since
|
||||
participated in development, testing and support. This manual is part
|
||||
of the KasmVNC software suite.
|
18
unix/xserver/hw/vnc/buildtime.c
Normal file
18
unix/xserver/hw/vnc/buildtime.c
Normal file
@ -0,0 +1,18 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
char buildtime[] = __DATE__ " " __TIME__;
|
245
unix/xserver/hw/vnc/qnum_to_xorgevdev.c
Normal file
245
unix/xserver/hw/vnc/qnum_to_xorgevdev.c
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* This file is auto-generated from keymaps.csv on 2017-08-28 13:03
|
||||
* Database checksum sha256(f8aeff0c3430077a350e3d7ba2b335b381bd929ac4b193413730a402ff3f0097)
|
||||
* To re-generate, run:
|
||||
* keymap-gen --lang=stdc code-map keymaps.csv qnum xorgevdev
|
||||
*/
|
||||
const unsigned short code_map_qnum_to_xorgevdev[254] = {
|
||||
[0x1] = 0x9, /* qnum:1 -> linux:1 (KEY_ESC) -> xorgevdev:9 */
|
||||
[0x2] = 0xa, /* qnum:2 -> linux:2 (KEY_1) -> xorgevdev:10 */
|
||||
[0x3] = 0xb, /* qnum:3 -> linux:3 (KEY_2) -> xorgevdev:11 */
|
||||
[0x4] = 0xc, /* qnum:4 -> linux:4 (KEY_3) -> xorgevdev:12 */
|
||||
[0x5] = 0xd, /* qnum:5 -> linux:5 (KEY_4) -> xorgevdev:13 */
|
||||
[0x6] = 0xe, /* qnum:6 -> linux:6 (KEY_5) -> xorgevdev:14 */
|
||||
[0x7] = 0xf, /* qnum:7 -> linux:7 (KEY_6) -> xorgevdev:15 */
|
||||
[0x8] = 0x10, /* qnum:8 -> linux:8 (KEY_7) -> xorgevdev:16 */
|
||||
[0x9] = 0x11, /* qnum:9 -> linux:9 (KEY_8) -> xorgevdev:17 */
|
||||
[0xa] = 0x12, /* qnum:10 -> linux:10 (KEY_9) -> xorgevdev:18 */
|
||||
[0xb] = 0x13, /* qnum:11 -> linux:11 (KEY_0) -> xorgevdev:19 */
|
||||
[0xc] = 0x14, /* qnum:12 -> linux:12 (KEY_MINUS) -> xorgevdev:20 */
|
||||
[0xd] = 0x15, /* qnum:13 -> linux:13 (KEY_EQUAL) -> xorgevdev:21 */
|
||||
[0xe] = 0x16, /* qnum:14 -> linux:14 (KEY_BACKSPACE) -> xorgevdev:22 */
|
||||
[0xf] = 0x17, /* qnum:15 -> linux:15 (KEY_TAB) -> xorgevdev:23 */
|
||||
[0x10] = 0x18, /* qnum:16 -> linux:16 (KEY_Q) -> xorgevdev:24 */
|
||||
[0x11] = 0x19, /* qnum:17 -> linux:17 (KEY_W) -> xorgevdev:25 */
|
||||
[0x12] = 0x1a, /* qnum:18 -> linux:18 (KEY_E) -> xorgevdev:26 */
|
||||
[0x13] = 0x1b, /* qnum:19 -> linux:19 (KEY_R) -> xorgevdev:27 */
|
||||
[0x14] = 0x1c, /* qnum:20 -> linux:20 (KEY_T) -> xorgevdev:28 */
|
||||
[0x15] = 0x1d, /* qnum:21 -> linux:21 (KEY_Y) -> xorgevdev:29 */
|
||||
[0x16] = 0x1e, /* qnum:22 -> linux:22 (KEY_U) -> xorgevdev:30 */
|
||||
[0x17] = 0x1f, /* qnum:23 -> linux:23 (KEY_I) -> xorgevdev:31 */
|
||||
[0x18] = 0x20, /* qnum:24 -> linux:24 (KEY_O) -> xorgevdev:32 */
|
||||
[0x19] = 0x21, /* qnum:25 -> linux:25 (KEY_P) -> xorgevdev:33 */
|
||||
[0x1a] = 0x22, /* qnum:26 -> linux:26 (KEY_LEFTBRACE) -> xorgevdev:34 */
|
||||
[0x1b] = 0x23, /* qnum:27 -> linux:27 (KEY_RIGHTBRACE) -> xorgevdev:35 */
|
||||
[0x1c] = 0x24, /* qnum:28 -> linux:28 (KEY_ENTER) -> xorgevdev:36 */
|
||||
[0x1d] = 0x25, /* qnum:29 -> linux:29 (KEY_LEFTCTRL) -> xorgevdev:37 */
|
||||
[0x1e] = 0x26, /* qnum:30 -> linux:30 (KEY_A) -> xorgevdev:38 */
|
||||
[0x1f] = 0x27, /* qnum:31 -> linux:31 (KEY_S) -> xorgevdev:39 */
|
||||
[0x20] = 0x28, /* qnum:32 -> linux:32 (KEY_D) -> xorgevdev:40 */
|
||||
[0x21] = 0x29, /* qnum:33 -> linux:33 (KEY_F) -> xorgevdev:41 */
|
||||
[0x22] = 0x2a, /* qnum:34 -> linux:34 (KEY_G) -> xorgevdev:42 */
|
||||
[0x23] = 0x2b, /* qnum:35 -> linux:35 (KEY_H) -> xorgevdev:43 */
|
||||
[0x24] = 0x2c, /* qnum:36 -> linux:36 (KEY_J) -> xorgevdev:44 */
|
||||
[0x25] = 0x2d, /* qnum:37 -> linux:37 (KEY_K) -> xorgevdev:45 */
|
||||
[0x26] = 0x2e, /* qnum:38 -> linux:38 (KEY_L) -> xorgevdev:46 */
|
||||
[0x27] = 0x2f, /* qnum:39 -> linux:39 (KEY_SEMICOLON) -> xorgevdev:47 */
|
||||
[0x28] = 0x30, /* qnum:40 -> linux:40 (KEY_APOSTROPHE) -> xorgevdev:48 */
|
||||
[0x29] = 0x31, /* qnum:41 -> linux:41 (KEY_GRAVE) -> xorgevdev:49 */
|
||||
[0x2a] = 0x32, /* qnum:42 -> linux:42 (KEY_LEFTSHIFT) -> xorgevdev:50 */
|
||||
[0x2b] = 0x33, /* qnum:43 -> linux:43 (KEY_BACKSLASH) -> xorgevdev:51 */
|
||||
[0x2c] = 0x34, /* qnum:44 -> linux:44 (KEY_Z) -> xorgevdev:52 */
|
||||
[0x2d] = 0x35, /* qnum:45 -> linux:45 (KEY_X) -> xorgevdev:53 */
|
||||
[0x2e] = 0x36, /* qnum:46 -> linux:46 (KEY_C) -> xorgevdev:54 */
|
||||
[0x2f] = 0x37, /* qnum:47 -> linux:47 (KEY_V) -> xorgevdev:55 */
|
||||
[0x30] = 0x38, /* qnum:48 -> linux:48 (KEY_B) -> xorgevdev:56 */
|
||||
[0x31] = 0x39, /* qnum:49 -> linux:49 (KEY_N) -> xorgevdev:57 */
|
||||
[0x32] = 0x3a, /* qnum:50 -> linux:50 (KEY_M) -> xorgevdev:58 */
|
||||
[0x33] = 0x3b, /* qnum:51 -> linux:51 (KEY_COMMA) -> xorgevdev:59 */
|
||||
[0x34] = 0x3c, /* qnum:52 -> linux:52 (KEY_DOT) -> xorgevdev:60 */
|
||||
[0x35] = 0x3d, /* qnum:53 -> linux:53 (KEY_SLASH) -> xorgevdev:61 */
|
||||
[0x36] = 0x3e, /* qnum:54 -> linux:54 (KEY_RIGHTSHIFT) -> xorgevdev:62 */
|
||||
[0x37] = 0x3f, /* qnum:55 -> linux:55 (KEY_KPASTERISK) -> xorgevdev:63 */
|
||||
[0x38] = 0x40, /* qnum:56 -> linux:56 (KEY_LEFTALT) -> xorgevdev:64 */
|
||||
[0x39] = 0x41, /* qnum:57 -> linux:57 (KEY_SPACE) -> xorgevdev:65 */
|
||||
[0x3a] = 0x42, /* qnum:58 -> linux:58 (KEY_CAPSLOCK) -> xorgevdev:66 */
|
||||
[0x3b] = 0x43, /* qnum:59 -> linux:59 (KEY_F1) -> xorgevdev:67 */
|
||||
[0x3c] = 0x44, /* qnum:60 -> linux:60 (KEY_F2) -> xorgevdev:68 */
|
||||
[0x3d] = 0x45, /* qnum:61 -> linux:61 (KEY_F3) -> xorgevdev:69 */
|
||||
[0x3e] = 0x46, /* qnum:62 -> linux:62 (KEY_F4) -> xorgevdev:70 */
|
||||
[0x3f] = 0x47, /* qnum:63 -> linux:63 (KEY_F5) -> xorgevdev:71 */
|
||||
[0x40] = 0x48, /* qnum:64 -> linux:64 (KEY_F6) -> xorgevdev:72 */
|
||||
[0x41] = 0x49, /* qnum:65 -> linux:65 (KEY_F7) -> xorgevdev:73 */
|
||||
[0x42] = 0x4a, /* qnum:66 -> linux:66 (KEY_F8) -> xorgevdev:74 */
|
||||
[0x43] = 0x4b, /* qnum:67 -> linux:67 (KEY_F9) -> xorgevdev:75 */
|
||||
[0x44] = 0x4c, /* qnum:68 -> linux:68 (KEY_F10) -> xorgevdev:76 */
|
||||
[0x45] = 0x4d, /* qnum:69 -> linux:69 (KEY_NUMLOCK) -> xorgevdev:77 */
|
||||
[0x46] = 0x4e, /* qnum:70 -> linux:70 (KEY_SCROLLLOCK) -> xorgevdev:78 */
|
||||
[0x47] = 0x4f, /* qnum:71 -> linux:71 (KEY_KP7) -> xorgevdev:79 */
|
||||
[0x48] = 0x50, /* qnum:72 -> linux:72 (KEY_KP8) -> xorgevdev:80 */
|
||||
[0x49] = 0x51, /* qnum:73 -> linux:73 (KEY_KP9) -> xorgevdev:81 */
|
||||
[0x4a] = 0x52, /* qnum:74 -> linux:74 (KEY_KPMINUS) -> xorgevdev:82 */
|
||||
[0x4b] = 0x53, /* qnum:75 -> linux:75 (KEY_KP4) -> xorgevdev:83 */
|
||||
[0x4c] = 0x54, /* qnum:76 -> linux:76 (KEY_KP5) -> xorgevdev:84 */
|
||||
[0x4d] = 0x55, /* qnum:77 -> linux:77 (KEY_KP6) -> xorgevdev:85 */
|
||||
[0x4e] = 0x56, /* qnum:78 -> linux:78 (KEY_KPPLUS) -> xorgevdev:86 */
|
||||
[0x4f] = 0x57, /* qnum:79 -> linux:79 (KEY_KP1) -> xorgevdev:87 */
|
||||
[0x50] = 0x58, /* qnum:80 -> linux:80 (KEY_KP2) -> xorgevdev:88 */
|
||||
[0x51] = 0x59, /* qnum:81 -> linux:81 (KEY_KP3) -> xorgevdev:89 */
|
||||
[0x52] = 0x5a, /* qnum:82 -> linux:82 (KEY_KP0) -> xorgevdev:90 */
|
||||
[0x53] = 0x5b, /* qnum:83 -> linux:83 (KEY_KPDOT) -> xorgevdev:91 */
|
||||
[0x54] = 0x6b, /* qnum:84 -> linux:99 (KEY_SYSRQ) -> xorgevdev:107 */
|
||||
[0x55] = 0xc2, /* qnum:85 -> linux:186 (KEY_F16) -> xorgevdev:194 */
|
||||
[0x56] = 0x5e, /* qnum:86 -> linux:86 (KEY_102ND) -> xorgevdev:94 */
|
||||
[0x57] = 0x5f, /* qnum:87 -> linux:87 (KEY_F11) -> xorgevdev:95 */
|
||||
[0x58] = 0x60, /* qnum:88 -> linux:88 (KEY_F12) -> xorgevdev:96 */
|
||||
[0x59] = 0x7d, /* qnum:89 -> linux:117 (KEY_KPEQUAL) -> xorgevdev:125 */
|
||||
[0x5a] = 0xc6, /* qnum:90 -> linux:190 (KEY_F20) -> xorgevdev:198 */
|
||||
[0x5b] = 0x6d, /* qnum:91 -> linux:101 (KEY_LINEFEED) -> xorgevdev:109 */
|
||||
[0x5c] = 0x67, /* qnum:92 -> linux:95 (KEY_KPJPCOMMA) -> xorgevdev:103 */
|
||||
[0x5d] = 0xbf, /* qnum:93 -> linux:183 (KEY_F13) -> xorgevdev:191 */
|
||||
[0x5e] = 0xc0, /* qnum:94 -> linux:184 (KEY_F14) -> xorgevdev:192 */
|
||||
[0x5f] = 0xc1, /* qnum:95 -> linux:185 (KEY_F15) -> xorgevdev:193 */
|
||||
[0x63] = 0xb1, /* qnum:99 -> linux:169 (KEY_PHONE) -> xorgevdev:177 */
|
||||
[0x64] = 0x8e, /* qnum:100 -> linux:134 (KEY_OPEN) -> xorgevdev:142 */
|
||||
[0x65] = 0x8f, /* qnum:101 -> linux:135 (KEY_PASTE) -> xorgevdev:143 */
|
||||
[0x66] = 0x95, /* qnum:102 -> linux:141 (KEY_SETUP) -> xorgevdev:149 */
|
||||
[0x67] = 0x98, /* qnum:103 -> linux:144 (KEY_FILE) -> xorgevdev:152 */
|
||||
[0x68] = 0x99, /* qnum:104 -> linux:145 (KEY_SENDFILE) -> xorgevdev:153 */
|
||||
[0x69] = 0x9a, /* qnum:105 -> linux:146 (KEY_DELETEFILE) -> xorgevdev:154 */
|
||||
[0x6a] = 0x9f, /* qnum:106 -> linux:151 (KEY_MSDOS) -> xorgevdev:159 */
|
||||
[0x6b] = 0xa1, /* qnum:107 -> linux:153 (KEY_DIRECTION) -> xorgevdev:161 */
|
||||
[0x6c] = 0xa9, /* qnum:108 -> linux:161 (KEY_EJECTCD) -> xorgevdev:169 */
|
||||
[0x6d] = 0xc9, /* qnum:109 -> linux:193 (KEY_F23) -> xorgevdev:201 */
|
||||
[0x6f] = 0xca, /* qnum:111 -> linux:194 (KEY_F24) -> xorgevdev:202 */
|
||||
[0x70] = 0xb2, /* qnum:112 -> linux:170 (KEY_ISO) -> xorgevdev:178 */
|
||||
[0x71] = 0xb6, /* qnum:113 -> linux:174 (KEY_EXIT) -> xorgevdev:182 */
|
||||
[0x72] = 0xb7, /* qnum:114 -> linux:175 (KEY_MOVE) -> xorgevdev:183 */
|
||||
[0x73] = 0x61, /* qnum:115 -> linux:89 (KEY_RO) -> xorgevdev:97 */
|
||||
[0x74] = 0xc7, /* qnum:116 -> linux:191 (KEY_F21) -> xorgevdev:199 */
|
||||
[0x75] = 0xb9, /* qnum:117 -> linux:177 (KEY_SCROLLUP) -> xorgevdev:185 */
|
||||
[0x76] = 0x5d, /* qnum:118 -> linux:85 (KEY_ZENKAKUHANKAKU) -> xorgevdev:93 */
|
||||
[0x77] = 0x63, /* qnum:119 -> linux:91 (KEY_HIRAGANA) -> xorgevdev:99 */
|
||||
[0x78] = 0x62, /* qnum:120 -> linux:90 (KEY_KATAKANA) -> xorgevdev:98 */
|
||||
[0x79] = 0x64, /* qnum:121 -> linux:92 (KEY_HENKAN) -> xorgevdev:100 */
|
||||
[0x7b] = 0x66, /* qnum:123 -> linux:94 (KEY_MUHENKAN) -> xorgevdev:102 */
|
||||
[0x7d] = 0x84, /* qnum:125 -> linux:124 (KEY_YEN) -> xorgevdev:132 */
|
||||
[0x7e] = 0x81, /* qnum:126 -> linux:121 (KEY_KPCOMMA) -> xorgevdev:129 */
|
||||
[0x81] = 0xb3, /* qnum:129 -> linux:171 (KEY_CONFIG) -> xorgevdev:179 */
|
||||
[0x82] = 0x9e, /* qnum:130 -> linux:150 (KEY_WWW) -> xorgevdev:158 */
|
||||
[0x83] = 0xc3, /* qnum:131 -> linux:187 (KEY_F17) -> xorgevdev:195 */
|
||||
[0x84] = 0xc5, /* qnum:132 -> linux:189 (KEY_F19) -> xorgevdev:197 */
|
||||
[0x85] = 0x89, /* qnum:133 -> linux:129 (KEY_AGAIN) -> xorgevdev:137 */
|
||||
[0x86] = 0x8a, /* qnum:134 -> linux:130 (KEY_PROPS) -> xorgevdev:138 */
|
||||
[0x87] = 0x8b, /* qnum:135 -> linux:131 (KEY_UNDO) -> xorgevdev:139 */
|
||||
[0x88] = 0xb8, /* qnum:136 -> linux:176 (KEY_EDIT) -> xorgevdev:184 */
|
||||
[0x89] = 0xbd, /* qnum:137 -> linux:181 (KEY_NEW) -> xorgevdev:189 */
|
||||
[0x8a] = 0xbe, /* qnum:138 -> linux:182 (KEY_REDO) -> xorgevdev:190 */
|
||||
[0x8b] = 0x80, /* qnum:139 -> linux:120 (KEY_SCALE) -> xorgevdev:128 */
|
||||
[0x8c] = 0x8c, /* qnum:140 -> linux:132 (KEY_FRONT) -> xorgevdev:140 */
|
||||
[0x8d] = 0x83, /* qnum:141 -> linux:123 (KEY_HANJA) -> xorgevdev:131 */
|
||||
[0x8e] = 0xf1, /* qnum:142 -> linux:233 (KEY_FORWARDMAIL) -> xorgevdev:241 */
|
||||
[0x8f] = 0xba, /* qnum:143 -> linux:178 (KEY_SCROLLDOWN) -> xorgevdev:186 */
|
||||
[0x90] = 0xad, /* qnum:144 -> linux:165 (KEY_PREVIOUSSONG) -> xorgevdev:173 */
|
||||
[0x92] = 0xa0, /* qnum:146 -> linux:152 (KEY_SCREENLOCK) -> xorgevdev:160 */
|
||||
[0x93] = 0x9b, /* qnum:147 -> linux:147 (KEY_XFER) -> xorgevdev:155 */
|
||||
[0x94] = 0xe6, /* qnum:148 -> linux:222 (KEY_ALTERASE) -> xorgevdev:230 */
|
||||
[0x95] = 0xcb, /* qnum:149 -> linux:195 (unnamed) -> xorgevdev:203 */
|
||||
[0x96] = 0xcc, /* qnum:150 -> linux:196 (unnamed) -> xorgevdev:204 */
|
||||
[0x97] = 0x9d, /* qnum:151 -> linux:149 (KEY_PROG2) -> xorgevdev:157 */
|
||||
[0x98] = 0xb0, /* qnum:152 -> linux:168 (KEY_REWIND) -> xorgevdev:176 */
|
||||
[0x99] = 0xab, /* qnum:153 -> linux:163 (KEY_NEXTSONG) -> xorgevdev:171 */
|
||||
[0x9a] = 0xcd, /* qnum:154 -> linux:197 (unnamed) -> xorgevdev:205 */
|
||||
[0x9b] = 0xce, /* qnum:155 -> linux:198 (unnamed) -> xorgevdev:206 */
|
||||
[0x9c] = 0x68, /* qnum:156 -> linux:96 (KEY_KPENTER) -> xorgevdev:104 */
|
||||
[0x9d] = 0x69, /* qnum:157 -> linux:97 (KEY_RIGHTCTRL) -> xorgevdev:105 */
|
||||
[0x9e] = 0x93, /* qnum:158 -> linux:139 (KEY_MENU) -> xorgevdev:147 */
|
||||
[0x9f] = 0x9c, /* qnum:159 -> linux:148 (KEY_PROG1) -> xorgevdev:156 */
|
||||
[0xa0] = 0x79, /* qnum:160 -> linux:113 (KEY_MUTE) -> xorgevdev:121 */
|
||||
[0xa1] = 0x94, /* qnum:161 -> linux:140 (KEY_CALC) -> xorgevdev:148 */
|
||||
[0xa2] = 0xac, /* qnum:162 -> linux:164 (KEY_PLAYPAUSE) -> xorgevdev:172 */
|
||||
[0xa3] = 0xa8, /* qnum:163 -> linux:160 (KEY_CLOSECD) -> xorgevdev:168 */
|
||||
[0xa4] = 0xae, /* qnum:164 -> linux:166 (KEY_STOPCD) -> xorgevdev:174 */
|
||||
[0xa5] = 0xd5, /* qnum:165 -> linux:205 (KEY_SUSPEND) -> xorgevdev:213 */
|
||||
[0xa6] = 0xa2, /* qnum:166 -> linux:154 (KEY_CYCLEWINDOWS) -> xorgevdev:162 */
|
||||
[0xa7] = 0xcf, /* qnum:167 -> linux:199 (unnamed) -> xorgevdev:207 */
|
||||
[0xa8] = 0xd0, /* qnum:168 -> linux:200 (KEY_PLAYCD) -> xorgevdev:208 */
|
||||
[0xa9] = 0xd1, /* qnum:169 -> linux:201 (KEY_PAUSECD) -> xorgevdev:209 */
|
||||
[0xab] = 0xd2, /* qnum:171 -> linux:202 (KEY_PROG3) -> xorgevdev:210 */
|
||||
[0xac] = 0xd3, /* qnum:172 -> linux:203 (KEY_PROG4) -> xorgevdev:211 */
|
||||
[0xad] = 0xd4, /* qnum:173 -> linux:204 (KEY_DASHBOARD) -> xorgevdev:212 */
|
||||
[0xae] = 0x7a, /* qnum:174 -> linux:114 (KEY_VOLUMEDOWN) -> xorgevdev:122 */
|
||||
[0xaf] = 0xd6, /* qnum:175 -> linux:206 (KEY_CLOSE) -> xorgevdev:214 */
|
||||
[0xb0] = 0x7b, /* qnum:176 -> linux:115 (KEY_VOLUMEUP) -> xorgevdev:123 */
|
||||
[0xb1] = 0xaf, /* qnum:177 -> linux:167 (KEY_RECORD) -> xorgevdev:175 */
|
||||
[0xb2] = 0xb4, /* qnum:178 -> linux:172 (KEY_HOMEPAGE) -> xorgevdev:180 */
|
||||
[0xb3] = 0xd7, /* qnum:179 -> linux:207 (KEY_PLAY) -> xorgevdev:215 */
|
||||
[0xb4] = 0xd8, /* qnum:180 -> linux:208 (KEY_FASTFORWARD) -> xorgevdev:216 */
|
||||
[0xb5] = 0x6a, /* qnum:181 -> linux:98 (KEY_KPSLASH) -> xorgevdev:106 */
|
||||
[0xb6] = 0xd9, /* qnum:182 -> linux:209 (KEY_BASSBOOST) -> xorgevdev:217 */
|
||||
[0xb8] = 0x6c, /* qnum:184 -> linux:100 (KEY_RIGHTALT) -> xorgevdev:108 */
|
||||
[0xb9] = 0xda, /* qnum:185 -> linux:210 (KEY_PRINT) -> xorgevdev:218 */
|
||||
[0xba] = 0xdb, /* qnum:186 -> linux:211 (KEY_HP) -> xorgevdev:219 */
|
||||
[0xbb] = 0xdc, /* qnum:187 -> linux:212 (KEY_CAMERA) -> xorgevdev:220 */
|
||||
[0xbc] = 0x91, /* qnum:188 -> linux:137 (KEY_CUT) -> xorgevdev:145 */
|
||||
[0xbd] = 0xdd, /* qnum:189 -> linux:213 (KEY_SOUND) -> xorgevdev:221 */
|
||||
[0xbe] = 0xde, /* qnum:190 -> linux:214 (KEY_QUESTION) -> xorgevdev:222 */
|
||||
[0xbf] = 0xdf, /* qnum:191 -> linux:215 (KEY_EMAIL) -> xorgevdev:223 */
|
||||
[0xc0] = 0xe0, /* qnum:192 -> linux:216 (KEY_CHAT) -> xorgevdev:224 */
|
||||
[0xc1] = 0x90, /* qnum:193 -> linux:136 (KEY_FIND) -> xorgevdev:144 */
|
||||
[0xc2] = 0xe2, /* qnum:194 -> linux:218 (KEY_CONNECT) -> xorgevdev:226 */
|
||||
[0xc3] = 0xe3, /* qnum:195 -> linux:219 (KEY_FINANCE) -> xorgevdev:227 */
|
||||
[0xc4] = 0xe4, /* qnum:196 -> linux:220 (KEY_SPORT) -> xorgevdev:228 */
|
||||
[0xc5] = 0xe5, /* qnum:197 -> linux:221 (KEY_SHOP) -> xorgevdev:229 */
|
||||
[0xc6] = 0x7f, /* qnum:198 -> linux:119 (KEY_PAUSE) -> xorgevdev:127 */
|
||||
[0xc7] = 0x6e, /* qnum:199 -> linux:102 (KEY_HOME) -> xorgevdev:110 */
|
||||
[0xc8] = 0x6f, /* qnum:200 -> linux:103 (KEY_UP) -> xorgevdev:111 */
|
||||
[0xc9] = 0x70, /* qnum:201 -> linux:104 (KEY_PAGEUP) -> xorgevdev:112 */
|
||||
[0xca] = 0xe7, /* qnum:202 -> linux:223 (KEY_CANCEL) -> xorgevdev:231 */
|
||||
[0xcb] = 0x71, /* qnum:203 -> linux:105 (KEY_LEFT) -> xorgevdev:113 */
|
||||
[0xcc] = 0xe8, /* qnum:204 -> linux:224 (KEY_BRIGHTNESSDOWN) -> xorgevdev:232 */
|
||||
[0xcd] = 0x72, /* qnum:205 -> linux:106 (KEY_RIGHT) -> xorgevdev:114 */
|
||||
[0xce] = 0x7e, /* qnum:206 -> linux:118 (KEY_KPPLUSMINUS) -> xorgevdev:126 */
|
||||
[0xcf] = 0x73, /* qnum:207 -> linux:107 (KEY_END) -> xorgevdev:115 */
|
||||
[0xd0] = 0x74, /* qnum:208 -> linux:108 (KEY_DOWN) -> xorgevdev:116 */
|
||||
[0xd1] = 0x75, /* qnum:209 -> linux:109 (KEY_PAGEDOWN) -> xorgevdev:117 */
|
||||
[0xd2] = 0x76, /* qnum:210 -> linux:110 (KEY_INSERT) -> xorgevdev:118 */
|
||||
[0xd3] = 0x77, /* qnum:211 -> linux:111 (KEY_DELETE) -> xorgevdev:119 */
|
||||
[0xd4] = 0xe9, /* qnum:212 -> linux:225 (KEY_BRIGHTNESSUP) -> xorgevdev:233 */
|
||||
[0xd5] = 0xf2, /* qnum:213 -> linux:234 (KEY_SAVE) -> xorgevdev:242 */
|
||||
[0xd6] = 0xeb, /* qnum:214 -> linux:227 (KEY_SWITCHVIDEOMODE) -> xorgevdev:235 */
|
||||
[0xd7] = 0xec, /* qnum:215 -> linux:228 (KEY_KBDILLUMTOGGLE) -> xorgevdev:236 */
|
||||
[0xd8] = 0xed, /* qnum:216 -> linux:229 (KEY_KBDILLUMDOWN) -> xorgevdev:237 */
|
||||
[0xd9] = 0xee, /* qnum:217 -> linux:230 (KEY_KBDILLUMUP) -> xorgevdev:238 */
|
||||
[0xda] = 0xef, /* qnum:218 -> linux:231 (KEY_SEND) -> xorgevdev:239 */
|
||||
[0xdb] = 0x85, /* qnum:219 -> linux:125 (KEY_LEFTMETA) -> xorgevdev:133 */
|
||||
[0xdc] = 0x86, /* qnum:220 -> linux:126 (KEY_RIGHTMETA) -> xorgevdev:134 */
|
||||
[0xdd] = 0x87, /* qnum:221 -> linux:127 (KEY_COMPOSE) -> xorgevdev:135 */
|
||||
[0xde] = 0x7c, /* qnum:222 -> linux:116 (KEY_POWER) -> xorgevdev:124 */
|
||||
[0xdf] = 0x96, /* qnum:223 -> linux:142 (KEY_SLEEP) -> xorgevdev:150 */
|
||||
[0xe3] = 0x97, /* qnum:227 -> linux:143 (KEY_WAKEUP) -> xorgevdev:151 */
|
||||
[0xe4] = 0xf0, /* qnum:228 -> linux:232 (KEY_REPLY) -> xorgevdev:240 */
|
||||
[0xe5] = 0xe1, /* qnum:229 -> linux:217 (KEY_SEARCH) -> xorgevdev:225 */
|
||||
[0xe6] = 0xa4, /* qnum:230 -> linux:156 (KEY_BOOKMARKS) -> xorgevdev:164 */
|
||||
[0xe7] = 0xb5, /* qnum:231 -> linux:173 (KEY_REFRESH) -> xorgevdev:181 */
|
||||
[0xe8] = 0x88, /* qnum:232 -> linux:128 (KEY_STOP) -> xorgevdev:136 */
|
||||
[0xe9] = 0xa7, /* qnum:233 -> linux:159 (KEY_FORWARD) -> xorgevdev:167 */
|
||||
[0xea] = 0xa6, /* qnum:234 -> linux:158 (KEY_BACK) -> xorgevdev:166 */
|
||||
[0xeb] = 0xa5, /* qnum:235 -> linux:157 (KEY_COMPUTER) -> xorgevdev:165 */
|
||||
[0xec] = 0xa3, /* qnum:236 -> linux:155 (KEY_MAIL) -> xorgevdev:163 */
|
||||
[0xed] = 0xea, /* qnum:237 -> linux:226 (KEY_MEDIA) -> xorgevdev:234 */
|
||||
[0xef] = 0x78, /* qnum:239 -> linux:112 (KEY_MACRO) -> xorgevdev:120 */
|
||||
[0xf0] = 0xf3, /* qnum:240 -> linux:235 (KEY_DOCUMENTS) -> xorgevdev:243 */
|
||||
[0xf1] = 0xf4, /* qnum:241 -> linux:236 (KEY_BATTERY) -> xorgevdev:244 */
|
||||
[0xf2] = 0xf5, /* qnum:242 -> linux:237 (KEY_BLUETOOTH) -> xorgevdev:245 */
|
||||
[0xf3] = 0xf6, /* qnum:243 -> linux:238 (KEY_WLAN) -> xorgevdev:246 */
|
||||
[0xf4] = 0xf7, /* qnum:244 -> linux:239 (KEY_UWB) -> xorgevdev:247 */
|
||||
[0xf5] = 0x92, /* qnum:245 -> linux:138 (KEY_HELP) -> xorgevdev:146 */
|
||||
[0xf6] = 0xbb, /* qnum:246 -> linux:179 (KEY_KPLEFTPAREN) -> xorgevdev:187 */
|
||||
[0xf7] = 0xc4, /* qnum:247 -> linux:188 (KEY_F18) -> xorgevdev:196 */
|
||||
[0xf8] = 0x8d, /* qnum:248 -> linux:133 (KEY_COPY) -> xorgevdev:141 */
|
||||
[0xf9] = 0xc8, /* qnum:249 -> linux:192 (KEY_F22) -> xorgevdev:200 */
|
||||
[0xfb] = 0xbc, /* qnum:251 -> linux:180 (KEY_KPRIGHTPAREN) -> xorgevdev:188 */
|
||||
[0xfd] = 0xaa, /* qnum:253 -> linux:162 (KEY_EJECTCLOSECD) -> xorgevdev:170 */
|
||||
};
|
||||
const unsigned int code_map_qnum_to_xorgevdev_len = sizeof(code_map_qnum_to_xorgevdev)/sizeof(code_map_qnum_to_xorgevdev[0]);
|
121
unix/xserver/hw/vnc/qnum_to_xorgkbd.c
Normal file
121
unix/xserver/hw/vnc/qnum_to_xorgkbd.c
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* This file is auto-generated from keymaps.csv on 2017-08-28 13:04
|
||||
* Database checksum sha256(f8aeff0c3430077a350e3d7ba2b335b381bd929ac4b193413730a402ff3f0097)
|
||||
* To re-generate, run:
|
||||
* keymap-gen --lang=stdc code-map keymaps.csv qnum xorgkbd
|
||||
*/
|
||||
const unsigned short code_map_qnum_to_xorgkbd[254] = {
|
||||
[0x1] = 0x9, /* qnum:1 -> linux:1 (KEY_ESC) -> xorgkbd:9 */
|
||||
[0x2] = 0xa, /* qnum:2 -> linux:2 (KEY_1) -> xorgkbd:10 */
|
||||
[0x3] = 0xb, /* qnum:3 -> linux:3 (KEY_2) -> xorgkbd:11 */
|
||||
[0x4] = 0xc, /* qnum:4 -> linux:4 (KEY_3) -> xorgkbd:12 */
|
||||
[0x5] = 0xd, /* qnum:5 -> linux:5 (KEY_4) -> xorgkbd:13 */
|
||||
[0x6] = 0xe, /* qnum:6 -> linux:6 (KEY_5) -> xorgkbd:14 */
|
||||
[0x7] = 0xf, /* qnum:7 -> linux:7 (KEY_6) -> xorgkbd:15 */
|
||||
[0x8] = 0x10, /* qnum:8 -> linux:8 (KEY_7) -> xorgkbd:16 */
|
||||
[0x9] = 0x11, /* qnum:9 -> linux:9 (KEY_8) -> xorgkbd:17 */
|
||||
[0xa] = 0x12, /* qnum:10 -> linux:10 (KEY_9) -> xorgkbd:18 */
|
||||
[0xb] = 0x13, /* qnum:11 -> linux:11 (KEY_0) -> xorgkbd:19 */
|
||||
[0xc] = 0x14, /* qnum:12 -> linux:12 (KEY_MINUS) -> xorgkbd:20 */
|
||||
[0xd] = 0x15, /* qnum:13 -> linux:13 (KEY_EQUAL) -> xorgkbd:21 */
|
||||
[0xe] = 0x16, /* qnum:14 -> linux:14 (KEY_BACKSPACE) -> xorgkbd:22 */
|
||||
[0xf] = 0x17, /* qnum:15 -> linux:15 (KEY_TAB) -> xorgkbd:23 */
|
||||
[0x10] = 0x18, /* qnum:16 -> linux:16 (KEY_Q) -> xorgkbd:24 */
|
||||
[0x11] = 0x19, /* qnum:17 -> linux:17 (KEY_W) -> xorgkbd:25 */
|
||||
[0x12] = 0x1a, /* qnum:18 -> linux:18 (KEY_E) -> xorgkbd:26 */
|
||||
[0x13] = 0x1b, /* qnum:19 -> linux:19 (KEY_R) -> xorgkbd:27 */
|
||||
[0x14] = 0x1c, /* qnum:20 -> linux:20 (KEY_T) -> xorgkbd:28 */
|
||||
[0x15] = 0x1d, /* qnum:21 -> linux:21 (KEY_Y) -> xorgkbd:29 */
|
||||
[0x16] = 0x1e, /* qnum:22 -> linux:22 (KEY_U) -> xorgkbd:30 */
|
||||
[0x17] = 0x1f, /* qnum:23 -> linux:23 (KEY_I) -> xorgkbd:31 */
|
||||
[0x18] = 0x20, /* qnum:24 -> linux:24 (KEY_O) -> xorgkbd:32 */
|
||||
[0x19] = 0x21, /* qnum:25 -> linux:25 (KEY_P) -> xorgkbd:33 */
|
||||
[0x1a] = 0x22, /* qnum:26 -> linux:26 (KEY_LEFTBRACE) -> xorgkbd:34 */
|
||||
[0x1b] = 0x23, /* qnum:27 -> linux:27 (KEY_RIGHTBRACE) -> xorgkbd:35 */
|
||||
[0x1c] = 0x24, /* qnum:28 -> linux:28 (KEY_ENTER) -> xorgkbd:36 */
|
||||
[0x1d] = 0x25, /* qnum:29 -> linux:29 (KEY_LEFTCTRL) -> xorgkbd:37 */
|
||||
[0x1e] = 0x26, /* qnum:30 -> linux:30 (KEY_A) -> xorgkbd:38 */
|
||||
[0x1f] = 0x27, /* qnum:31 -> linux:31 (KEY_S) -> xorgkbd:39 */
|
||||
[0x20] = 0x28, /* qnum:32 -> linux:32 (KEY_D) -> xorgkbd:40 */
|
||||
[0x21] = 0x29, /* qnum:33 -> linux:33 (KEY_F) -> xorgkbd:41 */
|
||||
[0x22] = 0x2a, /* qnum:34 -> linux:34 (KEY_G) -> xorgkbd:42 */
|
||||
[0x23] = 0x2b, /* qnum:35 -> linux:35 (KEY_H) -> xorgkbd:43 */
|
||||
[0x24] = 0x2c, /* qnum:36 -> linux:36 (KEY_J) -> xorgkbd:44 */
|
||||
[0x25] = 0x2d, /* qnum:37 -> linux:37 (KEY_K) -> xorgkbd:45 */
|
||||
[0x26] = 0x2e, /* qnum:38 -> linux:38 (KEY_L) -> xorgkbd:46 */
|
||||
[0x27] = 0x2f, /* qnum:39 -> linux:39 (KEY_SEMICOLON) -> xorgkbd:47 */
|
||||
[0x28] = 0x30, /* qnum:40 -> linux:40 (KEY_APOSTROPHE) -> xorgkbd:48 */
|
||||
[0x29] = 0x31, /* qnum:41 -> linux:41 (KEY_GRAVE) -> xorgkbd:49 */
|
||||
[0x2a] = 0x32, /* qnum:42 -> linux:42 (KEY_LEFTSHIFT) -> xorgkbd:50 */
|
||||
[0x2b] = 0x33, /* qnum:43 -> linux:43 (KEY_BACKSLASH) -> xorgkbd:51 */
|
||||
[0x2c] = 0x34, /* qnum:44 -> linux:44 (KEY_Z) -> xorgkbd:52 */
|
||||
[0x2d] = 0x35, /* qnum:45 -> linux:45 (KEY_X) -> xorgkbd:53 */
|
||||
[0x2e] = 0x36, /* qnum:46 -> linux:46 (KEY_C) -> xorgkbd:54 */
|
||||
[0x2f] = 0x37, /* qnum:47 -> linux:47 (KEY_V) -> xorgkbd:55 */
|
||||
[0x30] = 0x38, /* qnum:48 -> linux:48 (KEY_B) -> xorgkbd:56 */
|
||||
[0x31] = 0x39, /* qnum:49 -> linux:49 (KEY_N) -> xorgkbd:57 */
|
||||
[0x32] = 0x3a, /* qnum:50 -> linux:50 (KEY_M) -> xorgkbd:58 */
|
||||
[0x33] = 0x3b, /* qnum:51 -> linux:51 (KEY_COMMA) -> xorgkbd:59 */
|
||||
[0x34] = 0x3c, /* qnum:52 -> linux:52 (KEY_DOT) -> xorgkbd:60 */
|
||||
[0x35] = 0x3d, /* qnum:53 -> linux:53 (KEY_SLASH) -> xorgkbd:61 */
|
||||
[0x36] = 0x3e, /* qnum:54 -> linux:54 (KEY_RIGHTSHIFT) -> xorgkbd:62 */
|
||||
[0x37] = 0x3f, /* qnum:55 -> linux:55 (KEY_KPASTERISK) -> xorgkbd:63 */
|
||||
[0x38] = 0x40, /* qnum:56 -> linux:56 (KEY_LEFTALT) -> xorgkbd:64 */
|
||||
[0x39] = 0x41, /* qnum:57 -> linux:57 (KEY_SPACE) -> xorgkbd:65 */
|
||||
[0x3a] = 0x42, /* qnum:58 -> linux:58 (KEY_CAPSLOCK) -> xorgkbd:66 */
|
||||
[0x3b] = 0x43, /* qnum:59 -> linux:59 (KEY_F1) -> xorgkbd:67 */
|
||||
[0x3c] = 0x44, /* qnum:60 -> linux:60 (KEY_F2) -> xorgkbd:68 */
|
||||
[0x3d] = 0x45, /* qnum:61 -> linux:61 (KEY_F3) -> xorgkbd:69 */
|
||||
[0x3e] = 0x46, /* qnum:62 -> linux:62 (KEY_F4) -> xorgkbd:70 */
|
||||
[0x3f] = 0x47, /* qnum:63 -> linux:63 (KEY_F5) -> xorgkbd:71 */
|
||||
[0x40] = 0x48, /* qnum:64 -> linux:64 (KEY_F6) -> xorgkbd:72 */
|
||||
[0x41] = 0x49, /* qnum:65 -> linux:65 (KEY_F7) -> xorgkbd:73 */
|
||||
[0x42] = 0x4a, /* qnum:66 -> linux:66 (KEY_F8) -> xorgkbd:74 */
|
||||
[0x43] = 0x4b, /* qnum:67 -> linux:67 (KEY_F9) -> xorgkbd:75 */
|
||||
[0x44] = 0x4c, /* qnum:68 -> linux:68 (KEY_F10) -> xorgkbd:76 */
|
||||
[0x45] = 0x4d, /* qnum:69 -> linux:69 (KEY_NUMLOCK) -> xorgkbd:77 */
|
||||
[0x46] = 0x4e, /* qnum:70 -> linux:70 (KEY_SCROLLLOCK) -> xorgkbd:78 */
|
||||
[0x47] = 0x4f, /* qnum:71 -> linux:71 (KEY_KP7) -> xorgkbd:79 */
|
||||
[0x48] = 0x50, /* qnum:72 -> linux:72 (KEY_KP8) -> xorgkbd:80 */
|
||||
[0x49] = 0x51, /* qnum:73 -> linux:73 (KEY_KP9) -> xorgkbd:81 */
|
||||
[0x4a] = 0x52, /* qnum:74 -> linux:74 (KEY_KPMINUS) -> xorgkbd:82 */
|
||||
[0x4b] = 0x53, /* qnum:75 -> linux:75 (KEY_KP4) -> xorgkbd:83 */
|
||||
[0x4c] = 0x54, /* qnum:76 -> linux:76 (KEY_KP5) -> xorgkbd:84 */
|
||||
[0x4d] = 0x55, /* qnum:77 -> linux:77 (KEY_KP6) -> xorgkbd:85 */
|
||||
[0x4e] = 0x56, /* qnum:78 -> linux:78 (KEY_KPPLUS) -> xorgkbd:86 */
|
||||
[0x4f] = 0x57, /* qnum:79 -> linux:79 (KEY_KP1) -> xorgkbd:87 */
|
||||
[0x50] = 0x58, /* qnum:80 -> linux:80 (KEY_KP2) -> xorgkbd:88 */
|
||||
[0x51] = 0x59, /* qnum:81 -> linux:81 (KEY_KP3) -> xorgkbd:89 */
|
||||
[0x52] = 0x5a, /* qnum:82 -> linux:82 (KEY_KP0) -> xorgkbd:90 */
|
||||
[0x53] = 0x5b, /* qnum:83 -> linux:83 (KEY_KPDOT) -> xorgkbd:91 */
|
||||
[0x54] = 0x6f, /* qnum:84 -> linux:99 (KEY_SYSRQ) -> xorgkbd:111 */
|
||||
[0x55] = 0x79, /* qnum:85 -> linux:186 (KEY_F16) -> xorgkbd:121 */
|
||||
[0x56] = 0x5e, /* qnum:86 -> linux:86 (KEY_102ND) -> xorgkbd:94 */
|
||||
[0x57] = 0x5f, /* qnum:87 -> linux:87 (KEY_F11) -> xorgkbd:95 */
|
||||
[0x58] = 0x60, /* qnum:88 -> linux:88 (KEY_F12) -> xorgkbd:96 */
|
||||
[0x59] = 0x7e, /* qnum:89 -> linux:117 (KEY_KPEQUAL) -> xorgkbd:126 */
|
||||
[0x5d] = 0x76, /* qnum:93 -> linux:183 (KEY_F13) -> xorgkbd:118 */
|
||||
[0x5e] = 0x77, /* qnum:94 -> linux:184 (KEY_F14) -> xorgkbd:119 */
|
||||
[0x5f] = 0x78, /* qnum:95 -> linux:185 (KEY_F15) -> xorgkbd:120 */
|
||||
[0x7d] = 0x85, /* qnum:125 -> linux:124 (KEY_YEN) -> xorgkbd:133 */
|
||||
[0x83] = 0x7a, /* qnum:131 -> linux:187 (KEY_F17) -> xorgkbd:122 */
|
||||
[0x9c] = 0x6c, /* qnum:156 -> linux:96 (KEY_KPENTER) -> xorgkbd:108 */
|
||||
[0x9d] = 0x6d, /* qnum:157 -> linux:97 (KEY_RIGHTCTRL) -> xorgkbd:109 */
|
||||
[0xb5] = 0x70, /* qnum:181 -> linux:98 (KEY_KPSLASH) -> xorgkbd:112 */
|
||||
[0xb8] = 0x71, /* qnum:184 -> linux:100 (KEY_RIGHTALT) -> xorgkbd:113 */
|
||||
[0xc6] = 0x6e, /* qnum:198 -> linux:119 (KEY_PAUSE) -> xorgkbd:110 */
|
||||
[0xc7] = 0x61, /* qnum:199 -> linux:102 (KEY_HOME) -> xorgkbd:97 */
|
||||
[0xc8] = 0x62, /* qnum:200 -> linux:103 (KEY_UP) -> xorgkbd:98 */
|
||||
[0xc9] = 0x63, /* qnum:201 -> linux:104 (KEY_PAGEUP) -> xorgkbd:99 */
|
||||
[0xcb] = 0x64, /* qnum:203 -> linux:105 (KEY_LEFT) -> xorgkbd:100 */
|
||||
[0xcd] = 0x66, /* qnum:205 -> linux:106 (KEY_RIGHT) -> xorgkbd:102 */
|
||||
[0xcf] = 0x67, /* qnum:207 -> linux:107 (KEY_END) -> xorgkbd:103 */
|
||||
[0xd0] = 0x68, /* qnum:208 -> linux:108 (KEY_DOWN) -> xorgkbd:104 */
|
||||
[0xd1] = 0x69, /* qnum:209 -> linux:109 (KEY_PAGEDOWN) -> xorgkbd:105 */
|
||||
[0xd2] = 0x6a, /* qnum:210 -> linux:110 (KEY_INSERT) -> xorgkbd:106 */
|
||||
[0xd3] = 0x6b, /* qnum:211 -> linux:111 (KEY_DELETE) -> xorgkbd:107 */
|
||||
[0xdb] = 0x73, /* qnum:219 -> linux:125 (KEY_LEFTMETA) -> xorgkbd:115 */
|
||||
[0xdc] = 0x74, /* qnum:220 -> linux:126 (KEY_RIGHTMETA) -> xorgkbd:116 */
|
||||
[0xdd] = 0x75, /* qnum:221 -> linux:127 (KEY_COMPOSE) -> xorgkbd:117 */
|
||||
};
|
||||
const unsigned int code_map_qnum_to_xorgkbd_len = sizeof(code_map_qnum_to_xorgkbd)/sizeof(code_map_qnum_to_xorgkbd[0]);
|
296
unix/xserver/hw/vnc/vncBlockHandler.c
Normal file
296
unix/xserver/hw/vnc/vncBlockHandler.c
Normal file
@ -0,0 +1,296 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011-2014 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_DIX_CONFIG_H
|
||||
#include <dix-config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <X11/Xpoll.h>
|
||||
|
||||
#include "os.h"
|
||||
#include "dix.h"
|
||||
#include "scrnintstr.h"
|
||||
|
||||
#include "vncExtInit.h"
|
||||
#include "vncBlockHandler.h"
|
||||
#include "xorg-version.h"
|
||||
|
||||
#if XORG >= 119
|
||||
static void vncBlockHandler(void* data, void* timeout);
|
||||
static void vncSocketNotify(int fd, int xevents, void *data);
|
||||
#else
|
||||
static void vncBlockHandler(void * data, OSTimePtr t, void * readmask);
|
||||
static void vncWakeupHandler(void * data, int nfds, void * readmask);
|
||||
|
||||
struct vncFdEntry {
|
||||
int fd;
|
||||
int read, write;
|
||||
int scrIdx;
|
||||
struct vncFdEntry* next;
|
||||
};
|
||||
|
||||
static struct vncFdEntry* fdsHead = NULL;
|
||||
#endif
|
||||
|
||||
void vncRegisterBlockHandlers(void)
|
||||
{
|
||||
if (!RegisterBlockAndWakeupHandlers(vncBlockHandler,
|
||||
#if XORG >= 119
|
||||
(ServerWakeupHandlerProcPtr)NoopDDA,
|
||||
#else
|
||||
vncWakeupHandler,
|
||||
#endif
|
||||
0))
|
||||
FatalError("RegisterBlockAndWakeupHandlers() failed\n");
|
||||
}
|
||||
|
||||
void vncSetNotifyFd(int fd, int scrIdx, int read, int write)
|
||||
{
|
||||
#if XORG >= 119
|
||||
int mask = (read ? X_NOTIFY_READ : 0) | (write ? X_NOTIFY_WRITE : 0);
|
||||
SetNotifyFd(fd, vncSocketNotify, mask, (void*)(intptr_t)scrIdx);
|
||||
#else
|
||||
struct vncFdEntry* entry;
|
||||
|
||||
entry = fdsHead;
|
||||
while (entry) {
|
||||
if (entry->fd == fd) {
|
||||
assert(entry->scrIdx == scrIdx);
|
||||
entry->read = read;
|
||||
entry->write = write;
|
||||
return;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
entry = malloc(sizeof(struct vncFdEntry));
|
||||
memset(entry, 0, sizeof(struct vncFdEntry));
|
||||
|
||||
entry->fd = fd;
|
||||
entry->scrIdx = scrIdx;
|
||||
entry->read = read;
|
||||
entry->write = write;
|
||||
|
||||
entry->next = fdsHead;
|
||||
fdsHead = entry;
|
||||
#endif
|
||||
}
|
||||
|
||||
void vncRemoveNotifyFd(int fd)
|
||||
{
|
||||
#if XORG >= 119
|
||||
RemoveNotifyFd(fd);
|
||||
#else
|
||||
struct vncFdEntry** prev;
|
||||
struct vncFdEntry* entry;
|
||||
|
||||
prev = &fdsHead;
|
||||
entry = fdsHead;
|
||||
while (entry) {
|
||||
if (entry->fd == fd) {
|
||||
*prev = entry->next;
|
||||
return;
|
||||
}
|
||||
prev = &entry->next;
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
assert(FALSE);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if XORG >= 119
|
||||
static void vncSocketNotify(int fd, int xevents, void *data)
|
||||
{
|
||||
int scrIdx;
|
||||
|
||||
scrIdx = (intptr_t)data;
|
||||
vncHandleSocketEvent(fd, scrIdx,
|
||||
xevents & X_NOTIFY_READ,
|
||||
xevents & X_NOTIFY_WRITE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if XORG < 119
|
||||
static void vncWriteBlockHandlerFallback(OSTimePtr timeout);
|
||||
static void vncWriteWakeupHandlerFallback(void);
|
||||
void vncWriteBlockHandler(fd_set *fds);
|
||||
void vncWriteWakeupHandler(int nfds, fd_set *fds);
|
||||
#endif
|
||||
|
||||
//
|
||||
// vncBlockHandler - called just before the X server goes into poll().
|
||||
//
|
||||
// For older versions of X this also allows us to register file
|
||||
// descriptors that we want read events on.
|
||||
//
|
||||
|
||||
#if XORG >= 119
|
||||
static void vncBlockHandler(void* data, void* timeout)
|
||||
#else
|
||||
static void vncBlockHandler(void * data, OSTimePtr t, void * readmask)
|
||||
#endif
|
||||
{
|
||||
#if XORG < 119
|
||||
int _timeout;
|
||||
int* timeout = &_timeout;
|
||||
static struct timeval tv;
|
||||
|
||||
fd_set* fds;
|
||||
static struct vncFdEntry* entry;
|
||||
|
||||
if (*t == NULL)
|
||||
_timeout = -1;
|
||||
else
|
||||
_timeout = (*t)->tv_sec * 1000 + (*t)->tv_usec / 1000;
|
||||
#endif
|
||||
|
||||
vncCallBlockHandlers(timeout);
|
||||
|
||||
#if XORG < 119
|
||||
if (_timeout != -1) {
|
||||
tv.tv_sec= _timeout / 1000;
|
||||
tv.tv_usec = (_timeout % 1000) * 1000;
|
||||
*t = &tv;
|
||||
}
|
||||
|
||||
fds = (fd_set*)readmask;
|
||||
entry = fdsHead;
|
||||
while (entry) {
|
||||
if (entry->read)
|
||||
FD_SET(entry->fd, fds);
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
vncWriteBlockHandlerFallback(t);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if XORG < 119
|
||||
static void vncWakeupHandler(void * data, int nfds, void * readmask)
|
||||
{
|
||||
fd_set* fds = (fd_set*)readmask;
|
||||
|
||||
static struct vncFdEntry* entry;
|
||||
|
||||
if (nfds <= 0)
|
||||
return;
|
||||
|
||||
entry = fdsHead;
|
||||
while (entry) {
|
||||
if (entry->read && FD_ISSET(entry->fd, fds))
|
||||
vncHandleSocketEvent(entry->fd, entry->scrIdx, TRUE, FALSE);
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
vncWriteWakeupHandlerFallback();
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// vncWriteBlockHandler - extra hack to be able to get old versions of the X
|
||||
// server to monitor writeable fds and not just readable. This requirers a
|
||||
// modified Xorg and might therefore not be called.
|
||||
//
|
||||
|
||||
#if XORG < 119
|
||||
static Bool needFallback = TRUE;
|
||||
static fd_set fallbackFds;
|
||||
static struct timeval tw;
|
||||
|
||||
void vncWriteBlockHandler(fd_set *fds)
|
||||
{
|
||||
static struct vncFdEntry* entry;
|
||||
|
||||
needFallback = FALSE;
|
||||
|
||||
entry = fdsHead;
|
||||
while (entry) {
|
||||
if (entry->write)
|
||||
FD_SET(entry->fd, fds);
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
void vncWriteWakeupHandler(int nfds, fd_set *fds)
|
||||
{
|
||||
static struct vncFdEntry* entry;
|
||||
|
||||
if (nfds <= 0)
|
||||
return;
|
||||
|
||||
entry = fdsHead;
|
||||
while (entry) {
|
||||
if (entry->write && FD_ISSET(entry->fd, fds))
|
||||
vncHandleSocketEvent(entry->fd, entry->scrIdx, FALSE, TRUE);
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void vncWriteBlockHandlerFallback(OSTimePtr timeout)
|
||||
{
|
||||
if (!needFallback)
|
||||
return;
|
||||
|
||||
FD_ZERO(&fallbackFds);
|
||||
vncWriteBlockHandler(&fallbackFds);
|
||||
|
||||
// vncWriteBlockHandler() will clear this, so we need to restore it
|
||||
needFallback = TRUE;
|
||||
|
||||
if (!XFD_ANYSET(&fallbackFds))
|
||||
return;
|
||||
|
||||
if ((*timeout == NULL) ||
|
||||
((*timeout)->tv_sec > 0) || ((*timeout)->tv_usec > 10000)) {
|
||||
tw.tv_sec = 0;
|
||||
tw.tv_usec = 10000;
|
||||
*timeout = &tw;
|
||||
}
|
||||
}
|
||||
|
||||
static void vncWriteWakeupHandlerFallback(void)
|
||||
{
|
||||
int ret;
|
||||
struct timeval timeout;
|
||||
|
||||
if (!needFallback)
|
||||
return;
|
||||
|
||||
if (!XFD_ANYSET(&fallbackFds))
|
||||
return;
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
ret = select(XFD_SETSIZE, NULL, &fallbackFds, NULL, &timeout);
|
||||
if (ret < 0) {
|
||||
ErrorF("vncWriteWakeupHandlerFallback(): select: %s\n",
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return;
|
||||
|
||||
vncWriteWakeupHandler(ret, &fallbackFds);
|
||||
}
|
||||
#endif
|
36
unix/xserver/hw/vnc/vncBlockHandler.h
Normal file
36
unix/xserver/hw/vnc/vncBlockHandler.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011-2014 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 VNCBLOCKHANDLER_H
|
||||
#define VNCBLOCKHANDLER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void vncRegisterBlockHandlers(void);
|
||||
|
||||
void vncSetNotifyFd(int fd, int scrIdx, int read, int write);
|
||||
void vncRemoveNotifyFd(int fd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
605
unix/xserver/hw/vnc/vncExt.c
Normal file
605
unix/xserver/hw/vnc/vncExt.c
Normal file
@ -0,0 +1,605 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011-2015 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_DIX_CONFIG_H
|
||||
#include <dix-config.h>
|
||||
#endif
|
||||
|
||||
#define NEED_EVENTS
|
||||
#include "misc.h"
|
||||
#include "os.h"
|
||||
#include "dixstruct.h"
|
||||
#include "extnsionst.h"
|
||||
#include "scrnintstr.h"
|
||||
|
||||
#define _VNCEXT_SERVER_
|
||||
#define _VNCEXT_PROTO_
|
||||
#include "vncExt.h"
|
||||
|
||||
#include "xorg-version.h"
|
||||
|
||||
#include "vncExtInit.h"
|
||||
#include "RFBGlue.h"
|
||||
|
||||
static int ProcVncExtDispatch(ClientPtr client);
|
||||
static int SProcVncExtDispatch(ClientPtr client);
|
||||
static void vncResetProc(ExtensionEntry* extEntry);
|
||||
|
||||
static void vncClientStateChange(CallbackListPtr*, void *, void *);
|
||||
|
||||
static int vncErrorBase = 0;
|
||||
static int vncEventBase = 0;
|
||||
|
||||
int vncNoClipboard = 0;
|
||||
|
||||
static struct VncInputSelect* vncInputSelectHead = NULL;
|
||||
|
||||
struct VncInputSelect {
|
||||
ClientPtr client;
|
||||
Window window;
|
||||
int mask;
|
||||
struct VncInputSelect* next;
|
||||
};
|
||||
|
||||
void vncAddExtension(void)
|
||||
{
|
||||
ExtensionEntry* extEntry;
|
||||
|
||||
extEntry = AddExtension(VNCEXTNAME, VncExtNumberEvents, VncExtNumberErrors,
|
||||
ProcVncExtDispatch, SProcVncExtDispatch, vncResetProc,
|
||||
StandardMinorOpcode);
|
||||
if (!extEntry) {
|
||||
FatalError("vncAddExtension: AddExtension failed\n");
|
||||
}
|
||||
|
||||
vncErrorBase = extEntry->errorBase;
|
||||
vncEventBase = extEntry->eventBase;
|
||||
|
||||
if (!AddCallback(&ClientStateCallback, vncClientStateChange, 0)) {
|
||||
FatalError("Add ClientStateCallback failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
int vncNotifyQueryConnect(void)
|
||||
{
|
||||
int count;
|
||||
xVncExtQueryConnectNotifyEvent ev;
|
||||
|
||||
ev.type = vncEventBase + VncExtQueryConnectNotify;
|
||||
|
||||
count = 0;
|
||||
for (struct VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) {
|
||||
if (cur->mask & VncExtQueryConnectMask) {
|
||||
ev.sequenceNumber = cur->client->sequence;
|
||||
ev.window = cur->window;
|
||||
if (cur->client->swapped) {
|
||||
#if XORG < 112
|
||||
int n;
|
||||
swaps(&ev.sequenceNumber, n);
|
||||
swapl(&ev.window, n);
|
||||
#else
|
||||
swaps(&ev.sequenceNumber);
|
||||
swapl(&ev.window);
|
||||
#endif
|
||||
}
|
||||
WriteToClient(cur->client, sizeof(xVncExtQueryConnectNotifyEvent),
|
||||
(char *)&ev);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int ProcVncExtSetParam(ClientPtr client)
|
||||
{
|
||||
char *param;
|
||||
xVncExtSetParamReply rep;
|
||||
|
||||
REQUEST(xVncExtSetParamReq);
|
||||
REQUEST_FIXED_SIZE(xVncExtSetParamReq, stuff->paramLen);
|
||||
|
||||
param = malloc(stuff->paramLen+1);
|
||||
if (param == NULL)
|
||||
return BadAlloc;
|
||||
strncpy(param, (char*)&stuff[1], stuff->paramLen);
|
||||
param[stuff->paramLen] = '\0';
|
||||
|
||||
rep.type = X_Reply;
|
||||
rep.length = 0;
|
||||
rep.success = 0;
|
||||
rep.sequenceNumber = client->sequence;
|
||||
|
||||
/*
|
||||
* Prevent change of clipboard related parameters if clipboard is disabled.
|
||||
*/
|
||||
if (vncNoClipboard &&
|
||||
(strncasecmp(param, "SendCutText", 11) == 0 ||
|
||||
strncasecmp(param, "AcceptCutText", 13) == 0))
|
||||
goto deny;
|
||||
|
||||
if (!vncOverrideParam(param))
|
||||
goto deny;
|
||||
|
||||
rep.success = 1;
|
||||
|
||||
// Send DesktopName update if desktop name has been changed
|
||||
if (strncasecmp(param, "desktop", 7) == 0)
|
||||
vncUpdateDesktopName();
|
||||
|
||||
deny:
|
||||
free(param);
|
||||
|
||||
if (client->swapped) {
|
||||
#if XORG < 112
|
||||
int n;
|
||||
swaps(&rep.sequenceNumber, n);
|
||||
swapl(&rep.length, n);
|
||||
#else
|
||||
swaps(&rep.sequenceNumber);
|
||||
swapl(&rep.length);
|
||||
#endif
|
||||
}
|
||||
WriteToClient(client, sizeof(xVncExtSetParamReply), (char *)&rep);
|
||||
return (client->noClientException);
|
||||
}
|
||||
|
||||
static int SProcVncExtSetParam(ClientPtr client)
|
||||
{
|
||||
REQUEST(xVncExtSetParamReq);
|
||||
#if XORG < 112
|
||||
register char n;
|
||||
swaps(&stuff->length, n);
|
||||
#else
|
||||
swaps(&stuff->length);
|
||||
#endif
|
||||
REQUEST_AT_LEAST_SIZE(xVncExtSetParamReq);
|
||||
return ProcVncExtSetParam(client);
|
||||
}
|
||||
|
||||
static int ProcVncExtGetParam(ClientPtr client)
|
||||
{
|
||||
char* param;
|
||||
char* value;
|
||||
size_t len;
|
||||
xVncExtGetParamReply rep;
|
||||
|
||||
REQUEST(xVncExtGetParamReq);
|
||||
REQUEST_FIXED_SIZE(xVncExtGetParamReq, stuff->paramLen);
|
||||
|
||||
param = malloc(stuff->paramLen+1);
|
||||
if (param == NULL)
|
||||
return BadAlloc;
|
||||
strncpy(param, (char*)&stuff[1], stuff->paramLen);
|
||||
param[stuff->paramLen] = 0;
|
||||
|
||||
value = vncGetParam(param);
|
||||
len = value ? strlen(value) : 0;
|
||||
|
||||
free(param);
|
||||
|
||||
rep.type = X_Reply;
|
||||
rep.sequenceNumber = client->sequence;
|
||||
rep.success = 0;
|
||||
if (value)
|
||||
rep.success = 1;
|
||||
rep.length = (len + 3) >> 2;
|
||||
rep.valueLen = len;
|
||||
if (client->swapped) {
|
||||
#if XORG < 112
|
||||
int n;
|
||||
swaps(&rep.sequenceNumber, n);
|
||||
swapl(&rep.length, n);
|
||||
swaps(&rep.valueLen, n);
|
||||
#else
|
||||
swaps(&rep.sequenceNumber);
|
||||
swapl(&rep.length);
|
||||
swaps(&rep.valueLen);
|
||||
#endif
|
||||
}
|
||||
WriteToClient(client, sizeof(xVncExtGetParamReply), (char *)&rep);
|
||||
if (value)
|
||||
WriteToClient(client, len, value);
|
||||
free(value);
|
||||
return (client->noClientException);
|
||||
}
|
||||
|
||||
static int SProcVncExtGetParam(ClientPtr client)
|
||||
{
|
||||
REQUEST(xVncExtGetParamReq);
|
||||
#if XORG < 112
|
||||
register char n;
|
||||
swaps(&stuff->length, n);
|
||||
#else
|
||||
swaps(&stuff->length);
|
||||
#endif
|
||||
REQUEST_AT_LEAST_SIZE(xVncExtGetParamReq);
|
||||
return ProcVncExtGetParam(client);
|
||||
}
|
||||
|
||||
static int ProcVncExtGetParamDesc(ClientPtr client)
|
||||
{
|
||||
char* param;
|
||||
const char* desc;
|
||||
size_t len;
|
||||
xVncExtGetParamDescReply rep;
|
||||
|
||||
REQUEST(xVncExtGetParamDescReq);
|
||||
REQUEST_FIXED_SIZE(xVncExtGetParamDescReq, stuff->paramLen);
|
||||
|
||||
param = malloc(stuff->paramLen+1);
|
||||
if (param == NULL)
|
||||
return BadAlloc;
|
||||
strncpy(param, (char*)&stuff[1], stuff->paramLen);
|
||||
param[stuff->paramLen] = 0;
|
||||
|
||||
desc = vncGetParamDesc(param);
|
||||
len = desc ? strlen(desc) : 0;
|
||||
|
||||
free(param);
|
||||
|
||||
rep.type = X_Reply;
|
||||
rep.sequenceNumber = client->sequence;
|
||||
rep.success = 0;
|
||||
if (desc)
|
||||
rep.success = 1;
|
||||
rep.length = (len + 3) >> 2;
|
||||
rep.descLen = len;
|
||||
if (client->swapped) {
|
||||
#if XORG < 112
|
||||
int n;
|
||||
swaps(&rep.sequenceNumber, n);
|
||||
swapl(&rep.length, n);
|
||||
swaps(&rep.descLen, n);
|
||||
#else
|
||||
swaps(&rep.sequenceNumber);
|
||||
swapl(&rep.length);
|
||||
swaps(&rep.descLen);
|
||||
#endif
|
||||
}
|
||||
WriteToClient(client, sizeof(xVncExtGetParamDescReply), (char *)&rep);
|
||||
if (desc)
|
||||
WriteToClient(client, len, desc);
|
||||
return (client->noClientException);
|
||||
}
|
||||
|
||||
static int SProcVncExtGetParamDesc(ClientPtr client)
|
||||
{
|
||||
REQUEST(xVncExtGetParamDescReq);
|
||||
#if XORG < 112
|
||||
register char n;
|
||||
swaps(&stuff->length, n);
|
||||
#else
|
||||
swaps(&stuff->length);
|
||||
#endif
|
||||
REQUEST_AT_LEAST_SIZE(xVncExtGetParamDescReq);
|
||||
return ProcVncExtGetParamDesc(client);
|
||||
}
|
||||
|
||||
static int ProcVncExtListParams(ClientPtr client)
|
||||
{
|
||||
xVncExtListParamsReply rep;
|
||||
char *params;
|
||||
size_t len;
|
||||
|
||||
REQUEST_SIZE_MATCH(xVncExtListParamsReq);
|
||||
|
||||
rep.type = X_Reply;
|
||||
rep.sequenceNumber = client->sequence;
|
||||
|
||||
params = vncGetParamList();
|
||||
if (params == NULL)
|
||||
return BadAlloc;
|
||||
|
||||
len = strlen(params);
|
||||
|
||||
rep.length = (len + 3) >> 2;
|
||||
rep.nParams = vncGetParamCount();
|
||||
if (client->swapped) {
|
||||
#if XORG < 112
|
||||
int n;
|
||||
swaps(&rep.sequenceNumber, n);
|
||||
swapl(&rep.length, n);
|
||||
swaps(&rep.nParams, n);
|
||||
#else
|
||||
swaps(&rep.sequenceNumber);
|
||||
swapl(&rep.length);
|
||||
swaps(&rep.nParams);
|
||||
#endif
|
||||
}
|
||||
WriteToClient(client, sizeof(xVncExtListParamsReply), (char *)&rep);
|
||||
WriteToClient(client, len, (char*)params);
|
||||
free(params);
|
||||
return (client->noClientException);
|
||||
}
|
||||
|
||||
static int SProcVncExtListParams(ClientPtr client)
|
||||
{
|
||||
REQUEST(xVncExtListParamsReq);
|
||||
#if XORG < 112
|
||||
register char n;
|
||||
swaps(&stuff->length, n);
|
||||
#else
|
||||
swaps(&stuff->length);
|
||||
#endif
|
||||
REQUEST_SIZE_MATCH(xVncExtListParamsReq);
|
||||
return ProcVncExtListParams(client);
|
||||
}
|
||||
|
||||
static int ProcVncExtSelectInput(ClientPtr client)
|
||||
{
|
||||
struct VncInputSelect** nextPtr;
|
||||
struct VncInputSelect* cur;
|
||||
REQUEST(xVncExtSelectInputReq);
|
||||
REQUEST_SIZE_MATCH(xVncExtSelectInputReq);
|
||||
nextPtr = &vncInputSelectHead;
|
||||
for (cur = vncInputSelectHead; cur; cur = *nextPtr) {
|
||||
if (cur->client == client && cur->window == stuff->window) {
|
||||
cur->mask = stuff->mask;
|
||||
if (!cur->mask) {
|
||||
*nextPtr = cur->next;
|
||||
free(cur);
|
||||
}
|
||||
break;
|
||||
}
|
||||
nextPtr = &cur->next;
|
||||
}
|
||||
if (!cur) {
|
||||
cur = malloc(sizeof(struct VncInputSelect));
|
||||
if (cur == NULL)
|
||||
return BadAlloc;
|
||||
memset(cur, 0, sizeof(struct VncInputSelect));
|
||||
|
||||
cur->client = client;
|
||||
cur->window = stuff->window;
|
||||
cur->mask = stuff->mask;
|
||||
|
||||
cur->next = vncInputSelectHead;
|
||||
vncInputSelectHead = cur;
|
||||
}
|
||||
return (client->noClientException);
|
||||
}
|
||||
|
||||
static int SProcVncExtSelectInput(ClientPtr client)
|
||||
{
|
||||
REQUEST(xVncExtSelectInputReq);
|
||||
#if XORG < 112
|
||||
register char n;
|
||||
swaps(&stuff->length, n);
|
||||
#else
|
||||
swaps(&stuff->length);
|
||||
#endif
|
||||
REQUEST_SIZE_MATCH(xVncExtSelectInputReq);
|
||||
#if XORG < 112
|
||||
swapl(&stuff->window, n);
|
||||
swapl(&stuff->mask, n);
|
||||
#else
|
||||
swapl(&stuff->window);
|
||||
swapl(&stuff->mask);
|
||||
#endif
|
||||
return ProcVncExtSelectInput(client);
|
||||
}
|
||||
|
||||
static int ProcVncExtConnect(ClientPtr client)
|
||||
{
|
||||
char *address;
|
||||
xVncExtConnectReply rep;
|
||||
|
||||
REQUEST(xVncExtConnectReq);
|
||||
REQUEST_FIXED_SIZE(xVncExtConnectReq, stuff->strLen);
|
||||
|
||||
address = malloc(stuff->strLen+1);
|
||||
if (address == NULL)
|
||||
return BadAlloc;
|
||||
strncpy(address, (char*)&stuff[1], stuff->strLen);
|
||||
address[stuff->strLen] = 0;
|
||||
|
||||
rep.success = 0;
|
||||
if (vncConnectClient(address) == 0)
|
||||
rep.success = 1;
|
||||
|
||||
rep.type = X_Reply;
|
||||
rep.length = 0;
|
||||
rep.sequenceNumber = client->sequence;
|
||||
if (client->swapped) {
|
||||
#if XORG < 112
|
||||
int n;
|
||||
swaps(&rep.sequenceNumber, n);
|
||||
swapl(&rep.length, n);
|
||||
#else
|
||||
swaps(&rep.sequenceNumber);
|
||||
swapl(&rep.length);
|
||||
#endif
|
||||
}
|
||||
WriteToClient(client, sizeof(xVncExtConnectReply), (char *)&rep);
|
||||
|
||||
free(address);
|
||||
|
||||
return (client->noClientException);
|
||||
}
|
||||
|
||||
static int SProcVncExtConnect(ClientPtr client)
|
||||
{
|
||||
REQUEST(xVncExtConnectReq);
|
||||
#if XORG < 112
|
||||
register char n;
|
||||
swaps(&stuff->length, n);
|
||||
#else
|
||||
swaps(&stuff->length);
|
||||
#endif
|
||||
REQUEST_AT_LEAST_SIZE(xVncExtConnectReq);
|
||||
return ProcVncExtConnect(client);
|
||||
}
|
||||
|
||||
|
||||
static int ProcVncExtGetQueryConnect(ClientPtr client)
|
||||
{
|
||||
uint32_t opaqueId;
|
||||
const char *qcAddress, *qcUsername;
|
||||
int qcTimeout;
|
||||
|
||||
xVncExtGetQueryConnectReply rep;
|
||||
|
||||
REQUEST_SIZE_MATCH(xVncExtGetQueryConnectReq);
|
||||
|
||||
vncGetQueryConnect(&opaqueId, &qcAddress, &qcUsername, &qcTimeout);
|
||||
|
||||
rep.type = X_Reply;
|
||||
rep.sequenceNumber = client->sequence;
|
||||
rep.timeout = qcTimeout;
|
||||
rep.addrLen = qcTimeout ? strlen(qcAddress) : 0;
|
||||
rep.userLen = qcTimeout ? strlen(qcUsername) : 0;
|
||||
rep.opaqueId = (CARD32)(long)opaqueId;
|
||||
rep.length = ((rep.userLen + 3) >> 2) + ((rep.addrLen + 3) >> 2);
|
||||
if (client->swapped) {
|
||||
#if XORG < 112
|
||||
int n;
|
||||
swaps(&rep.sequenceNumber, n);
|
||||
swapl(&rep.addrLen, n);
|
||||
swapl(&rep.userLen, n);
|
||||
swapl(&rep.timeout, n);
|
||||
swapl(&rep.opaqueId, n);
|
||||
swapl(&rep.length, n);
|
||||
#else
|
||||
swaps(&rep.sequenceNumber);
|
||||
swapl(&rep.addrLen);
|
||||
swapl(&rep.userLen);
|
||||
swapl(&rep.timeout);
|
||||
swapl(&rep.opaqueId);
|
||||
swapl(&rep.length);
|
||||
#endif
|
||||
}
|
||||
WriteToClient(client, sizeof(xVncExtGetQueryConnectReply), (char *)&rep);
|
||||
if (qcTimeout)
|
||||
WriteToClient(client, strlen(qcAddress), qcAddress);
|
||||
if (qcTimeout)
|
||||
WriteToClient(client, strlen(qcUsername), qcUsername);
|
||||
return (client->noClientException);
|
||||
}
|
||||
|
||||
static int SProcVncExtGetQueryConnect(ClientPtr client)
|
||||
{
|
||||
REQUEST(xVncExtGetQueryConnectReq);
|
||||
#if XORG < 112
|
||||
register char n;
|
||||
swaps(&stuff->length, n);
|
||||
#else
|
||||
swaps(&stuff->length);
|
||||
#endif
|
||||
REQUEST_SIZE_MATCH(xVncExtGetQueryConnectReq);
|
||||
return ProcVncExtGetQueryConnect(client);
|
||||
}
|
||||
|
||||
|
||||
static int ProcVncExtApproveConnect(ClientPtr client)
|
||||
{
|
||||
REQUEST(xVncExtApproveConnectReq);
|
||||
REQUEST_SIZE_MATCH(xVncExtApproveConnectReq);
|
||||
vncApproveConnection(stuff->opaqueId, stuff->approve);
|
||||
// Inform other clients of the event and tidy up
|
||||
vncNotifyQueryConnect();
|
||||
return (client->noClientException);
|
||||
}
|
||||
|
||||
static int SProcVncExtApproveConnect(ClientPtr client)
|
||||
{
|
||||
REQUEST(xVncExtApproveConnectReq);
|
||||
#if XORG < 112
|
||||
register char n;
|
||||
swaps(&stuff->length, n);
|
||||
swapl(&stuff->opaqueId, n);
|
||||
#else
|
||||
swaps(&stuff->length);
|
||||
swapl(&stuff->opaqueId);
|
||||
#endif
|
||||
REQUEST_SIZE_MATCH(xVncExtApproveConnectReq);
|
||||
return ProcVncExtApproveConnect(client);
|
||||
}
|
||||
|
||||
|
||||
static int ProcVncExtDispatch(ClientPtr client)
|
||||
{
|
||||
REQUEST(xReq);
|
||||
switch (stuff->data) {
|
||||
case X_VncExtSetParam:
|
||||
return ProcVncExtSetParam(client);
|
||||
case X_VncExtGetParam:
|
||||
return ProcVncExtGetParam(client);
|
||||
case X_VncExtGetParamDesc:
|
||||
return ProcVncExtGetParamDesc(client);
|
||||
case X_VncExtListParams:
|
||||
return ProcVncExtListParams(client);
|
||||
case X_VncExtSelectInput:
|
||||
return ProcVncExtSelectInput(client);
|
||||
case X_VncExtConnect:
|
||||
return ProcVncExtConnect(client);
|
||||
case X_VncExtGetQueryConnect:
|
||||
return ProcVncExtGetQueryConnect(client);
|
||||
case X_VncExtApproveConnect:
|
||||
return ProcVncExtApproveConnect(client);
|
||||
default:
|
||||
return BadRequest;
|
||||
}
|
||||
}
|
||||
|
||||
static int SProcVncExtDispatch(ClientPtr client)
|
||||
{
|
||||
REQUEST(xReq);
|
||||
switch (stuff->data) {
|
||||
case X_VncExtSetParam:
|
||||
return SProcVncExtSetParam(client);
|
||||
case X_VncExtGetParam:
|
||||
return SProcVncExtGetParam(client);
|
||||
case X_VncExtGetParamDesc:
|
||||
return SProcVncExtGetParamDesc(client);
|
||||
case X_VncExtListParams:
|
||||
return SProcVncExtListParams(client);
|
||||
case X_VncExtSelectInput:
|
||||
return SProcVncExtSelectInput(client);
|
||||
case X_VncExtConnect:
|
||||
return SProcVncExtConnect(client);
|
||||
case X_VncExtGetQueryConnect:
|
||||
return SProcVncExtGetQueryConnect(client);
|
||||
case X_VncExtApproveConnect:
|
||||
return SProcVncExtApproveConnect(client);
|
||||
default:
|
||||
return BadRequest;
|
||||
}
|
||||
}
|
||||
|
||||
static void vncResetProc(ExtensionEntry* extEntry)
|
||||
{
|
||||
vncExtensionClose();
|
||||
}
|
||||
|
||||
static void vncClientStateChange(CallbackListPtr * l, void * d, void * p)
|
||||
{
|
||||
ClientPtr client = ((NewClientInfoRec*)p)->client;
|
||||
if (client->clientState == ClientStateGone) {
|
||||
struct VncInputSelect** nextPtr = &vncInputSelectHead;
|
||||
for (struct VncInputSelect* cur = vncInputSelectHead; cur; cur = *nextPtr) {
|
||||
if (cur->client == client) {
|
||||
*nextPtr = cur->next;
|
||||
free(cur);
|
||||
continue;
|
||||
}
|
||||
nextPtr = &cur->next;
|
||||
}
|
||||
}
|
||||
}
|
467
unix/xserver/hw/vnc/vncExtInit.cc
Normal file
467
unix/xserver/hw/vnc/vncExtInit.cc
Normal file
@ -0,0 +1,467 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011-2015 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <rfb/Configuration.h>
|
||||
#include <rfb/Logger_stdio.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/util.h>
|
||||
#include <rfb/ServerCore.h>
|
||||
#include <rdr/HexOutStream.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/Hostname.h>
|
||||
#include <rfb/Region.h>
|
||||
#include <rfb/ledStates.h>
|
||||
#include <network/TcpSocket.h>
|
||||
#include <network/UnixSocket.h>
|
||||
|
||||
#include "XserverDesktop.h"
|
||||
#include "vncExtInit.h"
|
||||
#include "vncHooks.h"
|
||||
#include "vncBlockHandler.h"
|
||||
#include "vncSelection.h"
|
||||
#include "XorgGlue.h"
|
||||
#include "RandrGlue.h"
|
||||
#include "xorg-version.h"
|
||||
|
||||
extern "C" {
|
||||
void vncSetGlueContext(int screenIndex);
|
||||
}
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
static rfb::LogWriter vlog("vncext");
|
||||
|
||||
// We can't safely get this from Xorg
|
||||
#define MAXSCREENS 16
|
||||
|
||||
static unsigned long vncExtGeneration = 0;
|
||||
static bool initialised = false;
|
||||
static XserverDesktop* desktop[MAXSCREENS] = { 0, };
|
||||
void* vncFbptr[MAXSCREENS] = { 0, };
|
||||
int vncFbstride[MAXSCREENS];
|
||||
|
||||
int vncInetdSock = -1;
|
||||
|
||||
struct CaseInsensitiveCompare {
|
||||
bool operator() (const std::string &a, const std::string &b) const {
|
||||
return strcasecmp(a.c_str(), b.c_str()) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::set<std::string, CaseInsensitiveCompare> ParamSet;
|
||||
static ParamSet allowOverrideSet;
|
||||
|
||||
rfb::AliasParameter rfbwait("rfbwait", "Alias for ClientWaitTimeMillis",
|
||||
&rfb::Server::clientWaitTimeMillis);
|
||||
rfb::IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",0);
|
||||
rfb::StringParameter rfbunixpath("rfbunixpath", "Unix socket to listen for RFB protocol", "");
|
||||
rfb::IntParameter rfbunixmode("rfbunixmode", "Unix socket access mode", 0600);
|
||||
rfb::StringParameter desktopName("desktop", "Name of VNC desktop","x11");
|
||||
rfb::BoolParameter localhostOnly("localhost",
|
||||
"Only allow connections from localhost",
|
||||
false);
|
||||
rfb::BoolParameter noWebsocket("noWebsocket",
|
||||
"Listen on a traditional VNC port instead of websocket",
|
||||
false);
|
||||
rfb::IntParameter websocketPort("websocketPort", "websocket port to listen for", 6800);
|
||||
rfb::StringParameter cert("cert", "SSL pem cert to use for websocket connections", "");
|
||||
rfb::BoolParameter sslonly("sslOnly", "Require SSL for websockets", false);
|
||||
rfb::StringParameter basicauth("BasicAuth", "user:pass for HTTP basic auth for websockets", "");
|
||||
rfb::StringParameter interface("interface",
|
||||
"listen on the specified network address",
|
||||
"all");
|
||||
rfb::BoolParameter avoidShiftNumLock("AvoidShiftNumLock",
|
||||
"Avoid fake Shift presses for keys affected by NumLock.",
|
||||
true);
|
||||
rfb::StringParameter allowOverride("AllowOverride",
|
||||
"Comma separated list of parameters that can be modified using VNC extension.",
|
||||
"desktop,AcceptPointerEvents,SendCutText,AcceptCutText,SendPrimary,SetPrimary");
|
||||
rfb::BoolParameter setPrimary("SetPrimary", "Set the PRIMARY as well "
|
||||
"as the CLIPBOARD selection", true);
|
||||
rfb::BoolParameter sendPrimary("SendPrimary",
|
||||
"Send the PRIMARY as well as the CLIPBOARD selection",
|
||||
true);
|
||||
|
||||
static PixelFormat vncGetPixelFormat(int scrIdx)
|
||||
{
|
||||
int depth, bpp;
|
||||
int trueColour, bigEndian;
|
||||
int redMask, greenMask, blueMask;
|
||||
|
||||
int redShift, greenShift, blueShift;
|
||||
int redMax, greenMax, blueMax;
|
||||
|
||||
vncGetScreenFormat(scrIdx, &depth, &bpp, &trueColour, &bigEndian,
|
||||
&redMask, &greenMask, &blueMask);
|
||||
|
||||
if (!trueColour) {
|
||||
vlog.error("pseudocolour not supported");
|
||||
abort();
|
||||
}
|
||||
|
||||
redShift = ffs(redMask) - 1;
|
||||
greenShift = ffs(greenMask) - 1;
|
||||
blueShift = ffs(blueMask) - 1;
|
||||
redMax = redMask >> redShift;
|
||||
greenMax = greenMask >> greenShift;
|
||||
blueMax = blueMask >> blueShift;
|
||||
|
||||
return PixelFormat(bpp, depth, bigEndian, trueColour,
|
||||
redMax, greenMax, blueMax,
|
||||
redShift, greenShift, blueShift);
|
||||
}
|
||||
|
||||
static void parseOverrideList(const char *text, ParamSet &out)
|
||||
{
|
||||
for (const char* iter = text; ; ++iter) {
|
||||
if (*iter == ',' || *iter == '\0') {
|
||||
out.insert(std::string(text, iter));
|
||||
text = iter + 1;
|
||||
|
||||
if (*iter == '\0')
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vncExtensionInit(void)
|
||||
{
|
||||
if (vncExtGeneration == vncGetServerGeneration()) {
|
||||
vlog.error("vncExtensionInit: called twice in same generation?");
|
||||
return;
|
||||
}
|
||||
vncExtGeneration = vncGetServerGeneration();
|
||||
|
||||
if (vncGetScreenCount() > MAXSCREENS)
|
||||
vncFatalError("vncExtensionInit: too many screens");
|
||||
|
||||
if (sizeof(ShortRect) != sizeof(struct UpdateRect))
|
||||
vncFatalError("vncExtensionInit: Incompatible ShortRect size");
|
||||
|
||||
vncAddExtension();
|
||||
|
||||
vncSelectionInit();
|
||||
|
||||
vlog.info("VNC extension running!");
|
||||
|
||||
try {
|
||||
if (!initialised) {
|
||||
rfb::initStdIOLoggers();
|
||||
|
||||
parseOverrideList(allowOverride, allowOverrideSet);
|
||||
allowOverride.setImmutable();
|
||||
|
||||
if (!Server::DLP_ClipLog[0] ||
|
||||
(strcmp(Server::DLP_ClipLog, "off") &&
|
||||
strcmp(Server::DLP_ClipLog, "info") &&
|
||||
strcmp(Server::DLP_ClipLog, "verbose")))
|
||||
vncFatalError("Invalid value to %s", Server::DLP_ClipLog.getName());
|
||||
|
||||
unsigned dummyX, dummyY;
|
||||
if (!Server::maxVideoResolution[0] ||
|
||||
sscanf(Server::maxVideoResolution, "%ux%u", &dummyX, &dummyY) != 2 ||
|
||||
dummyX < 16 ||
|
||||
dummyY < 16)
|
||||
vncFatalError("Invalid value to %s", Server::maxVideoResolution.getName());
|
||||
|
||||
initialised = true;
|
||||
}
|
||||
|
||||
for (int scr = 0; scr < vncGetScreenCount(); scr++) {
|
||||
|
||||
if (!desktop[scr]) {
|
||||
std::list<network::SocketListener*> listeners;
|
||||
std::list<network::SocketListener*> httpListeners;
|
||||
if (scr == 0 && vncInetdSock != -1) {
|
||||
if (network::isSocketListening(vncInetdSock))
|
||||
{
|
||||
listeners.push_back(new network::TcpListener(vncInetdSock));
|
||||
vlog.info("inetd wait");
|
||||
}
|
||||
} else if (rfbunixpath.getValueStr()[0] != '\0') {
|
||||
char path[PATH_MAX];
|
||||
int mode = (int)rfbunixmode;
|
||||
|
||||
if (scr == 0)
|
||||
strncpy(path, rfbunixpath, sizeof(path));
|
||||
else
|
||||
snprintf(path, sizeof(path), "%s.%d",
|
||||
rfbunixpath.getValueStr(), scr);
|
||||
path[sizeof(path)-1] = '\0';
|
||||
|
||||
listeners.push_back(new network::UnixListener(path, mode));
|
||||
|
||||
vlog.info("Listening for VNC connections on %s (mode %04o)",
|
||||
path, mode);
|
||||
} else {
|
||||
const char *addr = interface;
|
||||
int port = rfbport;
|
||||
if (port == 0) port = 5900 + atoi(vncGetDisplay());
|
||||
port += 1000 * scr;
|
||||
if (strcasecmp(addr, "all") == 0)
|
||||
addr = 0;
|
||||
if (!noWebsocket)
|
||||
network::createWebsocketListeners(&listeners, websocketPort,
|
||||
localhostOnly ? "local" : addr,
|
||||
sslonly, cert, basicauth, httpDir);
|
||||
else if (localhostOnly)
|
||||
network::createLocalTcpListeners(&listeners, port);
|
||||
else
|
||||
network::createTcpListeners(&listeners, addr, port);
|
||||
|
||||
if (noWebsocket)
|
||||
vlog.info("Listening for VNC connections on %s interface(s), port %d",
|
||||
localhostOnly ? "local" : (const char*)interface,
|
||||
port);
|
||||
else
|
||||
vlog.info("Listening for websocket connections on %s interface(s), port %d",
|
||||
localhostOnly ? "local" : (const char*)interface,
|
||||
(int) websocketPort);
|
||||
}
|
||||
|
||||
CharArray desktopNameStr(desktopName.getData());
|
||||
PixelFormat pf = vncGetPixelFormat(scr);
|
||||
|
||||
vncSetGlueContext(scr);
|
||||
desktop[scr] = new XserverDesktop(scr,
|
||||
listeners,
|
||||
httpListeners,
|
||||
desktopNameStr.buf,
|
||||
pf,
|
||||
vncGetScreenWidth(),
|
||||
vncGetScreenHeight(),
|
||||
vncFbptr[scr],
|
||||
vncFbstride[scr]);
|
||||
vlog.info("created VNC server for screen %d", scr);
|
||||
|
||||
if (scr == 0 && vncInetdSock != -1 && listeners.empty()) {
|
||||
network::Socket* sock = new network::TcpSocket(vncInetdSock);
|
||||
desktop[scr]->addClient(sock, false);
|
||||
vlog.info("added inetd sock");
|
||||
}
|
||||
}
|
||||
|
||||
vncHooksInit(scr);
|
||||
}
|
||||
} catch (rdr::Exception& e) {
|
||||
vncFatalError("vncExtInit: %s",e.str());
|
||||
}
|
||||
|
||||
vncRegisterBlockHandlers();
|
||||
}
|
||||
|
||||
void vncExtensionClose(void)
|
||||
{
|
||||
try {
|
||||
for (int scr = 0; scr < vncGetScreenCount(); scr++) {
|
||||
delete desktop[scr];
|
||||
desktop[scr] = NULL;
|
||||
}
|
||||
} catch (rdr::Exception& e) {
|
||||
vncFatalError("vncExtInit: %s",e.str());
|
||||
}
|
||||
}
|
||||
|
||||
void vncHandleSocketEvent(int fd, int scrIdx, int read, int write)
|
||||
{
|
||||
desktop[scrIdx]->handleSocketEvent(fd, read, write);
|
||||
}
|
||||
|
||||
void vncCallBlockHandlers(int* timeout)
|
||||
{
|
||||
for (int scr = 0; scr < vncGetScreenCount(); scr++)
|
||||
desktop[scr]->blockHandler(timeout);
|
||||
}
|
||||
|
||||
int vncGetAvoidShiftNumLock(void)
|
||||
{
|
||||
return (bool)avoidShiftNumLock;
|
||||
}
|
||||
|
||||
int vncGetSetPrimary(void)
|
||||
{
|
||||
return (bool)setPrimary;
|
||||
}
|
||||
|
||||
int vncGetSendPrimary(void)
|
||||
{
|
||||
return (bool)sendPrimary;
|
||||
}
|
||||
|
||||
void vncUpdateDesktopName(void)
|
||||
{
|
||||
for (int scr = 0; scr < vncGetScreenCount(); scr++)
|
||||
desktop[scr]->setDesktopName(desktopName);
|
||||
}
|
||||
|
||||
void vncServerCutText(const char *text, size_t len)
|
||||
{
|
||||
for (int scr = 0; scr < vncGetScreenCount(); scr++)
|
||||
desktop[scr]->serverCutText(text, len);
|
||||
}
|
||||
|
||||
int vncConnectClient(const char *addr)
|
||||
{
|
||||
if (strlen(addr) == 0) {
|
||||
try {
|
||||
desktop[0]->disconnectClients();
|
||||
} catch (rdr::Exception& e) {
|
||||
vlog.error("Disconnecting all clients: %s",e.str());
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *host;
|
||||
int port;
|
||||
|
||||
getHostAndPort(addr, &host, &port, 5500);
|
||||
|
||||
try {
|
||||
network::Socket* sock = new network::TcpSocket(host, port);
|
||||
delete [] host;
|
||||
desktop[0]->addClient(sock, true);
|
||||
} catch (rdr::Exception& e) {
|
||||
vlog.error("Reverse connection: %s",e.str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vncGetQueryConnect(uint32_t *opaqueId, const char**username,
|
||||
const char **address, int *timeout)
|
||||
{
|
||||
for (int scr = 0; scr < vncGetScreenCount(); scr++) {
|
||||
desktop[scr]->getQueryConnect(opaqueId, username, address, timeout);
|
||||
if (opaqueId != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void vncApproveConnection(uint32_t opaqueId, int approve)
|
||||
{
|
||||
for (int scr = 0; scr < vncGetScreenCount(); scr++) {
|
||||
desktop[scr]->approveConnection(opaqueId, approve,
|
||||
"Connection rejected by local user");
|
||||
}
|
||||
}
|
||||
|
||||
void vncBell()
|
||||
{
|
||||
for (int scr = 0; scr < vncGetScreenCount(); scr++)
|
||||
desktop[scr]->bell();
|
||||
}
|
||||
|
||||
void vncSetLEDState(unsigned long leds)
|
||||
{
|
||||
unsigned int state;
|
||||
|
||||
state = 0;
|
||||
if (leds & (1 << 0))
|
||||
state |= ledCapsLock;
|
||||
if (leds & (1 << 1))
|
||||
state |= ledNumLock;
|
||||
if (leds & (1 << 2))
|
||||
state |= ledScrollLock;
|
||||
|
||||
for (int scr = 0; scr < vncGetScreenCount(); scr++)
|
||||
desktop[scr]->setLEDState(state);
|
||||
}
|
||||
|
||||
void vncAddChanged(int scrIdx, const struct UpdateRect *extents,
|
||||
int nRects, const struct UpdateRect *rects)
|
||||
{
|
||||
Region reg;
|
||||
|
||||
reg.setExtentsAndOrderedRects((const ShortRect*)extents,
|
||||
nRects, (const ShortRect*)rects);
|
||||
desktop[scrIdx]->add_changed(reg);
|
||||
}
|
||||
|
||||
void vncAddCopied(int scrIdx, const struct UpdateRect *extents,
|
||||
int nRects, const struct UpdateRect *rects,
|
||||
int dx, int dy)
|
||||
{
|
||||
Region reg;
|
||||
|
||||
reg.setExtentsAndOrderedRects((const ShortRect*)extents,
|
||||
nRects, (const ShortRect*)rects);
|
||||
desktop[scrIdx]->add_copied(reg, rfb::Point(dx, dy));
|
||||
}
|
||||
|
||||
void vncSetCursor(int width, int height, int hotX, int hotY,
|
||||
const unsigned char *rgbaData)
|
||||
{
|
||||
for (int scr = 0; scr < vncGetScreenCount(); scr++)
|
||||
desktop[scr]->setCursor(width, height, hotX, hotY, rgbaData);
|
||||
}
|
||||
|
||||
void vncPreScreenResize(int scrIdx)
|
||||
{
|
||||
// We need to prevent the RFB core from accessing the framebuffer
|
||||
// for a while as there might be updates thrown our way inside
|
||||
// the routines that change the screen (i.e. before we have a
|
||||
// pointer to the new framebuffer).
|
||||
desktop[scrIdx]->blockUpdates();
|
||||
}
|
||||
|
||||
void vncPostScreenResize(int scrIdx, int success, int width, int height)
|
||||
{
|
||||
if (success) {
|
||||
// Let the RFB core know of the new dimensions and framebuffer
|
||||
desktop[scrIdx]->setFramebuffer(width, height,
|
||||
vncFbptr[scrIdx], vncFbstride[scrIdx]);
|
||||
}
|
||||
|
||||
desktop[scrIdx]->unblockUpdates();
|
||||
|
||||
if (success) {
|
||||
// Mark entire screen as changed
|
||||
desktop[scrIdx]->add_changed(Region(Rect(0, 0, width, height)));
|
||||
}
|
||||
}
|
||||
|
||||
void vncRefreshScreenLayout(int scrIdx)
|
||||
{
|
||||
try {
|
||||
desktop[scrIdx]->refreshScreenLayout();
|
||||
} catch (rdr::Exception& e) {
|
||||
vncFatalError("%s", e.str());
|
||||
}
|
||||
}
|
||||
|
||||
int vncOverrideParam(const char *nameAndValue)
|
||||
{
|
||||
const char* equalSign = strchr(nameAndValue, '=');
|
||||
if (!equalSign)
|
||||
return 0;
|
||||
|
||||
std::string key(nameAndValue, equalSign);
|
||||
if (allowOverrideSet.find(key) == allowOverrideSet.end())
|
||||
return 0;
|
||||
|
||||
return rfb::Configuration::setParam(nameAndValue);
|
||||
}
|
100
unix/xserver/hw/vnc/vncExtInit.h
Normal file
100
unix/xserver/hw/vnc/vncExtInit.h
Normal file
@ -0,0 +1,100 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011-2015 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 __VNCEXTINIT_H__
|
||||
#define __VNCEXTINIT_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
// Only from C++
|
||||
#ifdef __cplusplus
|
||||
namespace rfb { class StringParameter; };
|
||||
|
||||
extern rfb::StringParameter httpDir;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// vncExt.c
|
||||
extern int vncNoClipboard;
|
||||
|
||||
void vncAddExtension(void);
|
||||
|
||||
int vncNotifyQueryConnect(void);
|
||||
|
||||
// vncExtInit.cc
|
||||
extern void* vncFbptr[];
|
||||
extern int vncFbstride[];
|
||||
|
||||
extern int vncInetdSock;
|
||||
|
||||
void vncExtensionInit(void);
|
||||
void vncExtensionClose(void);
|
||||
|
||||
void vncHandleSocketEvent(int fd, int scrIdx, int read, int write);
|
||||
void vncCallBlockHandlers(int* timeout);
|
||||
|
||||
int vncGetAvoidShiftNumLock(void);
|
||||
|
||||
int vncGetSetPrimary(void);
|
||||
int vncGetSendPrimary(void);
|
||||
|
||||
void vncUpdateDesktopName(void);
|
||||
|
||||
void vncServerCutText(const char *text, size_t len);
|
||||
|
||||
int vncConnectClient(const char *addr);
|
||||
|
||||
void vncGetQueryConnect(uint32_t *opaqueId, const char**username,
|
||||
const char **address, int *timeout);
|
||||
void vncApproveConnection(uint32_t opaqueId, int approve);
|
||||
|
||||
void vncBell(void);
|
||||
|
||||
void vncSetLEDState(unsigned long leds);
|
||||
|
||||
// Must match rfb::ShortRect in common/rfb/Region.h, and BoxRec in the
|
||||
// Xorg source.
|
||||
struct UpdateRect {
|
||||
short x1, y1, x2, y2;
|
||||
};
|
||||
|
||||
void vncAddChanged(int scrIdx, const struct UpdateRect *extents,
|
||||
int nRects, const struct UpdateRect *rects);
|
||||
void vncAddCopied(int scrIdx, const struct UpdateRect *extents,
|
||||
int nRects, const struct UpdateRect *rects,
|
||||
int dx, int dy);
|
||||
|
||||
void vncSetCursor(int width, int height, int hotX, int hotY,
|
||||
const unsigned char *rgbaData);
|
||||
|
||||
void vncPreScreenResize(int scrIdx);
|
||||
void vncPostScreenResize(int scrIdx, int success, int width, int height);
|
||||
void vncRefreshScreenLayout(int scrIdx);
|
||||
|
||||
int vncOverrideParam(const char *nameAndValue);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
2432
unix/xserver/hw/vnc/vncHooks.c
Normal file
2432
unix/xserver/hw/vnc/vncHooks.c
Normal file
File diff suppressed because it is too large
Load Diff
35
unix/xserver/hw/vnc/vncHooks.h
Normal file
35
unix/xserver/hw/vnc/vncHooks.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2009-2015 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 __VNCHOOKS_H__
|
||||
#define __VNCHOOKS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int vncHooksInit(int scrIdx);
|
||||
|
||||
void vncGetScreenImage(int scrIdx, int x, int y, int width, int height,
|
||||
char *buffer, int strideBytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
140
unix/xserver/hw/vnc/vncModule.c
Normal file
140
unix/xserver/hw/vnc/vncModule.c
Normal file
@ -0,0 +1,140 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2015 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.
|
||||
*/
|
||||
/* This is the xf86 module code for the vnc extension.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_DIX_CONFIG_H
|
||||
#include <dix-config.h>
|
||||
#endif
|
||||
|
||||
#include "opaque.h"
|
||||
#include "randrstr.h"
|
||||
|
||||
#include "xorg-version.h"
|
||||
#if XORG <= 111
|
||||
typedef pointer XF86OptionPtr;
|
||||
#endif
|
||||
|
||||
#include "xf86.h"
|
||||
#include "xf86Module.h"
|
||||
|
||||
#include "vncExtInit.h"
|
||||
#include "RFBGlue.h"
|
||||
#include "XorgGlue.h"
|
||||
#include "RandrGlue.h"
|
||||
|
||||
static void vncModuleInit(INITARGS);
|
||||
|
||||
static MODULESETUPPROTO(vncSetup);
|
||||
|
||||
ExtensionModule vncExt =
|
||||
{
|
||||
vncModuleInit,
|
||||
"VNC",
|
||||
#if XORG < 112
|
||||
NULL,
|
||||
NULL,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
static XF86ModuleVersionInfo vncVersRec =
|
||||
{
|
||||
"vnc",
|
||||
"KasmVNC Project",
|
||||
MODINFOSTRING1,
|
||||
MODINFOSTRING2,
|
||||
XORG_VERSION_CURRENT,
|
||||
1, 0, 0,
|
||||
ABI_CLASS_EXTENSION, /* needs the server extension ABI */
|
||||
ABI_EXTENSION_VERSION,
|
||||
MOD_CLASS_EXTENSION,
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
_X_EXPORT XF86ModuleData vncModuleData = { &vncVersRec, vncSetup, NULL };
|
||||
|
||||
static void *
|
||||
vncSetup(void * module, void * opts, int *errmaj, int *errmin) {
|
||||
#if XORG >= 116
|
||||
LoadExtensionList(&vncExt, 1, FALSE);
|
||||
#else
|
||||
LoadExtension(&vncExt, FALSE);
|
||||
#endif
|
||||
/* Need a non-NULL return value to indicate success */
|
||||
return (void *)1;
|
||||
}
|
||||
|
||||
static void vncModuleInit(INITARGS)
|
||||
{
|
||||
static char once = 0;
|
||||
|
||||
if (!once) {
|
||||
once++;
|
||||
|
||||
vncInitRFB();
|
||||
|
||||
for (int scr = 0; scr < screenInfo.numScreens; scr++) {
|
||||
ScrnInfoPtr pScrn;
|
||||
XF86OptionPtr option;
|
||||
|
||||
pScrn = xf86Screens[scr];
|
||||
option = pScrn->options;
|
||||
while (option != NULL) {
|
||||
vncSetParam(xf86OptionName(option), xf86OptionValue(option));
|
||||
option = xf86NextOption(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vncExtensionInit();
|
||||
}
|
||||
|
||||
void vncClientGone(int fd)
|
||||
{
|
||||
}
|
||||
|
||||
int vncRandRCanCreateScreenOutputs(int scrIdx, int extraOutputs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vncRandRCreateScreenOutputs(int scrIdx, int extraOutputs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vncRandRCanCreateModes(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* vncRandRCreateMode(void* output, int width, int height)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* vncRandRSetPreferredMode(void* output, void* mode)
|
||||
{
|
||||
/*
|
||||
* We're not going to change which modes are preferred,
|
||||
* so just return the incoming mode.
|
||||
*/
|
||||
return mode;
|
||||
}
|
524
unix/xserver/hw/vnc/vncSelection.c
Normal file
524
unix/xserver/hw/vnc/vncSelection.c
Normal file
@ -0,0 +1,524 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_DIX_CONFIG_H
|
||||
#include <dix-config.h>
|
||||
#endif
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
#include "propertyst.h"
|
||||
#include "scrnintstr.h"
|
||||
#include "selection.h"
|
||||
#include "windowstr.h"
|
||||
#include "xace.h"
|
||||
|
||||
#include "xorg-version.h"
|
||||
|
||||
#include "vncExtInit.h"
|
||||
#include "vncSelection.h"
|
||||
#include "RFBGlue.h"
|
||||
|
||||
#define LOG_NAME "Selection"
|
||||
|
||||
#define LOG_ERROR(...) vncLogError(LOG_NAME, __VA_ARGS__)
|
||||
#define LOG_STATUS(...) vncLogStatus(LOG_NAME, __VA_ARGS__)
|
||||
#define LOG_INFO(...) vncLogInfo(LOG_NAME, __VA_ARGS__)
|
||||
#define LOG_DEBUG(...) vncLogDebug(LOG_NAME, __VA_ARGS__)
|
||||
|
||||
static Atom xaPRIMARY, xaCLIPBOARD;
|
||||
static Atom xaTARGETS, xaTIMESTAMP, xaSTRING, xaTEXT, xaUTF8_STRING;
|
||||
|
||||
static WindowPtr pWindow;
|
||||
static Window wid;
|
||||
|
||||
static char* clientCutText;
|
||||
static int clientCutTextLen;
|
||||
|
||||
static int vncCreateSelectionWindow(void);
|
||||
static int vncOwnSelection(Atom selection);
|
||||
static int vncProcConvertSelection(ClientPtr client);
|
||||
static int vncProcSendEvent(ClientPtr client);
|
||||
static void vncSelectionCallback(CallbackListPtr *callbacks,
|
||||
void * data, void * args);
|
||||
|
||||
static int (*origProcConvertSelection)(ClientPtr);
|
||||
static int (*origProcSendEvent)(ClientPtr);
|
||||
|
||||
void vncSelectionInit(void)
|
||||
{
|
||||
xaPRIMARY = MakeAtom("PRIMARY", 7, TRUE);
|
||||
xaCLIPBOARD = MakeAtom("CLIPBOARD", 9, TRUE);
|
||||
|
||||
xaTARGETS = MakeAtom("TARGETS", 7, TRUE);
|
||||
xaTIMESTAMP = MakeAtom("TIMESTAMP", 9, TRUE);
|
||||
xaSTRING = MakeAtom("STRING", 6, TRUE);
|
||||
xaTEXT = MakeAtom("TEXT", 4, TRUE);
|
||||
xaUTF8_STRING = MakeAtom("UTF8_STRING", 11, TRUE);
|
||||
|
||||
/* There are no hooks for when these are internal windows, so
|
||||
* override the relevant handlers. */
|
||||
origProcConvertSelection = ProcVector[X_ConvertSelection];
|
||||
ProcVector[X_ConvertSelection] = vncProcConvertSelection;
|
||||
origProcSendEvent = ProcVector[X_SendEvent];
|
||||
ProcVector[X_SendEvent] = vncProcSendEvent;
|
||||
|
||||
if (!AddCallback(&SelectionCallback, vncSelectionCallback, 0))
|
||||
FatalError("Add VNC SelectionCallback failed\n");
|
||||
}
|
||||
|
||||
void vncClientCutText(const char* str, int len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (clientCutText != NULL)
|
||||
free(clientCutText);
|
||||
|
||||
clientCutText = malloc(len);
|
||||
if (clientCutText == NULL) {
|
||||
LOG_ERROR("Could not allocate clipboard buffer");
|
||||
DeleteWindowFromAnySelections(pWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(clientCutText, str, len);
|
||||
clientCutTextLen = len;
|
||||
|
||||
if (vncGetSetPrimary()) {
|
||||
rc = vncOwnSelection(xaPRIMARY);
|
||||
if (rc != Success)
|
||||
LOG_ERROR("Could not set PRIMARY selection");
|
||||
}
|
||||
|
||||
vncOwnSelection(xaCLIPBOARD);
|
||||
if (rc != Success)
|
||||
LOG_ERROR("Could not set CLIPBOARD selection");
|
||||
}
|
||||
|
||||
static int vncCreateSelectionWindow(void)
|
||||
{
|
||||
ScreenPtr pScreen;
|
||||
int result;
|
||||
|
||||
if (pWindow != NULL)
|
||||
return Success;
|
||||
|
||||
pScreen = screenInfo.screens[0];
|
||||
|
||||
wid = FakeClientID(0);
|
||||
pWindow = CreateWindow(wid, pScreen->root,
|
||||
0, 0, 100, 100, 0, InputOnly,
|
||||
0, NULL, 0, serverClient,
|
||||
CopyFromParent, &result);
|
||||
if (!pWindow)
|
||||
return result;
|
||||
|
||||
if (!AddResource(pWindow->drawable.id, RT_WINDOW, pWindow))
|
||||
return BadAlloc;
|
||||
|
||||
LOG_DEBUG("Created selection window");
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
static int vncOwnSelection(Atom selection)
|
||||
{
|
||||
Selection *pSel;
|
||||
int rc;
|
||||
|
||||
SelectionInfoRec info;
|
||||
|
||||
rc = vncCreateSelectionWindow();
|
||||
if (rc != Success)
|
||||
return rc;
|
||||
|
||||
rc = dixLookupSelection(&pSel, selection, serverClient, DixSetAttrAccess);
|
||||
if (rc == Success) {
|
||||
if (pSel->client && (pSel->client != serverClient)) {
|
||||
xEvent event = {
|
||||
.u.selectionClear.time = currentTime.milliseconds,
|
||||
.u.selectionClear.window = pSel->window,
|
||||
.u.selectionClear.atom = pSel->selection
|
||||
};
|
||||
event.u.u.type = SelectionClear;
|
||||
WriteEventsToClient(pSel->client, 1, &event);
|
||||
}
|
||||
} else if (rc == BadMatch) {
|
||||
pSel = dixAllocateObjectWithPrivates(Selection, PRIVATE_SELECTION);
|
||||
if (!pSel)
|
||||
return BadAlloc;
|
||||
|
||||
pSel->selection = selection;
|
||||
|
||||
rc = XaceHookSelectionAccess(serverClient, &pSel,
|
||||
DixCreateAccess | DixSetAttrAccess);
|
||||
if (rc != Success) {
|
||||
free(pSel);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pSel->next = CurrentSelections;
|
||||
CurrentSelections = pSel;
|
||||
}
|
||||
else
|
||||
return rc;
|
||||
|
||||
pSel->lastTimeChanged = currentTime;
|
||||
pSel->window = wid;
|
||||
pSel->pWin = pWindow;
|
||||
pSel->client = serverClient;
|
||||
|
||||
LOG_DEBUG("Grabbed %s selection", NameForAtom(selection));
|
||||
|
||||
info.selection = pSel;
|
||||
info.client = serverClient;
|
||||
info.kind = SelectionSetOwner;
|
||||
CallCallbacks(&SelectionCallback, &info);
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
static int vncConvertSelection(ClientPtr client, Atom selection,
|
||||
Atom target, Atom property,
|
||||
Window requestor, CARD32 time)
|
||||
{
|
||||
Selection *pSel;
|
||||
WindowPtr pWin;
|
||||
int rc;
|
||||
|
||||
Atom realProperty;
|
||||
|
||||
xEvent event;
|
||||
|
||||
LOG_DEBUG("Selection request for %s (type %s)",
|
||||
NameForAtom(selection), NameForAtom(target));
|
||||
|
||||
rc = dixLookupSelection(&pSel, selection, client, DixGetAttrAccess);
|
||||
if (rc != Success)
|
||||
return rc;
|
||||
|
||||
/* We do not validate the time argument because neither does
|
||||
* dix/selection.c and some clients (e.g. Qt) relies on this */
|
||||
|
||||
rc = dixLookupWindow(&pWin, requestor, client, DixSetAttrAccess);
|
||||
if (rc != Success)
|
||||
return rc;
|
||||
|
||||
if (property != None)
|
||||
realProperty = property;
|
||||
else
|
||||
realProperty = target;
|
||||
|
||||
/* FIXME: MULTIPLE target */
|
||||
|
||||
if (target == xaTARGETS) {
|
||||
Atom targets[] = { xaTARGETS, xaTIMESTAMP,
|
||||
xaSTRING, xaTEXT, xaUTF8_STRING };
|
||||
|
||||
rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
|
||||
XA_ATOM, 32, PropModeReplace,
|
||||
sizeof(targets)/sizeof(targets[0]),
|
||||
targets, TRUE);
|
||||
if (rc != Success)
|
||||
return rc;
|
||||
} else if (target == xaTIMESTAMP) {
|
||||
rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
|
||||
XA_INTEGER, 32, PropModeReplace, 1,
|
||||
&pSel->lastTimeChanged.milliseconds,
|
||||
TRUE);
|
||||
if (rc != Success)
|
||||
return rc;
|
||||
} else if ((target == xaSTRING) || (target == xaTEXT)) {
|
||||
rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
|
||||
XA_STRING, 8, PropModeReplace,
|
||||
clientCutTextLen, clientCutText,
|
||||
TRUE);
|
||||
if (rc != Success)
|
||||
return rc;
|
||||
} else if (target == xaUTF8_STRING) {
|
||||
unsigned char* buffer;
|
||||
unsigned char* out;
|
||||
size_t len;
|
||||
|
||||
const unsigned char* in;
|
||||
size_t in_len;
|
||||
|
||||
buffer = malloc(clientCutTextLen*2);
|
||||
if (buffer == NULL)
|
||||
return BadAlloc;
|
||||
|
||||
out = buffer;
|
||||
len = 0;
|
||||
in = clientCutText;
|
||||
in_len = clientCutTextLen;
|
||||
while (in_len > 0) {
|
||||
if (*in & 0x80) {
|
||||
*out++ = 0xc0 | (*in >> 6);
|
||||
*out++ = 0x80 | (*in & 0x3f);
|
||||
len += 2;
|
||||
in++;
|
||||
in_len--;
|
||||
} else {
|
||||
*out++ = *in++;
|
||||
len++;
|
||||
in_len--;
|
||||
}
|
||||
}
|
||||
|
||||
rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
|
||||
xaUTF8_STRING, 8, PropModeReplace,
|
||||
len, buffer, TRUE);
|
||||
free(buffer);
|
||||
if (rc != Success)
|
||||
return rc;
|
||||
} else {
|
||||
return BadMatch;
|
||||
}
|
||||
|
||||
event.u.u.type = SelectionNotify;
|
||||
event.u.selectionNotify.time = time;
|
||||
event.u.selectionNotify.requestor = requestor;
|
||||
event.u.selectionNotify.selection = selection;
|
||||
event.u.selectionNotify.target = target;
|
||||
event.u.selectionNotify.property = property;
|
||||
WriteEventsToClient(client, 1, &event);
|
||||
return Success;
|
||||
}
|
||||
|
||||
static int vncProcConvertSelection(ClientPtr client)
|
||||
{
|
||||
Bool paramsOkay;
|
||||
WindowPtr pWin;
|
||||
Selection *pSel;
|
||||
int rc;
|
||||
|
||||
REQUEST(xConvertSelectionReq);
|
||||
REQUEST_SIZE_MATCH(xConvertSelectionReq);
|
||||
|
||||
rc = dixLookupWindow(&pWin, stuff->requestor, client, DixSetAttrAccess);
|
||||
if (rc != Success)
|
||||
return rc;
|
||||
|
||||
paramsOkay = ValidAtom(stuff->selection) && ValidAtom(stuff->target);
|
||||
paramsOkay &= (stuff->property == None) || ValidAtom(stuff->property);
|
||||
if (!paramsOkay) {
|
||||
client->errorValue = stuff->property;
|
||||
return BadAtom;
|
||||
}
|
||||
|
||||
rc = dixLookupSelection(&pSel, stuff->selection, client, DixReadAccess);
|
||||
if (rc == Success && pSel->client == serverClient &&
|
||||
pSel->window == wid) {
|
||||
rc = vncConvertSelection(client, stuff->selection,
|
||||
stuff->target, stuff->property,
|
||||
stuff->requestor, stuff->time);
|
||||
if (rc != Success) {
|
||||
xEvent event;
|
||||
|
||||
memset(&event, 0, sizeof(xEvent));
|
||||
event.u.u.type = SelectionNotify;
|
||||
event.u.selectionNotify.time = stuff->time;
|
||||
event.u.selectionNotify.requestor = stuff->requestor;
|
||||
event.u.selectionNotify.selection = stuff->selection;
|
||||
event.u.selectionNotify.target = stuff->target;
|
||||
event.u.selectionNotify.property = None;
|
||||
WriteEventsToClient(client, 1, &event);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
return origProcConvertSelection(client);
|
||||
}
|
||||
|
||||
static void vncSelectionRequest(Atom selection, Atom target)
|
||||
{
|
||||
Selection *pSel;
|
||||
xEvent event;
|
||||
int rc;
|
||||
|
||||
rc = vncCreateSelectionWindow();
|
||||
if (rc != Success)
|
||||
return;
|
||||
|
||||
LOG_DEBUG("Requesting %s for %s selection",
|
||||
NameForAtom(target), NameForAtom(selection));
|
||||
|
||||
rc = dixLookupSelection(&pSel, selection, serverClient, DixGetAttrAccess);
|
||||
if (rc != Success)
|
||||
return;
|
||||
|
||||
event.u.u.type = SelectionRequest;
|
||||
event.u.selectionRequest.owner = pSel->window;
|
||||
event.u.selectionRequest.time = currentTime.milliseconds;
|
||||
event.u.selectionRequest.requestor = wid;
|
||||
event.u.selectionRequest.selection = selection;
|
||||
event.u.selectionRequest.target = target;
|
||||
event.u.selectionRequest.property = target;
|
||||
WriteEventsToClient(pSel->client, 1, &event);
|
||||
}
|
||||
|
||||
static Bool vncHasAtom(Atom atom, const Atom list[], size_t size)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0;i < size;i++) {
|
||||
if (list[i] == atom)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void vncHandleSelection(Atom selection, Atom target,
|
||||
Atom property, Atom requestor,
|
||||
TimeStamp time)
|
||||
{
|
||||
PropertyPtr prop;
|
||||
int rc;
|
||||
|
||||
rc = dixLookupProperty(&prop, pWindow, property,
|
||||
serverClient, DixReadAccess);
|
||||
if (rc != Success)
|
||||
return;
|
||||
|
||||
LOG_DEBUG("Selection notification for %s (target %s, property %s, type %s)",
|
||||
NameForAtom(selection), NameForAtom(target),
|
||||
NameForAtom(property), NameForAtom(prop->type));
|
||||
|
||||
if (target != property)
|
||||
return;
|
||||
|
||||
if (target == xaTARGETS) {
|
||||
if (prop->format != 32)
|
||||
return;
|
||||
if (prop->type != XA_ATOM)
|
||||
return;
|
||||
|
||||
if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size))
|
||||
vncSelectionRequest(selection, xaSTRING);
|
||||
else if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size))
|
||||
vncSelectionRequest(selection, xaUTF8_STRING);
|
||||
} else if (target == xaSTRING) {
|
||||
if (prop->format != 8)
|
||||
return;
|
||||
if (prop->type != xaSTRING)
|
||||
return;
|
||||
|
||||
vncServerCutText(prop->data, prop->size);
|
||||
} else if (target == xaUTF8_STRING) {
|
||||
unsigned char* buffer;
|
||||
unsigned char* out;
|
||||
size_t len;
|
||||
|
||||
const unsigned char* in;
|
||||
size_t in_len;
|
||||
|
||||
if (prop->format != 8)
|
||||
return;
|
||||
if (prop->type != xaUTF8_STRING)
|
||||
return;
|
||||
|
||||
buffer = malloc(prop->size);
|
||||
if (buffer == NULL)
|
||||
return;
|
||||
|
||||
out = buffer;
|
||||
len = 0;
|
||||
in = prop->data;
|
||||
in_len = prop->size;
|
||||
while (in_len > 0) {
|
||||
if ((*in & 0x80) == 0x00) {
|
||||
*out++ = *in++;
|
||||
len++;
|
||||
in_len--;
|
||||
} else if ((*in & 0xe0) == 0xc0) {
|
||||
unsigned ucs;
|
||||
ucs = (*in++ & 0x1f) << 6;
|
||||
in_len--;
|
||||
if (in_len > 0) {
|
||||
ucs |= (*in++ & 0x3f);
|
||||
in_len--;
|
||||
}
|
||||
if (ucs <= 0xff)
|
||||
*out++ = ucs;
|
||||
else
|
||||
*out++ = '?';
|
||||
len++;
|
||||
} else {
|
||||
*out++ = '?';
|
||||
len++;
|
||||
do {
|
||||
in++;
|
||||
in_len--;
|
||||
} while ((in_len > 0) && ((*in & 0xc0) == 0x80));
|
||||
}
|
||||
}
|
||||
|
||||
vncServerCutText((const char*)buffer, len);
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#define SEND_EVENT_BIT 0x80
|
||||
|
||||
static int vncProcSendEvent(ClientPtr client)
|
||||
{
|
||||
REQUEST(xSendEventReq);
|
||||
REQUEST_SIZE_MATCH(xSendEventReq);
|
||||
|
||||
stuff->event.u.u.type &= ~(SEND_EVENT_BIT);
|
||||
|
||||
if (stuff->event.u.u.type == SelectionNotify &&
|
||||
stuff->event.u.selectionNotify.requestor == wid) {
|
||||
TimeStamp time;
|
||||
time = ClientTimeToServerTime(stuff->event.u.selectionNotify.time);
|
||||
vncHandleSelection(stuff->event.u.selectionNotify.selection,
|
||||
stuff->event.u.selectionNotify.target,
|
||||
stuff->event.u.selectionNotify.property,
|
||||
stuff->event.u.selectionNotify.requestor,
|
||||
time);
|
||||
}
|
||||
|
||||
return origProcSendEvent(client);
|
||||
}
|
||||
|
||||
static void vncSelectionCallback(CallbackListPtr *callbacks,
|
||||
void * data, void * args)
|
||||
{
|
||||
SelectionInfoRec *info = (SelectionInfoRec *) args;
|
||||
|
||||
if (info->kind != SelectionSetOwner)
|
||||
return;
|
||||
if (info->client == serverClient)
|
||||
return;
|
||||
|
||||
LOG_DEBUG("Selection owner change for %s",
|
||||
NameForAtom(info->selection->selection));
|
||||
|
||||
if ((info->selection->selection != xaPRIMARY) &&
|
||||
(info->selection->selection != xaCLIPBOARD))
|
||||
return;
|
||||
|
||||
if ((info->selection->selection == xaPRIMARY) &&
|
||||
!vncGetSendPrimary())
|
||||
return;
|
||||
|
||||
vncSelectionRequest(info->selection->selection, xaTARGETS);
|
||||
}
|
33
unix/xserver/hw/vnc/vncSelection.h
Normal file
33
unix/xserver/hw/vnc/vncSelection.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* 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 __SELECTION_H__
|
||||
#define __SELECTION_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void vncSelectionInit(void);
|
||||
|
||||
void vncClientCutText(const char* str, int len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
61
unix/xserver/hw/vnc/xorg-version.h
Normal file
61
unix/xserver/hw/vnc/xorg-version.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* Copyright (C) 2009 TightVNC Team
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
*
|
||||
* 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 XORG_VERSION_H
|
||||
#define XORG_VERSION_H
|
||||
|
||||
#ifdef HAVE_DIX_CONFIG_H
|
||||
#include <dix-config.h>
|
||||
#endif
|
||||
|
||||
#if XORG_VERSION_CURRENT < ((1 * 10000000) + (6 * 100000) + (99 * 1000))
|
||||
#error "X.Org older than 1.7 is not supported"
|
||||
#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (7 * 100000) + (99 * 1000))
|
||||
#define XORG 17
|
||||
#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (8 * 100000) + (99 * 1000))
|
||||
#define XORG 18
|
||||
#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (9 * 100000) + (99 * 1000))
|
||||
#define XORG 19
|
||||
#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (10 * 100000) + (99 * 1000))
|
||||
#define XORG 110
|
||||
#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (11 * 100000) + (99 * 1000))
|
||||
#define XORG 111
|
||||
#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (12 * 100000) + (99 * 1000))
|
||||
#define XORG 112
|
||||
#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (13 * 100000) + (99 * 1000))
|
||||
#define XORG 113
|
||||
#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (14 * 100000) + (99 * 1000))
|
||||
#define XORG 114
|
||||
#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (15 * 100000) + (99 * 1000))
|
||||
#define XORG 115
|
||||
#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (16 * 100000) + (99 * 1000))
|
||||
#define XORG 116
|
||||
#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (17 * 100000) + (99 * 1000))
|
||||
#define XORG 117
|
||||
#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (18 * 100000) + (99 * 1000))
|
||||
#define XORG 118
|
||||
#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (19 * 100000) + (99 * 1000))
|
||||
#define XORG 119
|
||||
#elif XORG_VERSION_CURRENT < ((1 * 10000000) + (20 * 100000) + (99 * 1000))
|
||||
#define XORG 120
|
||||
#else
|
||||
#error "X.Org newer than 1.20 is not supported"
|
||||
#endif
|
||||
|
||||
#endif
|
1854
unix/xserver/hw/vnc/xvnc.c
Normal file
1854
unix/xserver/hw/vnc/xvnc.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user