@@ -36,6 +36,10 @@ | |||
// is assigned a particular log level. | |||
#define DEF_LOGFUNCTION(name, level) \ | |||
inline void name(const char* fmt, va_list ap) { \ | |||
if (m_log && (level <= m_level)) \ | |||
m_log->write(level, m_name, fmt, ap);\ | |||
} \ | |||
inline void name(const char* fmt, ...) __printf_attr(2, 3) { \ | |||
if (m_log && (level <= m_level)) { \ | |||
va_list ap; va_start(ap, fmt); \ |
@@ -22,6 +22,7 @@ | |||
#define __RFB_SCREENSET_INCLUDED__ | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <rdr/types.h> | |||
#include <rfb/Rect.h> | |||
@@ -108,15 +109,20 @@ namespace rfb { | |||
return true; | |||
}; | |||
inline void debug_print(void) const { | |||
inline void print(char* str, size_t len) const { | |||
char buffer[128]; | |||
std::list<Screen>::const_iterator iter; | |||
fprintf(stderr, "%d screens\n", num_screens()); | |||
snprintf(buffer, sizeof(buffer), "%d screen(s)\n", num_screens()); | |||
str[0] = '\0'; | |||
strncat(str, buffer, len - 1 - strlen(str)); | |||
for (iter = screens.begin();iter != screens.end();++iter) { | |||
fprintf(stderr, " %10d (0x%08x): %dx%d+%d+%d (flags 0x%08x)\n", | |||
(int)iter->id, (unsigned)iter->id, | |||
iter->dimensions.width(), iter->dimensions.height(), | |||
iter->dimensions.tl.x, iter->dimensions.tl.y, | |||
(unsigned)iter->flags); | |||
snprintf(buffer, sizeof(buffer), | |||
" %10d (0x%08x): %dx%d+%d+%d (flags 0x%08x)\n", | |||
(int)iter->id, (unsigned)iter->id, | |||
iter->dimensions.width(), iter->dimensions.height(), | |||
iter->dimensions.tl.x, iter->dimensions.tl.y, | |||
(unsigned)iter->flags); | |||
strncat(str, buffer, len - 1 - strlen(str)); | |||
} | |||
}; | |||
@@ -97,4 +97,8 @@ rfb::BoolParameter rfb::Server::queryConnect | |||
("QueryConnect", | |||
"Prompt the local user to accept or reject incoming connections.", | |||
false); | |||
rfb::IntParameter rfb::Server::queryConnectTimeout | |||
("QueryConnectTimeout", | |||
"Number of seconds to show the Accept Connection dialog before " | |||
"rejecting the connection", | |||
10); |
@@ -48,6 +48,7 @@ namespace rfb { | |||
static BoolParameter sendCutText; | |||
static BoolParameter acceptSetDesktopSize; | |||
static BoolParameter queryConnect; | |||
static IntParameter queryConnectTimeout; | |||
}; | |||
@@ -101,7 +101,6 @@ int Timer::checkTimeouts() { | |||
while (pending.front()->isBefore(now)) { | |||
Timer* timer = pending.front(); | |||
pending.pop_front(); | |||
vlog.debug("handleTimeout(%p)", timer); | |||
if (timer->cb->handleTimeout(timer)) { | |||
timer->dueTime = addMillis(timer->dueTime, timer->timeoutMs); | |||
if (timer->isBefore(now)) { |
@@ -1,5 +1,5 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* Copyright 2009-2014 Pierre Ossman for Cendio AB | |||
* 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 | |||
@@ -66,7 +66,8 @@ struct RTTInfo { | |||
VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, | |||
bool reverse) | |||
: SConnection(reverse), sock(s), inProcessMessages(false), | |||
: SConnection(reverse), sock(s), | |||
queryConnectTimer(this), inProcessMessages(false), | |||
pendingSyncFence(false), syncFence(false), fenceFlags(0), | |||
fenceDataLen(0), fenceData(NULL), | |||
baseRTT(-1), minRTT(-1), seenCongestion(false), pingCounter(0), | |||
@@ -434,8 +435,10 @@ void VNCSConnectionST::queryConnection(const char* userName) | |||
CharArray reason; | |||
VNCServerST::queryResult qr = server->queryConnection(sock, userName, | |||
&reason.buf); | |||
if (qr == VNCServerST::PENDING) | |||
if (qr == VNCServerST::PENDING) { | |||
queryConnectTimer.start(rfb::Server::queryConnectTimeout * 1000); | |||
return; | |||
} | |||
// - If server returns ACCEPT/REJECT then pass result to SConnection | |||
approveConnection(qr == VNCServerST::ACCEPT, reason.buf); | |||
@@ -714,6 +717,10 @@ bool VNCSConnectionST::handleTimeout(Timer* t) | |||
writeFramebufferUpdate(); | |||
else if (t == &congestionTimer) | |||
updateCongestion(); | |||
else if (t == &queryConnectTimer) { | |||
if (state() == RFBSTATE_QUERYING) | |||
approveConnection(false, "The attempt to prompt the user to accept the connection failed"); | |||
} | |||
} catch (rdr::Exception& e) { | |||
close(e.str()); | |||
} |
@@ -174,6 +174,8 @@ namespace rfb { | |||
network::Socket* sock; | |||
CharArray peerEndpoint; | |||
Timer queryConnectTimer; | |||
bool inProcessMessages; | |||
bool pendingSyncFence, syncFence; |
@@ -1,6 +1,6 @@ | |||
/* Copyright (C) 2009 TightVNC Team | |||
* Copyright (C) 2009, 2014 Red Hat, Inc. | |||
* Copyright 2013 Pierre Ossman for Cendio AB | |||
* 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 | |||
@@ -22,14 +22,11 @@ | |||
#include <dix-config.h> | |||
#endif | |||
#include <rfb/LogWriter.h> | |||
#include "Input.h" | |||
#include "xorg-version.h" | |||
#include "Input.h" | |||
#include "vncExtInit.h" | |||
extern "C" { | |||
#define public c_public | |||
#define class c_class | |||
#include "inputstr.h" | |||
#if XORG >= 110 | |||
#include "inpututils.h" | |||
@@ -51,26 +48,9 @@ extern _X_EXPORT DevPrivateKey CoreDevicePrivateKey; | |||
#include <X11/keysym.h> | |||
#include <X11/Xlib.h> | |||
#include <X11/Xutil.h> | |||
#undef public | |||
#undef class | |||
} | |||
#if XORG >= 110 | |||
#define Xfree free | |||
#endif | |||
using namespace rdr; | |||
using namespace rfb; | |||
static LogWriter vlog("Input"); | |||
rfb::BoolParameter avoidShiftNumLock("AvoidShiftNumLock", "Avoid fake Shift presses for keys affected by NumLock.", true); | |||
#define BUTTONS 7 | |||
class InputDevice *vncInputDevice; | |||
InputDevice InputDevice::singleton; | |||
/* Event queue is shared between all devices. */ | |||
#if XORG == 15 | |||
static xEvent *eventq = NULL; | |||
@@ -78,9 +58,91 @@ static xEvent *eventq = NULL; | |||
static EventList *eventq = NULL; | |||
#endif | |||
#if XORG < 111 | |||
static void initEventq(void) | |||
DeviceIntPtr vncKeyboardDev; | |||
DeviceIntPtr vncPointerDev; | |||
static int oldButtonMask; | |||
static int cursorPosX, cursorPosY; | |||
static KeySym pressedKeys[256]; | |||
#if XORG < 17 | |||
extern void vncGetInitKeyboardMap(KeySymsPtr keysyms, CARD8 *modmap); | |||
#endif | |||
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); | |||
#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 extesions | |||
* 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; | |||
for (i = 0;i < 256;i++) | |||
pressedKeys[i] = NoSymbol; | |||
#if XORG < 17 | |||
vncPointerDev = AddInputDevice( | |||
#if XORG >= 16 | |||
serverClient, | |||
#endif | |||
vncPointerProc, TRUE); | |||
RegisterPointerDevice(vncPointerDev); | |||
vncKeyboardDev = AddInputDevice( | |||
#if XORG >= 16 | |||
serverClient, | |||
#endif | |||
vncKeyboardProc, TRUE); | |||
RegisterKeyboardDevice(vncKeyboardDev); | |||
if (ActivateDevice(vncPointerDev) != Success || | |||
ActivateDevice(vncKeyboardDev) != Success) | |||
FatalError("Failed to activate TigerVNC devices\n"); | |||
if (!EnableDevice(vncPointerDev) || | |||
!EnableDevice(vncKeyboardDev)) | |||
FatalError("Failed to enable TigerVNC devices\n"); | |||
#else /* < 17 */ | |||
ret = AllocDevicePair(serverClient, "TigerVNC", | |||
&vncPointerDev, &vncKeyboardDev, | |||
vncPointerProc, vncKeyboardProc, | |||
FALSE); | |||
if (ret != Success) | |||
FatalError("Failed to initialize TigerVNC input devices\n"); | |||
if (ActivateDevice(vncPointerDev, TRUE) != Success || | |||
ActivateDevice(vncKeyboardDev, TRUE) != Success) | |||
FatalError("Failed to activate TigerVNC devices\n"); | |||
if (!EnableDevice(vncPointerDev, TRUE) || | |||
!EnableDevice(vncKeyboardDev, TRUE)) | |||
FatalError("Failed to activate TigerVNC devices\n"); | |||
#endif /* 17 */ | |||
#if XORG < 111 | |||
/* eventq is never free()-ed because it exists during server life. */ | |||
if (eventq == NULL) { | |||
#if XORG == 15 | |||
@@ -92,45 +154,12 @@ static void initEventq(void) | |||
GetEventList(&eventq); | |||
#endif | |||
} | |||
} | |||
#endif /* XORG < 111 */ | |||
#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, | |||
#if XORG == 15 | |||
eventq + i | |||
#elif XORG == 16 | |||
(eventq + i)->event | |||
#else | |||
(InternalEvent *) (eventq + i)->event | |||
#endif | |||
); | |||
} | |||
} | |||
#endif /* XORG < 111 */ | |||
InputDevice::InputDevice() | |||
: oldButtonMask(0) | |||
{ | |||
int i; | |||
vncInputDevice = this; | |||
for (i = 0;i < 256;i++) | |||
pressedKeys[i] = NoSymbol; | |||
vncPrepareInputDevices(); | |||
} | |||
void InputDevice::PointerButtonAction(int buttonMask) | |||
void vncPointerButtonAction(int buttonMask) | |||
{ | |||
int i; | |||
#if XORG < 111 | |||
@@ -145,17 +174,19 @@ void InputDevice::PointerButtonAction(int buttonMask) | |||
int action = (buttonMask & (1<<i)) ? | |||
ButtonPress : ButtonRelease; | |||
#if XORG < 110 | |||
n = GetPointerEvents(eventq, pointerDev, action, i + 1, | |||
n = GetPointerEvents(eventq, vncPointerDev, | |||
action, i + 1, | |||
POINTER_RELATIVE, 0, 0, NULL); | |||
enqueueEvents(pointerDev, n); | |||
enqueueEvents(vncPointerDev, n); | |||
#elif XORG < 111 | |||
valuator_mask_set_range(&mask, 0, 0, NULL); | |||
n = GetPointerEvents(eventq, pointerDev, action, i + 1, | |||
n = GetPointerEvents(eventq, vncPointerDev, | |||
action, i + 1, | |||
POINTER_RELATIVE, &mask); | |||
enqueueEvents(pointerDev, n); | |||
enqueueEvents(vncPointerDev, n); | |||
#else | |||
valuator_mask_set_range(&mask, 0, 0, NULL); | |||
QueuePointerEvents(pointerDev, action, i + 1, | |||
QueuePointerEvents(vncPointerDev, action, i + 1, | |||
POINTER_RELATIVE, &mask); | |||
#endif | |||
} | |||
@@ -164,7 +195,7 @@ void InputDevice::PointerButtonAction(int buttonMask) | |||
oldButtonMask = buttonMask; | |||
} | |||
void InputDevice::PointerMove(const rfb::Point &pos) | |||
void vncPointerMove(int x, int y) | |||
{ | |||
int valuators[2]; | |||
#if XORG < 111 | |||
@@ -174,42 +205,64 @@ void InputDevice::PointerMove(const rfb::Point &pos) | |||
ValuatorMask mask; | |||
#endif | |||
if (pos.equals(cursorPos)) | |||
if (cursorPosX == x && cursorPosY == y) | |||
return; | |||
valuators[0] = pos.x; | |||
valuators[1] = pos.y; | |||
valuators[0] = x; | |||
valuators[1] = y; | |||
#if XORG < 110 | |||
n = GetPointerEvents(eventq, pointerDev, MotionNotify, 0, POINTER_ABSOLUTE, 0, | |||
2, valuators); | |||
enqueueEvents(pointerDev, n); | |||
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, pointerDev, MotionNotify, 0, POINTER_ABSOLUTE, | |||
&mask); | |||
enqueueEvents(pointerDev, n); | |||
n = GetPointerEvents(eventq, vncPointerDev, MotionNotify, 0, | |||
POINTER_ABSOLUTE, &mask); | |||
enqueueEvents(vncPointerDev, n); | |||
#else | |||
valuator_mask_set_range(&mask, 0, 2, valuators); | |||
QueuePointerEvents(pointerDev, MotionNotify, 0, POINTER_ABSOLUTE, &mask); | |||
QueuePointerEvents(vncPointerDev, MotionNotify, 0, | |||
POINTER_ABSOLUTE, &mask); | |||
#endif | |||
cursorPos = pos; | |||
cursorPosX = x; | |||
cursorPosY = y; | |||
} | |||
const rfb::Point &InputDevice::getPointerPos(void) | |||
void vncGetPointerPos(int *x, int *y) | |||
{ | |||
if (pointerDev != NULL) { | |||
int x, y; | |||
if (vncPointerDev != NULL) | |||
GetSpritePosition(vncPointerDev, &cursorPosX, &cursorPosY); | |||
GetSpritePosition (pointerDev, &x, &y); | |||
cursorPos.x = x; | |||
cursorPos.y = y; | |||
} | |||
*x = cursorPosX; | |||
*y = cursorPosY; | |||
} | |||
#if XORG < 111 | |||
static void enqueueEvents(DeviceIntPtr dev, int n) | |||
{ | |||
int i; | |||
return cursorPos; | |||
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, | |||
#if XORG == 15 | |||
eventq + i | |||
#elif XORG == 16 | |||
(eventq + i)->event | |||
#else | |||
(InternalEvent *) (eventq + i)->event | |||
#endif | |||
); | |||
} | |||
} | |||
#endif /* XORG < 111 */ | |||
int InputDevice::pointerProc(DeviceIntPtr pDevice, int onoff) | |||
static int vncPointerProc(DeviceIntPtr pDevice, int onoff) | |||
{ | |||
BYTE map[BUTTONS + 1]; | |||
DevicePtr pDev = (DevicePtr)pDevice; | |||
@@ -261,21 +314,21 @@ int InputDevice::pointerProc(DeviceIntPtr pDevice, int onoff) | |||
pDev->on = FALSE; | |||
break; | |||
case DEVICE_CLOSE: | |||
singleton.pointerDev = NULL; | |||
vncPointerDev = NULL; | |||
break; | |||
} | |||
return Success; | |||
} | |||
static void keyboardBell(int percent, DeviceIntPtr device, void * ctrl, | |||
int class_) | |||
static void vncKeyboardBell(int percent, DeviceIntPtr device, | |||
void * ctrl, int class) | |||
{ | |||
if (percent > 0) | |||
vncBell(); | |||
} | |||
int InputDevice::keyboardProc(DeviceIntPtr pDevice, int onoff) | |||
static int vncKeyboardProc(DeviceIntPtr pDevice, int onoff) | |||
{ | |||
#if XORG < 17 | |||
KeySymsRec keySyms; | |||
@@ -286,7 +339,7 @@ int InputDevice::keyboardProc(DeviceIntPtr pDevice, int onoff) | |||
switch (onoff) { | |||
case DEVICE_INIT: | |||
#if XORG < 17 | |||
GetInitKeyboardMap(&keySyms, modMap); | |||
vncGetInitKeyboardMap(&keySyms, modMap); | |||
#endif | |||
InitKeyboardDeviceStruct( | |||
#if XORG >= 17 | |||
@@ -294,7 +347,8 @@ int InputDevice::keyboardProc(DeviceIntPtr pDevice, int onoff) | |||
#else | |||
pDev, &keySyms, modMap, | |||
#endif | |||
keyboardBell, (KbdCtrlProcPtr)NoopDDA); | |||
vncKeyboardBell, | |||
(KbdCtrlProcPtr)NoopDDA); | |||
break; | |||
case DEVICE_ON: | |||
pDev->on = TRUE; | |||
@@ -303,67 +357,14 @@ int InputDevice::keyboardProc(DeviceIntPtr pDevice, int onoff) | |||
pDev->on = FALSE; | |||
break; | |||
case DEVICE_CLOSE: | |||
singleton.keyboardDev = NULL; | |||
vncKeyboardDev = NULL; | |||
break; | |||
} | |||
return Success; | |||
} | |||
void InputDevice::InitInputDevice(void) | |||
{ | |||
if ((pointerDev != NULL) || (keyboardDev != NULL)) | |||
return; | |||
#if XORG < 17 | |||
pointerDev = AddInputDevice( | |||
#if XORG >= 16 | |||
serverClient, | |||
#endif | |||
pointerProc, TRUE); | |||
RegisterPointerDevice(pointerDev); | |||
keyboardDev = AddInputDevice( | |||
#if XORG >= 16 | |||
serverClient, | |||
#endif | |||
keyboardProc, TRUE); | |||
RegisterKeyboardDevice(keyboardDev); | |||
if (ActivateDevice(pointerDev) != Success || | |||
ActivateDevice(keyboardDev) != Success) | |||
FatalError("Failed to activate TigerVNC devices\n"); | |||
if (!EnableDevice(pointerDev) || | |||
!EnableDevice(keyboardDev)) | |||
FatalError("Failed to enable TigerVNC devices\n"); | |||
#else /* < 17 */ | |||
int ret; | |||
ret = AllocDevicePair(serverClient, "TigerVNC", &pointerDev, | |||
&keyboardDev, pointerProc, keyboardProc, | |||
FALSE); | |||
if (ret != Success) | |||
FatalError("Failed to initialize TigerVNC input devices\n"); | |||
if (ActivateDevice(pointerDev, TRUE) != Success || | |||
ActivateDevice(keyboardDev, TRUE) != Success) | |||
FatalError("Failed to activate TigerVNC devices\n"); | |||
if (!EnableDevice(pointerDev, TRUE) || | |||
!EnableDevice(keyboardDev, TRUE)) | |||
FatalError("Failed to activate TigerVNC devices\n"); | |||
#endif /* 17 */ | |||
#if XORG < 111 | |||
initEventq(); | |||
#endif | |||
PrepareInputDevices(); | |||
} | |||
static inline void pressKey(DeviceIntPtr dev, int kc, bool down, const char *msg) | |||
static inline void pressKey(DeviceIntPtr dev, int kc, Bool down, const char *msg) | |||
{ | |||
int action; | |||
#if XORG < 111 | |||
@@ -371,7 +372,7 @@ static inline void pressKey(DeviceIntPtr dev, int kc, bool down, const char *msg | |||
#endif | |||
if (msg != NULL) | |||
vlog.debug("%s %d %s", msg, kc, down ? "down" : "up"); | |||
LOG_DEBUG("%s %d %s", msg, kc, down ? "down" : "up"); | |||
action = down ? KeyPress : KeyRelease; | |||
#if XORG < 111 | |||
@@ -432,14 +433,14 @@ static struct altKeysym_t { | |||
}; | |||
/* | |||
* keyEvent() - 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 | |||
* vncKeyboardEvent() - 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. | |||
*/ | |||
void InputDevice::keyEvent(rdr::U32 keysym, bool down) | |||
void vncKeyboardEvent(KeySym keysym, int down) | |||
{ | |||
int i; | |||
unsigned state, new_state; | |||
@@ -447,7 +448,8 @@ void InputDevice::keyEvent(rdr::U32 keysym, bool down) | |||
unsigned level_three_mask; | |||
KeyCode shift_press, level_three_press; | |||
std::list<KeyCode> shift_release, level_three_release; | |||
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 | |||
@@ -457,7 +459,7 @@ void InputDevice::keyEvent(rdr::U32 keysym, bool down) | |||
for (i = 0;i < 256;i++) { | |||
if (pressedKeys[i] == keysym) { | |||
pressedKeys[i] = NoSymbol; | |||
pressKey(keyboardDev, i, false, "keycode"); | |||
pressKey(vncKeyboardDev, i, FALSE, "keycode"); | |||
mieqProcessInputEvents(); | |||
return; | |||
} | |||
@@ -467,7 +469,7 @@ void InputDevice::keyEvent(rdr::U32 keysym, bool down) | |||
* This can happen quite often as we ignore some | |||
* key presses. | |||
*/ | |||
vlog.debug("Unexpected release of keysym 0x%x", keysym); | |||
LOG_DEBUG("Unexpected release of keysym 0x%x", keysym); | |||
return; | |||
} | |||
@@ -479,9 +481,9 @@ void InputDevice::keyEvent(rdr::U32 keysym, bool down) | |||
*/ | |||
mieqProcessInputEvents(); | |||
state = getKeyboardState(); | |||
state = vncGetKeyboardState(); | |||
keycode = keysymToKeycode(keysym, state, &new_state); | |||
keycode = vncKeysymToKeycode(keysym, state, &new_state); | |||
/* Try some equivalent keysyms if we couldn't find a perfect match */ | |||
if (keycode == 0) { | |||
@@ -495,37 +497,37 @@ void InputDevice::keyEvent(rdr::U32 keysym, bool down) | |||
else | |||
continue; | |||
keycode = keysymToKeycode(altsym, state, &new_state); | |||
keycode = vncKeysymToKeycode(altsym, state, &new_state); | |||
if (keycode != 0) | |||
break; | |||
} | |||
} | |||
/* We don't have lock synchronisation... */ | |||
if (isLockModifier(keycode, new_state)) { | |||
vlog.debug("Ignoring lock key (e.g. caps lock)"); | |||
if (vncIsLockModifier(keycode, new_state)) { | |||
LOG_DEBUG("Ignoring lock key (e.g. caps lock)"); | |||
return; | |||
} | |||
/* No matches. Will have to add a new entry... */ | |||
if (keycode == 0) { | |||
keycode = addKeysym(keysym, state); | |||
keycode = vncAddKeysym(keysym, state); | |||
if (keycode == 0) { | |||
vlog.error("Failure adding new keysym 0x%x", keysym); | |||
LOG_ERROR("Failure adding new keysym 0x%x", keysym); | |||
return; | |||
} | |||
vlog.info("Added unknown keysym 0x%x to keycode %d", | |||
keysym, keycode); | |||
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 = keysymToKeycode(keysym, state, &new_state); | |||
keycode = vncKeysymToKeycode(keysym, state, &new_state); | |||
if (keycode == 0) { | |||
vlog.error("Newly added keysym 0x%x cannot be generated", keysym); | |||
LOG_ERROR("Newly added keysym 0x%x cannot be generated", keysym); | |||
return; | |||
} | |||
} | |||
@@ -542,11 +544,11 @@ void InputDevice::keyEvent(rdr::U32 keysym, bool down) | |||
* can use an alternative keysym. | |||
*/ | |||
if (((state & ShiftMask) != (new_state & ShiftMask)) && | |||
avoidShiftNumLock && isAffectedByNumLock(keycode)) { | |||
vncGetAvoidShiftNumLock() && vncIsAffectedByNumLock(keycode)) { | |||
KeyCode keycode2; | |||
unsigned new_state2; | |||
vlog.debug("Finding alternative to keysym 0x%x to avoid fake shift for numpad", keysym); | |||
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; | |||
@@ -558,19 +560,19 @@ void InputDevice::keyEvent(rdr::U32 keysym, bool down) | |||
else | |||
continue; | |||
keycode2 = keysymToKeycode(altsym, state, &new_state2); | |||
keycode2 = vncKeysymToKeycode(altsym, state, &new_state2); | |||
if (keycode2 == 0) | |||
continue; | |||
if (((state & ShiftMask) != (new_state2 & ShiftMask)) && | |||
isAffectedByNumLock(keycode2)) | |||
vncIsAffectedByNumLock(keycode2)) | |||
continue; | |||
break; | |||
} | |||
if (i == sizeof(altKeysym)/sizeof(altKeysym[0])) | |||
vlog.debug("No alternative keysym found"); | |||
LOG_DEBUG("No alternative keysym found"); | |||
else { | |||
keycode = keycode2; | |||
new_state = new_state2; | |||
@@ -598,65 +600,64 @@ void InputDevice::keyEvent(rdr::U32 keysym, bool down) | |||
* so we need to know what the mask is for level 3 shifts. | |||
*/ | |||
if ((new_state & ~ShiftMask) != (state & ~ShiftMask)) | |||
level_three_mask = getLevelThreeMask(); | |||
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 = pressShift(); | |||
shift_press = vncPressShift(); | |||
if (shift_press == 0) { | |||
vlog.error("Unable to find a modifier key for Shift"); | |||
LOG_ERROR("Unable to find a modifier key for Shift"); | |||
return; | |||
} | |||
pressKey(keyboardDev, shift_press, true, "temp shift"); | |||
pressKey(vncKeyboardDev, shift_press, TRUE, "temp shift"); | |||
} else if ((state & ShiftMask) && !(new_state & ShiftMask)) { | |||
std::list<KeyCode>::const_iterator iter; | |||
shift_release = releaseShift(); | |||
if (shift_release.empty()) { | |||
vlog.error("Unable to find the modifier key(s) for releasing Shift"); | |||
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 (iter = shift_release.begin();iter != shift_release.end();++iter) | |||
pressKey(keyboardDev, *iter, false, "temp shift"); | |||
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 = pressLevelThree(); | |||
level_three_press = vncPressLevelThree(); | |||
if (level_three_press == 0) { | |||
vlog.error("Unable to find a modifier key for ISO_Level3_Shift/Mode_Switch"); | |||
LOG_ERROR("Unable to find a modifier key for ISO_Level3_Shift/Mode_Switch"); | |||
return; | |||
} | |||
pressKey(keyboardDev, level_three_press, true, "temp level 3 shift"); | |||
pressKey(vncKeyboardDev, level_three_press, TRUE, "temp level 3 shift"); | |||
} else if ((state & level_three_mask) && !(new_state & level_three_mask)) { | |||
std::list<KeyCode>::const_iterator iter; | |||
level_three_release = releaseLevelThree(); | |||
if (level_three_release.empty()) { | |||
vlog.error("Unable to find the modifier key(s) for releasing ISO_Level3_Shift/Mode_Switch"); | |||
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 (iter = level_three_release.begin();iter != level_three_release.end();++iter) | |||
pressKey(keyboardDev, *iter, false, "temp level 3 shift"); | |||
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(keyboardDev, keycode, true, "keycode"); | |||
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) { | |||
vlog.error("Keysym 0x%x generated by both keys %d and %d", keysym, i, keycode); | |||
LOG_ERROR("Keysym 0x%x generated by both keys %d and %d", keysym, i, keycode); | |||
pressedKeys[i] = NoSymbol; | |||
} | |||
} | |||
@@ -665,20 +666,18 @@ void InputDevice::keyEvent(rdr::U32 keysym, bool down) | |||
/* Undo any fake level three shift */ | |||
if (level_three_press != 0) | |||
pressKey(keyboardDev, level_three_press, false, "temp level 3 shift"); | |||
else if (!level_three_release.empty()) { | |||
std::list<KeyCode>::const_iterator iter; | |||
for (iter = level_three_release.begin();iter != level_three_release.end();++iter) | |||
pressKey(keyboardDev, *iter, true, "temp level 3 shift"); | |||
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(keyboardDev, shift_press, false, "temp shift"); | |||
else if (!shift_release.empty()) { | |||
std::list<KeyCode>::const_iterator iter; | |||
for (iter = shift_release.begin();iter != shift_release.end();++iter) | |||
pressKey(keyboardDev, *iter, true, "temp shift"); | |||
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"); | |||
} | |||
/* |
@@ -1,7 +1,7 @@ | |||
/* Copyright (C) 2009 TightVNC Team | |||
* Copyright (C) 2009, 2010 Red Hat, Inc. | |||
* Copyright (C) 2009, 2010 TigerVNC Team | |||
* Copyright 2013 Pierre Ossman for Cendio AB | |||
* 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 | |||
@@ -23,109 +23,44 @@ | |||
#ifndef INPUT_H_ | |||
#define INPUT_H_ | |||
#ifdef HAVE_DIX_CONFIG_H | |||
#include <dix-config.h> | |||
#endif | |||
#include <list> | |||
#include <rdr/types.h> | |||
#include <rfb/Rect.h> | |||
#include <stdlib.h> | |||
#include <X11/X.h> | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#include "input.h" | |||
/* The Xorg headers define macros that wreak havoc with STL */ | |||
#undef max | |||
}; | |||
#include "xorg-version.h" | |||
/* | |||
* Represents input device (keyboard + pointer) | |||
* | |||
* Is a singleton as input devices are global in the X server so | |||
* we do not have one per desktop (i.e. per screen). | |||
*/ | |||
extern class InputDevice *vncInputDevice; | |||
class InputDevice { | |||
public: | |||
/* | |||
* Press or release buttons. Relationship between buttonMask and | |||
* buttons is specified in RFB protocol. | |||
*/ | |||
void PointerButtonAction(int buttonMask); | |||
/* Move pointer to target location (point coords are absolute). */ | |||
void PointerMove(const rfb::Point &point); | |||
/* Get current known location of the pointer */ | |||
const rfb::Point &getPointerPos(void); | |||
/* Press or release one or more keys to get the given symbol */ | |||
void KeyboardPress(rdr::U32 keysym) { keyEvent(keysym, true); } | |||
void KeyboardRelease(rdr::U32 keysym) { keyEvent(keysym, false); } | |||
#endif | |||
/* | |||
* Init input device. | |||
* This has to be called after core pointer/keyboard | |||
* initialization which unfortunately is after extesions | |||
* 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 InitInputDevice(void); | |||
void vncInitInputDevice(void); | |||
private: | |||
InputDevice(); | |||
void vncPointerButtonAction(int buttonMask); | |||
void vncPointerMove(int x, int y); | |||
void vncGetPointerPos(int *x, int *y); | |||
void keyEvent(rdr::U32 keysym, bool down); | |||
void vncKeyboardEvent(KeySym keysym, int down); | |||
/* Backend dependent functions below here */ | |||
void PrepareInputDevices(void); | |||
/* Backend dependent functions below here */ | |||
unsigned getKeyboardState(void); | |||
unsigned getLevelThreeMask(void); | |||
void vncPrepareInputDevices(void); | |||
KeyCode pressShift(void); | |||
std::list<KeyCode> releaseShift(void); | |||
unsigned vncGetKeyboardState(void); | |||
unsigned vncGetLevelThreeMask(void); | |||
KeyCode pressLevelThree(void); | |||
std::list<KeyCode> releaseLevelThree(void); | |||
KeyCode vncPressShift(void); | |||
size_t vncReleaseShift(KeyCode *keys, size_t maxKeys); | |||
KeyCode keysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state); | |||
KeyCode vncPressLevelThree(void); | |||
size_t vncReleaseLevelThree(KeyCode *keys, size_t maxKeys); | |||
bool isLockModifier(KeyCode keycode, unsigned state); | |||
KeyCode vncKeysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state); | |||
bool isAffectedByNumLock(KeyCode keycode); | |||
int vncIsLockModifier(KeyCode keycode, unsigned state); | |||
KeyCode addKeysym(KeySym keysym, unsigned state); | |||
int vncIsAffectedByNumLock(KeyCode keycode); | |||
private: | |||
static int pointerProc(DeviceIntPtr pDevice, int onoff); | |||
static int keyboardProc(DeviceIntPtr pDevice, int onoff); | |||
KeyCode vncAddKeysym(KeySym keysym, unsigned state); | |||
#if XORG >= 17 | |||
static void vncXkbProcessDeviceEvent(int screenNum, | |||
InternalEvent *event, | |||
DeviceIntPtr dev); | |||
#else | |||
static void GetInitKeyboardMap(KeySymsPtr keysyms, CARD8 *modmap); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
private: | |||
DeviceIntPtr keyboardDev; | |||
DeviceIntPtr pointerDev; | |||
int oldButtonMask; | |||
rfb::Point cursorPos; | |||
KeySym pressedKeys[256]; | |||
private: | |||
static InputDevice singleton; | |||
}; | |||
#endif |
@@ -1,6 +1,6 @@ | |||
/* Copyright (C) 2009 TightVNC Team | |||
* Copyright (C) 2009 Red Hat, Inc. | |||
* Copyright 2013 Pierre Ossman for Cendio AB | |||
* 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 | |||
@@ -25,9 +25,6 @@ | |||
#include "Input.h" | |||
#include "xorg-version.h" | |||
extern "C" { | |||
#define public c_public | |||
#define class c_class | |||
#include "inputstr.h" | |||
#ifndef XKB_IN_SERVER | |||
#define XKB_IN_SERVER | |||
@@ -46,9 +43,8 @@ extern "C" { | |||
#include <X11/XF86keysym.h> | |||
#include <X11/Xlib.h> | |||
#include <X11/Xutil.h> | |||
#undef public | |||
#undef class | |||
} | |||
extern DeviceIntPtr vncKeyboardDev; | |||
#if XORG < 17 | |||
@@ -61,7 +57,7 @@ extern "C" { | |||
#define MAX_KEY 255 | |||
#define MAP_LEN (MAX_KEY - MIN_KEY + 1) | |||
#define KEYSYMS_PER_KEY 2 | |||
KeySym keyboardMap[MAP_LEN * KEYSYMS_PER_KEY] = { | |||
static KeySym keyboardMap[MAP_LEN * KEYSYMS_PER_KEY] = { | |||
NoSymbol, NoSymbol, | |||
XK_Escape, NoSymbol, | |||
XK_1, XK_exclam, | |||
@@ -174,7 +170,7 @@ KeySym keyboardMap[MAP_LEN * KEYSYMS_PER_KEY] = { | |||
XK_Menu, NoSymbol, | |||
}; | |||
void InputDevice::GetInitKeyboardMap(KeySymsPtr keysyms, CARD8 *modmap) | |||
void vncGetInitKeyboardMap(KeySymsPtr keysyms, CARD8 *modmap) | |||
{ | |||
int i; | |||
@@ -221,17 +217,17 @@ void InputDevice::GetInitKeyboardMap(KeySymsPtr keysyms, CARD8 *modmap) | |||
keysyms->map = keyboardMap; | |||
} | |||
void InputDevice::PrepareInputDevices(void) | |||
void vncPrepareInputDevices(void) | |||
{ | |||
/* Don't need to do anything here */ | |||
} | |||
unsigned InputDevice::getKeyboardState(void) | |||
unsigned vncGetKeyboardState(void) | |||
{ | |||
return keyboardDev->key->state; | |||
return vncKeyboardDev->key->state; | |||
} | |||
unsigned InputDevice::getLevelThreeMask(void) | |||
unsigned vncGetLevelThreeMask(void) | |||
{ | |||
int i, j, k; | |||
@@ -241,12 +237,12 @@ unsigned InputDevice::getLevelThreeMask(void) | |||
int maxKeysPerMod; | |||
CARD8 *modmap; | |||
minKeyCode = keyboardDev->key->curKeySyms.minKeyCode; | |||
mapWidth = keyboardDev->key->curKeySyms.mapWidth; | |||
map = keyboardDev->key->curKeySyms.map; | |||
minKeyCode = vncKeyboardDev->key->curKeySyms.minKeyCode; | |||
mapWidth = vncKeyboardDev->key->curKeySyms.mapWidth; | |||
map = vncKeyboardDev->key->curKeySyms.map; | |||
maxKeysPerMod = keyboardDev->key->maxKeysPerModifier; | |||
modmap = keyboardDev->key->modifierKeyMap; | |||
maxKeysPerMod = vncKeyboardDev->key->maxKeysPerModifier; | |||
modmap = vncKeyboardDev->key->modifierKeyMap; | |||
for (i = 3; i < 8; i++) { | |||
for (k = 0; k < maxKeysPerMod; k++) { | |||
@@ -268,41 +264,44 @@ unsigned InputDevice::getLevelThreeMask(void) | |||
return 0; | |||
} | |||
KeyCode InputDevice::pressShift(void) | |||
KeyCode vncPressShift(void) | |||
{ | |||
int maxKeysPerMod; | |||
maxKeysPerMod = keyboardDev->key->maxKeysPerModifier; | |||
return keyboardDev->key->modifierKeyMap[ShiftMapIndex * maxKeysPerMod]; | |||
maxKeysPerMod = vncKeyboardDev->key->maxKeysPerModifier; | |||
return vncKeyboardDev->key->modifierKeyMap[ShiftMapIndex * maxKeysPerMod]; | |||
} | |||
std::list<KeyCode> InputDevice::releaseShift(void) | |||
size_t vncReleaseShift(KeyCode *keys, size_t maxKeys) | |||
{ | |||
std::list<KeyCode> keys; | |||
size_t count; | |||
int maxKeysPerMod; | |||
maxKeysPerMod = keyboardDev->key->maxKeysPerModifier; | |||
count = 0; | |||
maxKeysPerMod = vncKeyboardDev->key->maxKeysPerModifier; | |||
for (int k = 0; k < maxKeysPerMod; k++) { | |||
int keycode; | |||
int index; | |||
index = ShiftMapIndex * maxKeysPerMod + k; | |||
keycode = keyboardDev->key->modifierKeyMap[index]; | |||
keycode = vncKeyboardDev->key->modifierKeyMap[index]; | |||
if (keycode == 0) | |||
continue; | |||
if (!IS_PRESSED(keyboardDev, keycode)) | |||
if (!IS_PRESSED(vncKeyboardDev, keycode)) | |||
continue; | |||
keys.push_back(keycode); | |||
if (count >= maxKeys) | |||
return 0; | |||
keys[count++] = keycode; | |||
} | |||
return keys; | |||
return count; | |||
} | |||
KeyCode InputDevice::pressLevelThree(void) | |||
KeyCode vncPressLevelThree(void) | |||
{ | |||
unsigned mask, index; | |||
int maxKeysPerMod; | |||
@@ -312,41 +311,44 @@ KeyCode InputDevice::pressLevelThree(void) | |||
return 0; | |||
index = ffs(mask) - 1; | |||
maxKeysPerMod = keyboardDev->key->maxKeysPerModifier; | |||
return keyboardDev->key->modifierKeyMap[index * maxKeysPerMod]; | |||
maxKeysPerMod = vncKeyboardDev->key->maxKeysPerModifier; | |||
return vncKeyboardDev->key->modifierKeyMap[index * maxKeysPerMod]; | |||
} | |||
std::list<KeyCode> InputDevice::releaseLevelThree(void) | |||
size_t vncReleaseLevelThree(KeyCode *keys, size_t maxKeys) | |||
{ | |||
std::list<KeyCode> keys; | |||
size_t count; | |||
unsigned mask, msindex; | |||
int maxKeysPerMod; | |||
mask = getLevelThreeMask(); | |||
if (mask == 0) | |||
return keys; | |||
return 0; | |||
msindex = ffs(mask) - 1; | |||
maxKeysPerMod = keyboardDev->key->maxKeysPerModifier; | |||
count = 0; | |||
maxKeysPerMod = vncKeyboardDev->key->maxKeysPerModifier; | |||
for (int k = 0; k < maxKeysPerMod; k++) { | |||
int keycode; | |||
int index; | |||
index = msindex * maxKeysPerMod + k; | |||
keycode = keyboardDev->key->modifierKeyMap[index]; | |||
keycode = vncKeyboardDev->key->modifierKeyMap[index]; | |||
if (keycode == 0) | |||
continue; | |||
if (!IS_PRESSED(keyboardDev, keycode)) | |||
if (!IS_PRESSED(vncKeyboardDev, keycode)) | |||
continue; | |||
keys.push_back(keycode); | |||
if (count >= maxKeys) | |||
return 0; | |||
keys[count++] = keycode; | |||
} | |||
return keys; | |||
return count; | |||
} | |||
static KeySym KeyCodetoKeySym(KeySymsPtr keymap, int keycode, int col) | |||
@@ -397,7 +399,7 @@ static KeySym KeyCodetoKeySym(KeySymsPtr keymap, int keycode, int col) | |||
return syms[col]; | |||
} | |||
KeyCode InputDevice::keysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state) | |||
KeyCode vncKeysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state) | |||
{ | |||
int i, j; | |||
unsigned mask; | |||
@@ -405,9 +407,9 @@ KeyCode InputDevice::keysymToKeycode(KeySym keysym, unsigned state, unsigned *ne | |||
KeySymsPtr keymap; | |||
int mapWidth; | |||
mask = getLevelThreeMask(); | |||
mask = vncGetLevelThreeMask(); | |||
keymap = &keyboardDev->key->curKeySyms; | |||
keymap = &vncKeyboardDev->key->curKeySyms; | |||
/* | |||
* Column 0 means both shift and "mode_switch" (AltGr) must be released, | |||
@@ -449,7 +451,7 @@ KeyCode InputDevice::keysymToKeycode(KeySym keysym, unsigned state, unsigned *ne | |||
return 0; | |||
} | |||
bool InputDevice::isLockModifier(KeyCode keycode, unsigned state) | |||
int vncIsLockModifier(KeyCode keycode, unsigned state) | |||
{ | |||
int i, j, k; | |||
@@ -461,12 +463,12 @@ bool InputDevice::isLockModifier(KeyCode keycode, unsigned state) | |||
int num_lock_index; | |||
minKeyCode = keyboardDev->key->curKeySyms.minKeyCode; | |||
mapWidth = keyboardDev->key->curKeySyms.mapWidth; | |||
map = keyboardDev->key->curKeySyms.map; | |||
minKeyCode = vncKeyboardDev->key->curKeySyms.minKeyCode; | |||
mapWidth = vncKeyboardDev->key->curKeySyms.mapWidth; | |||
map = vncKeyboardDev->key->curKeySyms.map; | |||
maxKeysPerMod = keyboardDev->key->maxKeysPerModifier; | |||
modmap = keyboardDev->key->modifierKeyMap; | |||
maxKeysPerMod = vncKeyboardDev->key->maxKeysPerModifier; | |||
modmap = vncKeyboardDev->key->modifierKeyMap; | |||
/* Caps Lock is fairly easy as it has a dedicated modmap entry */ | |||
for (k = 0; k < maxKeysPerMod; k++) { | |||
@@ -474,7 +476,7 @@ bool InputDevice::isLockModifier(KeyCode keycode, unsigned state) | |||
index = LockMapIndex * maxKeysPerMod + k; | |||
if (keycode == modmap[index]) | |||
return true; | |||
return 1; | |||
} | |||
/* For Num Lock we need to find the correct modmap entry */ | |||
@@ -500,7 +502,7 @@ bool InputDevice::isLockModifier(KeyCode keycode, unsigned state) | |||
done: | |||
if (num_lock_index == 0) | |||
return false; | |||
return 0; | |||
/* Now we can look in the modmap */ | |||
for (k = 0; k < maxKeysPerMod; k++) { | |||
@@ -508,43 +510,43 @@ done: | |||
index = num_lock_index * maxKeysPerMod + k; | |||
if (keycode == modmap[index]) | |||
return true; | |||
return 1; | |||
} | |||
return false; | |||
return 0; | |||
} | |||
bool InputDevice::isAffectedByNumLock(KeyCode keycode) | |||
int vncIsAffectedByNumLock(KeyCode keycode) | |||
{ | |||
KeySymsPtr keymap; | |||
int i, per; | |||
KeySym *syms; | |||
keymap = &keyboardDev->key->curKeySyms; | |||
keymap = &vncKeyboardDev->key->curKeySyms; | |||
per = keymap->mapWidth; | |||
syms = &keymap->map[(keycode - keymap->minKeyCode) * per]; | |||
for (i = 0;i < per;i++) { | |||
if (IsKeypadKey(syms[i])) | |||
return true; | |||
return 1; | |||
if (IsPrivateKeypadKey(syms[i])) | |||
return true; | |||
return 1; | |||
} | |||
return false; | |||
return 0; | |||
} | |||
KeyCode InputDevice::addKeysym(KeySym keysym, unsigned state) | |||
KeyCode vncAddKeysym(KeySym keysym, unsigned state) | |||
{ | |||
KeyCode kc; | |||
int minKeyCode, maxKeyCode, mapWidth; | |||
KeySym *map; | |||
minKeyCode = keyboardDev->key->curKeySyms.minKeyCode; | |||
maxKeyCode = keyboardDev->key->curKeySyms.maxKeyCode; | |||
mapWidth = keyboardDev->key->curKeySyms.mapWidth; | |||
map = keyboardDev->key->curKeySyms.map; | |||
minKeyCode = vncKeyboardDev->key->curKeySyms.minKeyCode; | |||
maxKeyCode = vncKeyboardDev->key->curKeySyms.maxKeyCode; | |||
mapWidth = vncKeyboardDev->key->curKeySyms.mapWidth; | |||
map = vncKeyboardDev->key->curKeySyms.map; | |||
/* | |||
* Magic, which dynamically adds keysym<->keycode mapping | |||
@@ -570,17 +572,17 @@ KeyCode InputDevice::addKeysym(KeySym keysym, unsigned state) | |||
#if XORG == 15 | |||
master = inputInfo.keyboard; | |||
#else | |||
master = keyboardDev->u.master; | |||
master = vncKeyboardDev->u.master; | |||
#endif | |||
void *slave = dixLookupPrivate(&master->devPrivates, | |||
CoreDevicePrivateKey); | |||
if (keyboardDev == slave) { | |||
if (vncKeyboardDev == slave) { | |||
dixSetPrivate(&master->devPrivates, | |||
CoreDevicePrivateKey, NULL); | |||
#if XORG == 15 | |||
SwitchCoreKeyboard(keyboardDev); | |||
SwitchCoreKeyboard(vncKeyboardDev); | |||
#else | |||
CopyKeyClass(keyboardDev, master); | |||
CopyKeyClass(vncKeyboardDev, master); | |||
#endif | |||
} | |||
@@ -1,6 +1,6 @@ | |||
/* Copyright (C) 2009 TightVNC Team | |||
* Copyright (C) 2009 Red Hat, Inc. | |||
* Copyright 2013 Pierre Ossman for Cendio AB | |||
* 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 | |||
@@ -22,30 +22,34 @@ | |||
#include <dix-config.h> | |||
#endif | |||
#include "Input.h" | |||
#include "xorg-version.h" | |||
#if XORG >= 17 | |||
extern "C" { | |||
#define public c_public | |||
#define class c_class | |||
#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 <X11/keysym.h> | |||
#include <X11/Xlib.h> | |||
#include <X11/Xutil.h> | |||
#undef public | |||
#undef class | |||
} | |||
#include "Input.h" | |||
#ifndef KEYBOARD_OR_FLOAT | |||
#define KEYBOARD_OR_FLOAT MASTER_KEYBOARD | |||
#endif | |||
extern DeviceIntPtr vncKeyboardDev; | |||
static void vncXkbProcessDeviceEvent(int screenNum, | |||
InternalEvent *event, | |||
DeviceIntPtr dev); | |||
/* Stolen from libX11 */ | |||
static Bool | |||
XkbTranslateKeyCode(register XkbDescPtr xkb, KeyCode key, | |||
@@ -195,7 +199,7 @@ static unsigned XkbKeyEffectiveGroup(XkbDescPtr xkb, KeyCode key, unsigned int m | |||
return effectiveGroup; | |||
} | |||
void InputDevice::PrepareInputDevices(void) | |||
void vncPrepareInputDevices(void) | |||
{ | |||
/* | |||
* Not ideal since these callbacks do not stack, but it's the only | |||
@@ -206,15 +210,15 @@ void InputDevice::PrepareInputDevices(void) | |||
mieqSetHandler(ET_KeyRelease, vncXkbProcessDeviceEvent); | |||
} | |||
unsigned InputDevice::getKeyboardState(void) | |||
unsigned vncGetKeyboardState(void) | |||
{ | |||
DeviceIntPtr master; | |||
master = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT); | |||
master = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT); | |||
return XkbStateFieldFromRec(&master->key->xkbInfo->state); | |||
} | |||
unsigned InputDevice::getLevelThreeMask(void) | |||
unsigned vncGetLevelThreeMask(void) | |||
{ | |||
unsigned state; | |||
KeyCode keycode; | |||
@@ -222,17 +226,17 @@ unsigned InputDevice::getLevelThreeMask(void) | |||
XkbAction *act; | |||
/* Group state is still important */ | |||
state = getKeyboardState(); | |||
state = vncGetKeyboardState(); | |||
state &= ~0xff; | |||
keycode = keysymToKeycode(XK_ISO_Level3_Shift, state, NULL); | |||
keycode = vncKeysymToKeycode(XK_ISO_Level3_Shift, state, NULL); | |||
if (keycode == 0) { | |||
keycode = keysymToKeycode(XK_Mode_switch, state, NULL); | |||
keycode = vncKeysymToKeycode(XK_Mode_switch, state, NULL); | |||
if (keycode == 0) | |||
return 0; | |||
} | |||
xkb = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc; | |||
xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc; | |||
act = XkbKeyActionPtr(xkb, keycode, state); | |||
if (act == NULL) | |||
@@ -246,18 +250,18 @@ unsigned InputDevice::getLevelThreeMask(void) | |||
return act->mods.mask; | |||
} | |||
KeyCode InputDevice::pressShift(void) | |||
KeyCode vncPressShift(void) | |||
{ | |||
unsigned state; | |||
XkbDescPtr xkb; | |||
unsigned int key; | |||
state = getKeyboardState(); | |||
state = vncGetKeyboardState(); | |||
if (state & ShiftMask) | |||
return 0; | |||
xkb = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc; | |||
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; | |||
@@ -281,20 +285,23 @@ KeyCode InputDevice::pressShift(void) | |||
return 0; | |||
} | |||
std::list<KeyCode> InputDevice::releaseShift(void) | |||
size_t vncReleaseShift(KeyCode *keys, size_t maxKeys) | |||
{ | |||
size_t count; | |||
unsigned state; | |||
std::list<KeyCode> keys; | |||
DeviceIntPtr master; | |||
XkbDescPtr xkb; | |||
unsigned int key; | |||
state = getKeyboardState(); | |||
state = vncGetKeyboardState(); | |||
if (!(state & ShiftMask)) | |||
return keys; | |||
return 0; | |||
master = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT); | |||
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; | |||
@@ -318,13 +325,16 @@ std::list<KeyCode> InputDevice::releaseShift(void) | |||
if (!(mask & ShiftMask)) | |||
continue; | |||
keys.push_back(key); | |||
if (count >= maxKeys) | |||
return 0; | |||
keys[count++] = key; | |||
} | |||
return keys; | |||
return count; | |||
} | |||
KeyCode InputDevice::pressLevelThree(void) | |||
KeyCode vncPressLevelThree(void) | |||
{ | |||
unsigned state, mask; | |||
@@ -332,22 +342,22 @@ KeyCode InputDevice::pressLevelThree(void) | |||
XkbDescPtr xkb; | |||
XkbAction *act; | |||
mask = getLevelThreeMask(); | |||
mask = vncGetLevelThreeMask(); | |||
if (mask == 0) | |||
return 0; | |||
state = getKeyboardState(); | |||
state = vncGetKeyboardState(); | |||
if (state & mask) | |||
return 0; | |||
keycode = keysymToKeycode(XK_ISO_Level3_Shift, state, NULL); | |||
keycode = vncKeysymToKeycode(XK_ISO_Level3_Shift, state, NULL); | |||
if (keycode == 0) { | |||
keycode = keysymToKeycode(XK_Mode_switch, state, NULL); | |||
keycode = vncKeysymToKeycode(XK_Mode_switch, state, NULL); | |||
if (keycode == 0) | |||
return 0; | |||
} | |||
xkb = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc; | |||
xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc; | |||
act = XkbKeyActionPtr(xkb, keycode, state); | |||
if (act == NULL) | |||
@@ -358,24 +368,27 @@ KeyCode InputDevice::pressLevelThree(void) | |||
return keycode; | |||
} | |||
std::list<KeyCode> InputDevice::releaseLevelThree(void) | |||
size_t vncReleaseLevelThree(KeyCode *keys, size_t maxKeys) | |||
{ | |||
size_t count; | |||
unsigned state, mask; | |||
std::list<KeyCode> keys; | |||
DeviceIntPtr master; | |||
XkbDescPtr xkb; | |||
unsigned int key; | |||
mask = getLevelThreeMask(); | |||
mask = vncGetLevelThreeMask(); | |||
if (mask == 0) | |||
return keys; | |||
return 0; | |||
state = getKeyboardState(); | |||
state = vncGetKeyboardState(); | |||
if (!(state & mask)) | |||
return keys; | |||
return 0; | |||
master = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT); | |||
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; | |||
@@ -399,14 +412,16 @@ std::list<KeyCode> InputDevice::releaseLevelThree(void) | |||
if (!(key_mask & mask)) | |||
continue; | |||
keys.push_back(key); | |||
if (count >= maxKeys) | |||
return 0; | |||
keys[count++] = key; | |||
} | |||
return keys; | |||
return count; | |||
} | |||
KeyCode InputDevice::keysymToKeycode(KeySym keysym, unsigned state, | |||
unsigned *new_state) | |||
KeyCode vncKeysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state) | |||
{ | |||
XkbDescPtr xkb; | |||
unsigned int key; | |||
@@ -416,7 +431,7 @@ KeyCode InputDevice::keysymToKeycode(KeySym keysym, unsigned state, | |||
if (new_state != NULL) | |||
*new_state = state; | |||
xkb = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc; | |||
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; | |||
@@ -444,48 +459,48 @@ KeyCode InputDevice::keysymToKeycode(KeySym keysym, unsigned state, | |||
*new_state = (state & ~ShiftMask) | | |||
((state & ShiftMask) ? 0 : ShiftMask); | |||
key = keysymToKeycode(keysym, *new_state, NULL); | |||
key = vncKeysymToKeycode(keysym, *new_state, NULL); | |||
if (key != 0) | |||
return key; | |||
level_three_mask = getLevelThreeMask(); | |||
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 = keysymToKeycode(keysym, *new_state, NULL); | |||
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 = keysymToKeycode(keysym, *new_state, NULL); | |||
key = vncKeysymToKeycode(keysym, *new_state, NULL); | |||
if (key != 0) | |||
return key; | |||
return 0; | |||
} | |||
bool InputDevice::isLockModifier(KeyCode keycode, unsigned state) | |||
int vncIsLockModifier(KeyCode keycode, unsigned state) | |||
{ | |||
XkbDescPtr xkb; | |||
XkbAction *act; | |||
xkb = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc; | |||
xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc; | |||
act = XkbKeyActionPtr(xkb, keycode, state); | |||
if (act == NULL) | |||
return false; | |||
return 0; | |||
if (act->type != XkbSA_LockMods) | |||
return false; | |||
return 0; | |||
return true; | |||
return 1; | |||
} | |||
bool InputDevice::isAffectedByNumLock(KeyCode keycode) | |||
int vncIsAffectedByNumLock(KeyCode keycode) | |||
{ | |||
unsigned state; | |||
@@ -499,7 +514,7 @@ bool InputDevice::isAffectedByNumLock(KeyCode keycode) | |||
XkbKeyTypeRec *type; | |||
/* Group state is still important */ | |||
state = getKeyboardState(); | |||
state = vncGetKeyboardState(); | |||
state &= ~0xff; | |||
/* | |||
@@ -507,17 +522,17 @@ bool InputDevice::isAffectedByNumLock(KeyCode keycode) | |||
* or following the keysym Num_Lock is the best approach. We | |||
* try the latter. | |||
*/ | |||
numlock_keycode = keysymToKeycode(XK_Num_Lock, state, NULL); | |||
numlock_keycode = vncKeysymToKeycode(XK_Num_Lock, state, NULL); | |||
if (numlock_keycode == 0) | |||
return false; | |||
return 0; | |||
xkb = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc; | |||
xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc; | |||
act = XkbKeyActionPtr(xkb, numlock_keycode, state); | |||
if (act == NULL) | |||
return false; | |||
return 0; | |||
if (act->type != XkbSA_LockMods) | |||
return false; | |||
return 0; | |||
if (act->mods.flags & XkbSA_UseModMapMods) | |||
numlock_mask = xkb->map->modmap[keycode]; | |||
@@ -527,12 +542,12 @@ bool InputDevice::isAffectedByNumLock(KeyCode keycode) | |||
group = XkbKeyEffectiveGroup(xkb, keycode, state); | |||
type = XkbKeyKeyType(xkb, keycode, group); | |||
if ((type->mods.mask & numlock_mask) == 0) | |||
return false; | |||
return 0; | |||
return true; | |||
return 1; | |||
} | |||
KeyCode InputDevice::addKeysym(KeySym keysym, unsigned state) | |||
KeyCode vncAddKeysym(KeySym keysym, unsigned state) | |||
{ | |||
DeviceIntPtr master; | |||
XkbDescPtr xkb; | |||
@@ -545,7 +560,7 @@ KeyCode InputDevice::addKeysym(KeySym keysym, unsigned state) | |||
KeySym *syms; | |||
KeySym upper, lower; | |||
master = GetMaster(keyboardDev, KEYBOARD_OR_FLOAT); | |||
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) | |||
@@ -608,13 +623,13 @@ KeyCode InputDevice::addKeysym(KeySym keysym, unsigned state) | |||
return key; | |||
} | |||
void InputDevice::vncXkbProcessDeviceEvent(int screenNum, | |||
InternalEvent *event, | |||
DeviceIntPtr dev) | |||
static void vncXkbProcessDeviceEvent(int screenNum, | |||
InternalEvent *event, | |||
DeviceIntPtr dev) | |||
{ | |||
unsigned int backupctrls; | |||
if (event->device_event.sourceid == singleton.keyboardDev->id) { | |||
if (event->device_event.sourceid == vncKeyboardDev->id) { | |||
XkbControlsPtr ctrls; | |||
/* | |||
@@ -634,9 +649,9 @@ void InputDevice::vncXkbProcessDeviceEvent(int screenNum, | |||
event->device_event.key_repeat = TRUE; | |||
} | |||
dev->c_public.processInputProc(event, dev); | |||
dev->public.processInputProc(event, dev); | |||
if (event->device_event.sourceid == singleton.keyboardDev->id) { | |||
if (event->device_event.sourceid == vncKeyboardDev->id) { | |||
XkbControlsPtr ctrls; | |||
ctrls = dev->key->xkbInfo->desc->ctrls; |
@@ -9,11 +9,14 @@ COMMON_LIBS=$(NETWORK_LIB) $(RFB_LIB) $(RDR_LIB) $(XREGION_LIB) | |||
noinst_LTLIBRARIES = libvnccommon.la | |||
HDRS = RegionHelper.h vncExtInit.h vncHooks.h XserverDesktop.h xorg-version.h \ | |||
Input.h | |||
HDRS = vncExtInit.h vncHooks.h \ | |||
vncBlockHandler.h XorgGlue.h \ | |||
XserverDesktop.h xorg-version.h \ | |||
Input.h RFBGlue.h | |||
libvnccommon_la_SOURCES = $(HDRS) vncExtInit.cc vncHooks.cc XserverDesktop.cc \ | |||
Input.cc InputCore.cc InputXKB.cc | |||
libvnccommon_la_SOURCES = $(HDRS) vncExt.c vncExtInit.cc vncHooks.c \ | |||
vncBlockHandler.c XorgGlue.c RFBGlue.cc XserverDesktop.cc \ | |||
Input.c InputCore.c InputXKB.c | |||
libvnccommon_la_CPPFLAGS = -DVENDOR_RELEASE="$(VENDOR_RELEASE)" \ | |||
-DVENDOR_STRING="\"$(VENDOR_STRING)\"" -I$(TIGERVNC_SRCDIR)/common -UHAVE_CONFIG_H \ | |||
@@ -24,12 +27,10 @@ bin_PROGRAMS = Xvnc | |||
man1_MANS = Xvnc.man | |||
Xvnc_SOURCES = xvnc.cc \ | |||
Xvnc_SOURCES = xvnc.c \ | |||
$(top_srcdir)/Xi/stubs.c $(top_srcdir)/mi/miinitext.c \ | |||
$(top_srcdir)/fb/fbcmap_mi.c buildtime.c | |||
nodist_Xvnc_SOURCES = fbrop.h fb.h pixman.h | |||
Xvnc_CPPFLAGS = $(XVNC_CPPFLAGS) -DTIGERVNC -DNO_MODULE_EXTS \ | |||
-UHAVE_CONFIG_H \ | |||
-DXFree86Server -DVENDOR_RELEASE="$(VENDOR_RELEASE)" \ | |||
@@ -44,7 +45,7 @@ Xvnc_LDFLAGS = $(LD_EXPORT_SYMBOLS_FLAG) | |||
libvnc_la_LTLIBRARIES = libvnc.la | |||
libvnc_ladir = $(moduledir)/extensions | |||
libvnc_la_SOURCES = xf86vncModule.cc | |||
libvnc_la_SOURCES = vncModule.c | |||
libvnc_la_CPPFLAGS = $(XVNC_CPPFLAGS) -I$(TIGERVNC_SRCDIR)/common -UHAVE_CONFIG_H \ | |||
-I$(top_srcdir)/hw/xfree86/common \ | |||
@@ -58,24 +59,3 @@ libvnc_la_LDFLAGS = -module -avoid-version | |||
libvnc_la_LIBADD = libvnccommon.la $(COMMON_LIBS) | |||
EXTRA_DIST = Xvnc.man | |||
# C++ hacks | |||
BUILT_SOURCES = $(nodist_Xvnc_SOURCES) | |||
fb.h: $(top_srcdir)/fb/fb.h | |||
cat $(top_srcdir)/fb/fb.h | sed -e 's,and,c_and,g' -e 's,xor,c_xor,g' > $(srcdir)/fb.h | |||
pixman.h: | |||
for i in ${XSERVERLIBS_CFLAGS}; do \ | |||
if [[ "$$i" =~ "pixman" ]]; then \ | |||
PIXMANINCDIR=`echo $$i | sed s/-I//g`; \ | |||
fi; \ | |||
done; \ | |||
if [ ! "$$PIXMANINCDIR" = "" ]; then \ | |||
cat $$PIXMANINCDIR/pixman.h | sed 's/xor/c_xor/' > $(srcdir)/pixman.h; \ | |||
else \ | |||
echo Pixman include directory not set in XSERVERLIBS_CFLAGS \(perhaps Pixman was not found by configure?\); \ | |||
fi | |||
fbrop.h: $(top_srcdir)/fb/fbrop.h | |||
cat $(top_srcdir)/fb/fbrop.h | sed -e 's,and,c_and,g' -e 's,xor,c_xor,g' > $(srcdir)/fbrop.h |
@@ -0,0 +1,195 @@ | |||
/* 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 "RFBGlue.h" | |||
using namespace rfb; | |||
// Loggers used by C code must be created here | |||
static LogWriter inputLog("Input"); | |||
void vncInitRFB(void) | |||
{ | |||
rfb::initStdIOLoggers(); | |||
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->error(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->status(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->info(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->debug(format, ap); | |||
va_end(ap); | |||
} | |||
int vncSetParam(const char *name, const char *value) | |||
{ | |||
return rfb::Configuration::setParam(name, value); | |||
} | |||
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::TcpSocket::getSockPort(fd); | |||
} | |||
int vncIsTCPPortUsed(int port) | |||
{ | |||
try { | |||
network::TcpListener l(NULL, port); | |||
} catch (rdr::Exception& e) { | |||
return 0; | |||
} | |||
return 1; | |||
} |
@@ -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 |
@@ -1,84 +0,0 @@ | |||
/* 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. | |||
*/ | |||
#ifndef __REGIONHELPER_H__ | |||
#define __REGIONHELPER_H__ | |||
// RegionHelper is a class which helps in using X server regions by | |||
// automatically freeing them in the destructor. It also fixes a problem with | |||
// REGION_INIT when given an empty rectangle. | |||
// REGION_NULL was introduced in the Xorg tree as the way to initialise an | |||
// empty region. If it's not already defined do it the old way. Note that the | |||
// old way causes a segfault in the new tree... | |||
#ifndef REGION_NULL | |||
#define REGION_NULL(pScreen,pReg) REGION_INIT(pScreen,pReg,NullBox,0) | |||
#endif | |||
class RegionHelper { | |||
public: | |||
// constructor from a single rect | |||
RegionHelper(ScreenPtr pScreen_, BoxPtr rect, int size) | |||
: pScreen(pScreen_), reg(0) | |||
{ | |||
init(rect, size); | |||
} | |||
// constructor from an existing X server region | |||
RegionHelper(ScreenPtr pScreen_, RegionPtr pRegion) | |||
: pScreen(pScreen_), reg(®Rec) | |||
{ | |||
REGION_NULL(pScreen, reg); | |||
REGION_COPY(pScreen, reg, pRegion); | |||
} | |||
// constructor from an array of rectangles | |||
RegionHelper(ScreenPtr pScreen_, int nrects, xRectanglePtr rects, | |||
int ctype=CT_NONE) | |||
: pScreen(pScreen_) | |||
{ | |||
reg = RECTS_TO_REGION(pScreen, nrects, rects, ctype); | |||
} | |||
// constructor for calling init() later | |||
RegionHelper(ScreenPtr pScreen_) : pScreen(pScreen_), reg(0) { | |||
} | |||
void init(BoxPtr rect, int size) { | |||
reg = ®Rec; | |||
if (!rect || (rect && (rect->x2 == rect->x1 || rect->y2 == rect->y1))) { | |||
REGION_NULL(pScreen, reg); | |||
} else { | |||
REGION_INIT(pScreen, reg, rect, size); | |||
} | |||
} | |||
// destructor frees as appropriate | |||
~RegionHelper() { | |||
if (reg == ®Rec) { | |||
REGION_UNINIT(pScreen, reg); | |||
} else if (reg) { | |||
REGION_DESTROY(pScreen, reg); | |||
} | |||
} | |||
ScreenPtr pScreen; | |||
RegionRec regRec; | |||
RegionPtr reg; | |||
}; | |||
#endif |
@@ -0,0 +1,362 @@ | |||
/* 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" | |||
#ifdef RANDR | |||
extern RRModePtr vncRandRModeGet(int width, int height); | |||
#endif | |||
const char *vncGetDisplay(void) | |||
{ | |||
return display; | |||
} | |||
unsigned long vncGetServerGeneration(void) | |||
{ | |||
return serverGeneration; | |||
} | |||
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 vncGetScreenWidth(int scrIdx) | |||
{ | |||
return screenInfo.screens[scrIdx]->width; | |||
} | |||
int vncGetScreenHeight(int scrIdx) | |||
{ | |||
return screenInfo.screens[scrIdx]->height; | |||
} | |||
int vncRandRResizeScreen(int scrIdx, int width, int height) | |||
{ | |||
#ifdef RANDR | |||
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); | |||
#else | |||
return -1; | |||
#endif | |||
} | |||
void vncRandRUpdateSetTime(int scrIdx) | |||
{ | |||
#ifdef RANDR | |||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]); | |||
rp->lastSetTime = currentTime; | |||
#endif | |||
} | |||
int vncRandRHasOutputClones(int scrIdx) | |||
{ | |||
#ifdef RANDR | |||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]); | |||
for (int i = 0;i < rp->numCrtcs;i++) { | |||
if (rp->crtcs[i]->numOutputs > 1) | |||
return 1; | |||
} | |||
#endif | |||
return 0; | |||
} | |||
int vncRandRGetOutputCount(int scrIdx) | |||
{ | |||
#ifdef RANDR | |||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]); | |||
return rp->numOutputs; | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int vncRandRGetAvailableOutputs(int scrIdx) | |||
{ | |||
#ifdef RANDR | |||
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; | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
const char *vncRandRGetOutputName(int scrIdx, int outputIdx) | |||
{ | |||
#ifdef RANDR | |||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]); | |||
return rp->outputs[outputIdx]->name; | |||
#else | |||
return ""; | |||
#endif | |||
} | |||
int vncRandRIsOutputEnabled(int scrIdx, int outputIdx) | |||
{ | |||
#ifdef RANDR | |||
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; | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int vncRandRIsOutputUsable(int scrIdx, int outputIdx) | |||
{ | |||
#ifdef RANDR | |||
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; | |||
} | |||
#endif | |||
return 0; | |||
} | |||
int vncRandRDisableOutput(int scrIdx, int outputIdx) | |||
{ | |||
#ifdef RANDR | |||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]); | |||
RRCrtcPtr crtc; | |||
crtc = rp->outputs[outputIdx]->crtc; | |||
if (crtc == NULL) | |||
return 0; | |||
return RRCrtcSet(crtc, NULL, crtc->x, crtc->y, crtc->rotation, 0, NULL); | |||
#else | |||
return -1; | |||
#endif | |||
} | |||
intptr_t vncRandRGetOutputId(int scrIdx, int outputIdx) | |||
{ | |||
#ifdef RANDR | |||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]); | |||
return (intptr_t)rp->outputs[outputIdx]; | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
void vncRandRGetOutputDimensions(int scrIdx, int outputIdx, | |||
int *x, int *y, int *width, int *height) | |||
{ | |||
#ifdef RANDR | |||
rrScrPrivPtr rp = rrGetScrPriv(screenInfo.screens[scrIdx]); | |||
*x = rp->outputs[outputIdx]->crtc->x; | |||
*y = rp->outputs[outputIdx]->crtc->y; | |||
*width = rp->outputs[outputIdx]->crtc->mode->mode.width; | |||
*height = rp->outputs[outputIdx]->crtc->mode->mode.height; | |||
#endif | |||
} | |||
#ifdef RANDR | |||
static RRModePtr findRandRMode(RROutputPtr output, int width, int height) | |||
{ | |||
RRModePtr mode; | |||
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]; | |||
} | |||
mode = vncRandRModeGet(width, height); | |||
if (mode != NULL) | |||
return mode; | |||
return NULL; | |||
} | |||
#endif | |||
int vncRandRReconfigureOutput(int scrIdx, int outputIdx, int x, int y, | |||
int width, int height) | |||
{ | |||
#ifdef RANDR | |||
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 -1; | |||
} | |||
mode = crtc->mode; | |||
/* Need to switch mode? */ | |||
if ((mode == NULL) || | |||
(mode->mode.width != width) || (mode->mode.height != height)) { | |||
mode = findRandRMode(output, width, height); | |||
if (mode == NULL) | |||
return -1; | |||
} | |||
/* Reconfigure new mode and position */ | |||
return RRCrtcSet(crtc, mode, x, y, crtc->rotation, 1, &output); | |||
#else | |||
return -1; | |||
#endif | |||
} |
@@ -0,0 +1,67 @@ | |||
/* 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 | |||
const char *vncGetDisplay(void); | |||
unsigned long vncGetServerGeneration(void); | |||
int vncGetScreenCount(void); | |||
void vncGetScreenFormat(int scrIdx, int *depth, int *bpp, | |||
int *trueColour, int *bigEndian, | |||
int *redMask, int *greenMask, int *blueMask); | |||
int vncGetScreenWidth(int scrIdx); | |||
int vncGetScreenHeight(int scrIdx); | |||
int vncRandRResizeScreen(int scrIdx, int width, int height); | |||
void vncRandRUpdateSetTime(int scrIdx); | |||
int vncRandRHasOutputClones(int scrIdx); | |||
int vncRandRGetOutputCount(int scrIdx); | |||
int vncRandRGetAvailableOutputs(int scrIdx); | |||
const char *vncRandRGetOutputName(int scrIdx, int outputIdx); | |||
int vncRandRIsOutputEnabled(int scrIdx, int outputIdx); | |||
int vncRandRIsOutputUsable(int scrIdx, int outputIdx); | |||
int vncRandRDisableOutput(int scrIdx, int outputIdx); | |||
int vncRandRReconfigureOutput(int scrIdx, int outputIdx, int x, int y, | |||
int width, int height); | |||
intptr_t vncRandRGetOutputId(int scrIdx, int outputIdx); | |||
void vncRandRGetOutputDimensions(int scrIdx, int outputIdx, | |||
int *x, int *y, int *width, int *height); | |||
// This one hides in xvnc.c or vncModule.c | |||
int vncRandRCreateOutputs(int scrIdx, int extraOutputs); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif |
@@ -1,5 +1,5 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* Copyright 2009-2011 Pierre Ossman for Cendio AB | |||
* Copyright 2009-2015 Pierre Ossman for Cendio AB | |||
* Copyright 2014 Brian P. Hinz | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
@@ -21,12 +21,9 @@ | |||
// XserverDesktop.cxx | |||
// | |||
#ifdef HAVE_DIX_CONFIG_H | |||
#include <dix-config.h> | |||
#endif | |||
#include <assert.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <strings.h> | |||
#include <unistd.h> | |||
#include <pwd.h> | |||
@@ -34,65 +31,29 @@ | |||
#include <sys/stat.h> | |||
#include <fcntl.h> | |||
#include <sys/utsname.h> | |||
#include <network/TcpSocket.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 "vncExtInit.h" | |||
#include "xorg-version.h" | |||
#include "vncHooks.h" | |||
#include "XorgGlue.h" | |||
#include "Input.h" | |||
extern "C" { | |||
#define public c_public | |||
#define class c_class | |||
#ifdef RANDR | |||
#include "randrstr.h" | |||
#endif | |||
#include "cursorstr.h" | |||
#undef public | |||
#undef class | |||
} | |||
// Hack to catch when inetd has let us go | |||
extern "C" void vncClientGone(int fd); | |||
using namespace rfb; | |||
using namespace network; | |||
static LogWriter vlog("XserverDesktop"); | |||
IntParameter queryConnectTimeout("QueryConnectTimeout", | |||
"Number of seconds to show the Accept Connection dialog before " | |||
"rejecting the connection", | |||
10); | |||
static rdr::U8 reverseBits[] = { | |||
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, | |||
0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, | |||
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, | |||
0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, | |||
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, | |||
0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, | |||
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, | |||
0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, | |||
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, | |||
0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, | |||
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, | |||
0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, | |||
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, | |||
0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, | |||
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, | |||
0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, | |||
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, | |||
0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, | |||
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, | |||
0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, | |||
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, | |||
0x3f, 0xbf, 0x7f, 0xff | |||
}; | |||
class FileHTTPServer : public rfb::HTTPServer { | |||
public: | |||
FileHTTPServer(XserverDesktop* d) : desktop(d) {} | |||
@@ -132,22 +93,22 @@ public: | |||
}; | |||
XserverDesktop::XserverDesktop(ScreenPtr pScreen_, | |||
XserverDesktop::XserverDesktop(int screenIndex_, | |||
network::TcpListener* listener_, | |||
network::TcpListener* httpListener_, | |||
const char* name, const rfb::PixelFormat &pf, | |||
int width, int height, | |||
void* fbptr, int stride) | |||
: pScreen(pScreen_), | |||
: screenIndex(screenIndex_), | |||
server(0), httpServer(0), | |||
listener(listener_), httpListener(httpListener_), | |||
deferredUpdateTimerSet(false), | |||
grabbing(false), ignoreHooks_(false), directFbptr(true), | |||
deferredUpdateTimerSet(false), directFbptr(true), | |||
queryConnectId(0) | |||
{ | |||
format = pf; | |||
server = new VNCServerST(name, this); | |||
setFramebuffer(pScreen->width, pScreen->height, fbptr, stride); | |||
setFramebuffer(width, height, fbptr, stride); | |||
server->setQueryConnectionHandler(this); | |||
if (httpListener) | |||
@@ -206,25 +167,21 @@ void XserverDesktop::refreshScreenLayout() | |||
ScreenSet XserverDesktop::computeScreenLayout() | |||
{ | |||
ScreenSet layout; | |||
#ifdef RANDR | |||
rrScrPrivPtr rp = rrGetScrPriv(pScreen); | |||
OutputIdMap newIdMap; | |||
for (int i = 0;i < rp->numOutputs;i++) { | |||
RROutputPtr output; | |||
RRCrtcPtr crtc; | |||
output = rp->outputs[i]; | |||
crtc = output->crtc; | |||
for (int i = 0;i < vncRandRGetOutputCount(screenIndex);i++) { | |||
intptr_t outputId; | |||
int x, y, width, height; | |||
/* Disabled? */ | |||
if ((crtc == NULL) || (crtc->mode == NULL)) | |||
if (!vncRandRIsOutputEnabled(screenIndex, i)) | |||
continue; | |||
outputId = vncRandRGetOutputId(screenIndex, i); | |||
/* Known output? */ | |||
if (outputIdMap.count(output) == 1) | |||
newIdMap[output] = outputIdMap[output]; | |||
if (outputIdMap.count(outputId) == 1) | |||
newIdMap[outputId] = outputIdMap[outputId]; | |||
else { | |||
rdr::U32 id; | |||
OutputIdMap::const_iterator iter; | |||
@@ -239,52 +196,28 @@ ScreenSet XserverDesktop::computeScreenLayout() | |||
break; | |||
} | |||
newIdMap[output] = id; | |||
newIdMap[outputId] = id; | |||
} | |||
layout.add_screen(Screen(newIdMap[output], crtc->x, crtc->y, | |||
crtc->mode->mode.width, | |||
crtc->mode->mode.height, | |||
0)); | |||
vncRandRGetOutputDimensions(screenIndex, i, &x, &y, &width, &height); | |||
layout.add_screen(Screen(newIdMap[outputId], x, y, width, height, 0)); | |||
} | |||
/* Only keep the entries that are currently active */ | |||
outputIdMap = newIdMap; | |||
#endif | |||
/* | |||
* Make sure we have something to display. Hopefully it's just temporary | |||
* that we have no active outputs... | |||
*/ | |||
if (layout.num_screens() == 0) | |||
layout.add_screen(Screen(0, 0, 0, pScreen->width, pScreen->height, 0)); | |||
layout.add_screen(Screen(0, 0, 0, vncGetScreenWidth(screenIndex), | |||
vncGetScreenHeight(screenIndex), 0)); | |||
return layout; | |||
} | |||
#ifdef RANDR | |||
extern RRModePtr vncRandRModeGet(int width, int height); | |||
RRModePtr XserverDesktop::findRandRMode(RROutputPtr output, int width, int height) | |||
{ | |||
RRModePtr mode; | |||
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]; | |||
} | |||
mode = vncRandRModeGet(width, height); | |||
if (mode != NULL) | |||
return mode; | |||
return NULL; | |||
} | |||
#endif | |||
char* XserverDesktop::substitute(const char* varName) | |||
{ | |||
if (strcmp(varName, "$$") == 0) { | |||
@@ -325,7 +258,7 @@ char* XserverDesktop::substitute(const char* varName) | |||
strncpy(str, uts.nodename, 240); | |||
str[239] = '\0'; /* Ensure string is zero-terminated */ | |||
strcat(str, ":"); | |||
strncat(str, display, 10); | |||
strncat(str, vncGetDisplay(), 10); | |||
return str; | |||
} | |||
if (strcmp(varName, "$USER") == 0) { | |||
@@ -338,17 +271,28 @@ char* XserverDesktop::substitute(const char* varName) | |||
rfb::VNCServerST::queryResult | |||
XserverDesktop::queryConnection(network::Socket* sock, | |||
const char* userName, | |||
char** reason) { | |||
char** reason) | |||
{ | |||
int count; | |||
if (queryConnectId) { | |||
*reason = strDup("Another connection is currently being queried."); | |||
return rfb::VNCServerST::REJECT; | |||
} | |||
queryConnectAddress.replaceBuf(sock->getPeerAddress()); | |||
if (!userName) | |||
userName = "(anonymous)"; | |||
queryConnectUsername.replaceBuf(strDup(userName)); | |||
queryConnectId = sock; | |||
vncQueryConnect(this, sock); | |||
queryConnectId = (uint32_t)(intptr_t)sock; | |||
queryConnectSocket = sock; | |||
count = vncNotifyQueryConnect(); | |||
if (count == 0) { | |||
*reason = strDup("Unable to query the local user to accept the connection."); | |||
return rfb::VNCServerST::REJECT; | |||
} | |||
return rfb::VNCServerST::PENDING; | |||
} | |||
@@ -375,148 +319,77 @@ void XserverDesktop::setDesktopName(const char* name) | |||
} | |||
} | |||
void XserverDesktop::setCursor(CursorPtr cursor) | |||
void XserverDesktop::setCursor(int width, int height, int hotX, int hotY, | |||
const unsigned char *rgbaData) | |||
{ | |||
try { | |||
int w = cursor->bits->width; | |||
int h = cursor->bits->height; | |||
rdr::U8* cursorData = new rdr::U8[w * h * (getPF().bpp / 8)]; | |||
int rfbMaskBytesPerRow = (w + 7) / 8; | |||
rdr::U8* cursorData; | |||
rdr::U8* cursorMask; | |||
int rfbMaskBytesPerRow; | |||
rdr::U8* cursorMask = new rdr::U8[rfbMaskBytesPerRow * h]; | |||
rdr::U8 *out; | |||
const unsigned char *in; | |||
rdr::U8 rgb[3]; | |||
#ifdef ARGB_CURSOR | |||
if (cursor->bits->argb) { | |||
rdr::U8 *out; | |||
CARD32 *in; | |||
cursorData = new rdr::U8[width * height * (getPF().bpp / 8)]; | |||
rdr::U8 rgb[3]; | |||
rfbMaskBytesPerRow = (width + 7) / 8; | |||
memset(cursorMask, 0, rfbMaskBytesPerRow * h); | |||
cursorMask = new rdr::U8[rfbMaskBytesPerRow * height]; | |||
in = cursor->bits->argb; | |||
out = cursorData; | |||
for (int y = 0; y < h; y++) { | |||
for (int x = 0; x < w; x++) { | |||
rgb[0] = (*in >> 16) & 0xff; | |||
rgb[1] = (*in >> 8) & 0xff; | |||
rgb[2] = (*in >> 0) & 0xff; | |||
memset(cursorMask, 0, rfbMaskBytesPerRow * height); | |||
getPF().bufferFromRGB(out, rgb, 1); | |||
in = rgbaData; | |||
out = cursorData; | |||
for (int y = 0; y < height; y++) { | |||
for (int x = 0; x < width; x++) { | |||
rgb[0] = *in++; | |||
rgb[1] = *in++; | |||
rgb[2] = *in++; | |||
if (((*in >> 24) & 0xff) > 127) | |||
cursorMask[y * rfbMaskBytesPerRow + x/8] |= 0x80>>(x%8); | |||
getPF().bufferFromRGB(out, rgb, 1); | |||
in++; | |||
out += getPF().bpp/8; | |||
} | |||
} | |||
} else { | |||
#endif | |||
rdr::U8 rgb[3]; | |||
rdr::U8 fg[4], bg[4]; | |||
rdr::U8* buffer; | |||
rgb[0] = cursor->foreRed; | |||
rgb[1] = cursor->foreGreen; | |||
rgb[2] = cursor->foreBlue; | |||
getPF().bufferFromRGB(fg, rgb, 1); | |||
rgb[0] = cursor->backRed; | |||
rgb[1] = cursor->backGreen; | |||
rgb[2] = cursor->backBlue; | |||
getPF().bufferFromRGB(bg, rgb, 1); | |||
int xMaskBytesPerRow = BitmapBytePad(w); | |||
buffer = cursorData; | |||
for (int y = 0; y < h; y++) { | |||
for (int x = 0; x < w; x++) { | |||
rdr::U8 *pixel; | |||
int byte = y * xMaskBytesPerRow + x / 8; | |||
#if (BITMAP_BIT_ORDER == MSBFirst) | |||
int bit = 7 - x % 8; | |||
#else | |||
int bit = x % 8; | |||
#endif | |||
if (cursor->bits->source[byte] & (1 << bit)) | |||
pixel = fg; | |||
else | |||
pixel = bg; | |||
memcpy(buffer, pixel, getPF().bpp/8); | |||
buffer += getPF().bpp/8; | |||
} | |||
} | |||
if (*in++ > 127) | |||
cursorMask[y * rfbMaskBytesPerRow + x/8] |= 0x80>>(x%8); | |||
for (int j = 0; j < h; j++) { | |||
for (int i = 0; i < rfbMaskBytesPerRow; i++) | |||
#if (BITMAP_BIT_ORDER == MSBFirst) | |||
cursorMask[j * rfbMaskBytesPerRow + i] | |||
= cursor->bits->mask[j * xMaskBytesPerRow + i]; | |||
#else | |||
cursorMask[j * rfbMaskBytesPerRow + i] | |||
= reverseBits[cursor->bits->mask[j * xMaskBytesPerRow + i]]; | |||
#endif | |||
} | |||
#ifdef ARGB_CURSOR | |||
out += getPF().bpp/8; | |||
} | |||
#endif | |||
} | |||
server->setCursor(cursor->bits->width, cursor->bits->height, | |||
Point(cursor->bits->xhot, cursor->bits->yhot), | |||
cursorData, cursorMask); | |||
delete [] cursorData; | |||
delete [] cursorMask; | |||
try { | |||
server->setCursor(width, height, Point(hotX, hotY), cursorData, cursorMask); | |||
} catch (rdr::Exception& e) { | |||
vlog.error("XserverDesktop::setCursor: %s",e.str()); | |||
} | |||
delete [] cursorData; | |||
delete [] cursorMask; | |||
} | |||
void XserverDesktop::add_changed(RegionPtr reg) | |||
void XserverDesktop::add_changed(const rfb::Region ®ion) | |||
{ | |||
if (ignoreHooks_) return; | |||
if (grabbing) return; | |||
try { | |||
rfb::Region rfbReg; | |||
rfbReg.setExtentsAndOrderedRects((ShortRect*)REGION_EXTENTS(pScreen, reg), | |||
REGION_NUM_RECTS(reg), | |||
(ShortRect*)REGION_RECTS(reg)); | |||
server->add_changed(rfbReg); | |||
server->add_changed(region); | |||
} catch (rdr::Exception& e) { | |||
vlog.error("XserverDesktop::add_changed: %s",e.str()); | |||
} | |||
} | |||
void XserverDesktop::add_copied(RegionPtr dst, int dx, int dy) | |||
void XserverDesktop::add_copied(const rfb::Region &dest, const rfb::Point &delta) | |||
{ | |||
if (ignoreHooks_) return; | |||
if (grabbing) return; | |||
try { | |||
rfb::Region rfbReg; | |||
rfbReg.setExtentsAndOrderedRects((ShortRect*)REGION_EXTENTS(pScreen, dst), | |||
REGION_NUM_RECTS(dst), | |||
(ShortRect*)REGION_RECTS(dst)); | |||
server->add_copied(rfbReg, rfb::Point(dx, dy)); | |||
server->add_copied(dest, delta); | |||
} catch (rdr::Exception& e) { | |||
vlog.error("XserverDesktop::add_copied: %s",e.str()); | |||
} | |||
} | |||
static struct timeval XserverDesktopTimeout; | |||
void XserverDesktop::blockHandler(fd_set* fds, OSTimePtr timeout) | |||
void XserverDesktop::readBlockHandler(fd_set* fds, struct timeval ** 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. | |||
vncInputDevice->InitInputDevice(); | |||
vncInitInputDevice(); | |||
try { | |||
int nextTimeout; | |||
@@ -565,9 +438,9 @@ void XserverDesktop::blockHandler(fd_set* fds, OSTimePtr timeout) | |||
((*timeout)->tv_sec > (nextTimeout/1000)) || | |||
(((*timeout)->tv_sec == (nextTimeout/1000)) && | |||
((*timeout)->tv_usec > ((nextTimeout%1000)*1000)))) { | |||
XserverDesktopTimeout.tv_sec = nextTimeout/1000; | |||
XserverDesktopTimeout.tv_usec = (nextTimeout%1000)*1000; | |||
*timeout = &XserverDesktopTimeout; | |||
dixTimeout.tv_sec = nextTimeout/1000; | |||
dixTimeout.tv_usec = (nextTimeout%1000)*1000; | |||
*timeout = &dixTimeout; | |||
} | |||
} | |||
@@ -576,7 +449,7 @@ void XserverDesktop::blockHandler(fd_set* fds, OSTimePtr timeout) | |||
} | |||
} | |||
void XserverDesktop::wakeupHandler(fd_set* fds, int nfds) | |||
void XserverDesktop::readWakeupHandler(fd_set* fds, int nfds) | |||
{ | |||
try { | |||
// First check for file descriptors with something to do | |||
@@ -625,8 +498,11 @@ void XserverDesktop::wakeupHandler(fd_set* fds, int nfds) | |||
} | |||
// We are responsible for propagating mouse movement between clients | |||
if (!oldCursorPos.equals(vncInputDevice->getPointerPos())) { | |||
oldCursorPos = vncInputDevice->getPointerPos(); | |||
int cursorX, cursorY; | |||
vncGetPointerPos(&cursorX, &cursorY); | |||
if (oldCursorPos.x != cursorX || oldCursorPos.y != cursorY) { | |||
oldCursorPos.x = cursorX; | |||
oldCursorPos.y = cursorY; | |||
server->setCursorPos(oldCursorPos); | |||
} | |||
} | |||
@@ -639,7 +515,7 @@ void XserverDesktop::wakeupHandler(fd_set* fds, int nfds) | |||
} | |||
} | |||
void XserverDesktop::writeBlockHandler(fd_set* fds) | |||
void XserverDesktop::writeBlockHandler(fd_set* fds, struct timeval ** timeout) | |||
{ | |||
try { | |||
std::list<Socket*> sockets; | |||
@@ -724,26 +600,29 @@ void XserverDesktop::disconnectClients() | |||
} | |||
int XserverDesktop::getQueryTimeout(void* opaqueId, | |||
const char** address, | |||
const char** username) | |||
void XserverDesktop::getQueryConnect(uint32_t* opaqueId, | |||
const char** address, | |||
const char** username, | |||
int *timeout) | |||
{ | |||
if (opaqueId && queryConnectId == opaqueId) { | |||
vlog.info("address=%s, username=%s, timeout=%d", | |||
queryConnectAddress.buf, queryConnectUsername.buf, | |||
(int)queryConnectTimeout); | |||
if (address) *address = queryConnectAddress.buf; | |||
if (username) *username = queryConnectUsername.buf; | |||
return queryConnectTimeout; | |||
*opaqueId = queryConnectId; | |||
if (queryConnectId == 0) { | |||
*address = ""; | |||
*username = ""; | |||
*timeout = 0; | |||
} else { | |||
*address = queryConnectAddress.buf; | |||
*username = queryConnectUsername.buf; | |||
*timeout = rfb::Server::queryConnectTimeout; | |||
} | |||
return 0; | |||
} | |||
void XserverDesktop::approveConnection(void* opaqueId, bool accept, | |||
void XserverDesktop::approveConnection(uint32_t opaqueId, bool accept, | |||
const char* rejectMsg) | |||
{ | |||
if (queryConnectId == opaqueId) { | |||
server->approveConnection((network::Socket*)opaqueId, accept, rejectMsg); | |||
server->approveConnection(queryConnectSocket, accept, rejectMsg); | |||
queryConnectId = 0; | |||
} | |||
} | |||
@@ -755,8 +634,8 @@ void XserverDesktop::approveConnection(void* opaqueId, bool accept, | |||
void XserverDesktop::pointerEvent(const Point& pos, int buttonMask) | |||
{ | |||
vncInputDevice->PointerMove(pos); | |||
vncInputDevice->PointerButtonAction(buttonMask); | |||
vncPointerMove(pos.x, pos.y); | |||
vncPointerButtonAction(buttonMask); | |||
} | |||
void XserverDesktop::clientCutText(const char* str, int len) | |||
@@ -764,79 +643,50 @@ void XserverDesktop::clientCutText(const char* str, int len) | |||
vncClientCutText(str, len); | |||
} | |||
extern RROutputPtr vncRandROutputCreate(ScreenPtr pScreen); | |||
unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, | |||
const rfb::ScreenSet& layout) | |||
{ | |||
#ifndef RANDR | |||
return rfb::resultProhibited; | |||
#else | |||
int ret; | |||
int availableOutputs; | |||
Bool ret; | |||
rrScrPrivPtr rp = rrGetScrPriv(pScreen); | |||
// RandR support? | |||
if (vncRandRGetOutputCount(screenIndex) == 0) | |||
return rfb::resultProhibited; | |||
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); | |||
/* | |||
* First check that we don't have any active clone modes. That's just | |||
* too messy to deal with. | |||
*/ | |||
for (int i = 0;i < rp->numCrtcs;i++) { | |||
if (rp->crtcs[i]->numOutputs > 1) { | |||
vlog.error("Clone mode active. Refusing to touch screen layout."); | |||
return rfb::resultInvalid; | |||
} | |||
if (vncRandRHasOutputClones(screenIndex)) { | |||
vlog.error("Clone mode active. Refusing to touch screen layout."); | |||
return rfb::resultInvalid; | |||
} | |||
/* | |||
* Next count how many useful outputs we have... | |||
* | |||
* 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. | |||
*/ | |||
std::set<RRCrtcPtr> usedCrtcs; | |||
availableOutputs = 0; | |||
for (int i = 0;i < rp->numOutputs;i++) { | |||
RROutputPtr output; | |||
output = rp->outputs[i]; | |||
if (output->crtc != NULL) | |||
availableOutputs++; | |||
else { | |||
for (int j = 0;j < output->numCrtcs;j++) { | |||
if (output->crtcs[j]->numOutputs != 0) | |||
continue; | |||
if (usedCrtcs.count(output->crtcs[j]) != 0) | |||
continue; | |||
availableOutputs++; | |||
usedCrtcs.insert(output->crtcs[j]); | |||
break; | |||
} | |||
} | |||
} | |||
/* Next count how many useful outputs we have... */ | |||
availableOutputs = vncRandRGetAvailableOutputs(screenIndex); | |||
/* Try to create more outputs if needed... (only works on Xvnc) */ | |||
if (layout.num_screens() > availableOutputs) { | |||
for (int i = 0;i < (layout.num_screens() - availableOutputs);i++) { | |||
RROutputPtr output; | |||
output = vncRandROutputCreate(pScreen); | |||
if (output == NULL) { | |||
vlog.error("Unable to create more screens, as needed by the new client layout."); | |||
return rfb::resultInvalid; | |||
} | |||
vlog.debug("Insufficient screens. Need to create %d more.", | |||
layout.num_screens() - availableOutputs); | |||
ret = vncRandRCreateOutputs(screenIndex, | |||
layout.num_screens() - availableOutputs); | |||
if (ret < 0) { | |||
vlog.error("Unable to create more screens, as needed by the new client layout."); | |||
return rfb::resultInvalid; | |||
} | |||
} | |||
/* First we might need to resize the screen */ | |||
if ((fb_width != pScreen->width) || (fb_height != pScreen->height)) { | |||
/* Try to retain DPI when we resize */ | |||
ret = RRScreenSizeSet(pScreen, fb_width, fb_height, | |||
pScreen->mmWidth * fb_width / pScreen->width, | |||
pScreen->mmHeight * fb_height / pScreen->height); | |||
if ((fb_width != vncGetScreenWidth(screenIndex)) || | |||
(fb_height != vncGetScreenHeight(screenIndex))) { | |||
ret = vncRandRResizeScreen(screenIndex, fb_width, fb_height); | |||
if (!ret) { | |||
vlog.error("Failed to resize screen to %dx%d", fb_width, fb_height); | |||
return rfb::resultInvalid; | |||
@@ -844,27 +694,17 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, | |||
} | |||
/* Next, reconfigure all known outputs, and turn off the other ones */ | |||
for (int i = 0;i < rp->numOutputs;i++) { | |||
RROutputPtr output; | |||
RRCrtcPtr crtc; | |||
RRModePtr mode; | |||
for (int i = 0;i < vncRandRGetOutputCount(screenIndex);i++) { | |||
intptr_t output; | |||
ScreenSet::const_iterator iter; | |||
output = rp->outputs[i]; | |||
crtc = output->crtc; | |||
output = vncRandRGetOutputId(screenIndex, i); | |||
/* Known? */ | |||
if (outputIdMap.count(output) == 0) | |||
continue; | |||
/* A known output should have a CRTC, but double check... */ | |||
if (crtc == NULL) { | |||
vlog.error("Existing output '%s' has unexpectedly been disabled", | |||
output->name); | |||
continue; | |||
} | |||
/* Find the corresponding screen... */ | |||
for (iter = layout.begin();iter != layout.end();++iter) { | |||
if (iter->id == outputIdMap[output]) | |||
@@ -874,37 +714,25 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, | |||
/* Missing? */ | |||
if (iter == layout.end()) { | |||
/* Disable and move on... */ | |||
ret = RRCrtcSet(crtc, NULL, crtc->x, crtc->y, crtc->rotation, 0, NULL); | |||
ret = vncRandRDisableOutput(screenIndex, i); | |||
if (!ret) { | |||
vlog.error("Failed to disable unused CRTC for output '%s'", | |||
output->name); | |||
vlog.error("Failed to disable unused output '%s'", | |||
vncRandRGetOutputName(screenIndex, i)); | |||
return rfb::resultInvalid; | |||
} | |||
outputIdMap.erase(output); | |||
continue; | |||
} | |||
/* Need to switch mode? */ | |||
if ((crtc->mode->mode.width == iter->dimensions.width()) && | |||
(crtc->mode->mode.height == iter->dimensions.height())) | |||
mode = crtc->mode; | |||
else { | |||
mode = findRandRMode(output, iter->dimensions.width(), | |||
iter->dimensions.height()); | |||
if (mode == NULL) { | |||
vlog.error("Failed to find a suitable mode for %dx%d for output '%s'", | |||
iter->dimensions.width(), iter->dimensions.height(), | |||
output->name); | |||
return rfb::resultInvalid; | |||
} | |||
} | |||
/* Reconfigure new mode and position */ | |||
ret = RRCrtcSet(crtc, mode, iter->dimensions.tl.x, iter->dimensions.tl.y, | |||
crtc->rotation, crtc->numOutputs, crtc->outputs); | |||
ret = vncRandRReconfigureOutput(screenIndex, i, | |||
iter->dimensions.tl.x, | |||
iter->dimensions.tl.y, | |||
iter->dimensions.width(), | |||
iter->dimensions.height()); | |||
if (!ret) { | |||
vlog.error("Failed to reconfigure output '%s' to %dx%d+%d+%d", | |||
output->name, | |||
vncRandRGetOutputName(screenIndex, i), | |||
iter->dimensions.width(), iter->dimensions.height(), | |||
iter->dimensions.tl.x, iter->dimensions.tl.y); | |||
return rfb::resultInvalid; | |||
@@ -915,11 +743,7 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, | |||
ScreenSet::const_iterator iter; | |||
for (iter = layout.begin();iter != layout.end();++iter) { | |||
OutputIdMap::const_iterator oi; | |||
RROutputPtr output; | |||
RRCrtcPtr crtc; | |||
RRModePtr mode; | |||
intptr_t output; | |||
int i; | |||
/* Does this screen have an output already? */ | |||
@@ -932,53 +756,24 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, | |||
continue; | |||
/* Find an unused output */ | |||
for (i = 0;i < rp->numOutputs;i++) { | |||
output = rp->outputs[i]; | |||
crtc = output->crtc; | |||
for (i = 0;i < vncRandRGetOutputCount(screenIndex);i++) { | |||
output = vncRandRGetOutputId(screenIndex, i); | |||
/* In use? */ | |||
if (outputIdMap.count(output) == 1) | |||
continue; | |||
/* Need a CRTC? */ | |||
if (crtc == NULL) { | |||
for (int j = 0;j < output->numCrtcs;j++) { | |||
if (output->crtcs[j]->numOutputs != 0) | |||
continue; | |||
crtc = output->crtcs[j]; | |||
break; | |||
} | |||
/* Couldn't find one... */ | |||
if (crtc == NULL) | |||
continue; | |||
ret = RRCrtcSet(crtc, NULL, 0, 0, RR_Rotate_0, | |||
1, &output); | |||
if (!ret) { | |||
vlog.error("Failed to associate a CRTC with output '%s'", | |||
output->name); | |||
return rfb::resultInvalid; | |||
} | |||
} | |||
/* Can it be used? */ | |||
if (!vncRandRIsOutputUsable(screenIndex, i)) | |||
continue; | |||
break; | |||
} | |||
/* Shouldn't happen */ | |||
if (i == rp->numOutputs) | |||
if (i == vncRandRGetOutputCount(screenIndex)) | |||
return rfb::resultInvalid; | |||
mode = findRandRMode(output, iter->dimensions.width(), | |||
iter->dimensions.height()); | |||
if (mode == NULL) { | |||
vlog.error("Failed to find a suitable mode for %dx%d for output '%s'", | |||
iter->dimensions.width(), iter->dimensions.height(), | |||
output->name); | |||
return rfb::resultInvalid; | |||
} | |||
/* | |||
* Make sure we already have an entry for this, or | |||
* computeScreenLayout() will think it is a brand new output and | |||
@@ -987,11 +782,14 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, | |||
outputIdMap[output] = iter->id; | |||
/* Reconfigure new mode and position */ | |||
ret = RRCrtcSet(crtc, mode, iter->dimensions.tl.x, iter->dimensions.tl.y, | |||
crtc->rotation, crtc->numOutputs, crtc->outputs); | |||
ret = vncRandRReconfigureOutput(screenIndex, i, | |||
iter->dimensions.tl.x, | |||
iter->dimensions.tl.y, | |||
iter->dimensions.width(), | |||
iter->dimensions.height()); | |||
if (!ret) { | |||
vlog.error("Failed to reconfigure output '%s' to %dx%d+%d+%d", | |||
output->name, | |||
vncRandRGetOutputName(screenIndex, i), | |||
iter->dimensions.width(), iter->dimensions.height(), | |||
iter->dimensions.tl.x, iter->dimensions.tl.y); | |||
return rfb::resultInvalid; | |||
@@ -1003,50 +801,31 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, | |||
* This is normally done in the X11 request handlers, which is | |||
* why we have to deal with it manually here. | |||
*/ | |||
rp->lastSetTime = currentTime; | |||
vncRandRUpdateSetTime(screenIndex); | |||
return rfb::resultSuccess; | |||
#endif | |||
} | |||
void XserverDesktop::grabRegion(const rfb::Region& region) | |||
{ | |||
if (directFbptr) return; | |||
if (!pScreen->GetImage) { | |||
vlog.error("VNC error: pScreen->GetImage == 0"); | |||
if (directFbptr) | |||
return; | |||
} | |||
grabbing = true; | |||
int bytesPerPixel = format.bpp/8; | |||
int bytesPerRow = pScreen->width * bytesPerPixel; | |||
std::vector<rfb::Rect> rects; | |||
std::vector<rfb::Rect>::iterator i; | |||
region.get_rects(&rects); | |||
for (i = rects.begin(); i != rects.end(); i++) { | |||
for (int y = i->tl.y; y < i->br.y; y++) { | |||
DrawablePtr pDrawable; | |||
#if XORG < 19 | |||
pDrawable = (DrawablePtr) WindowTable[pScreen->myNum]; | |||
#else | |||
pDrawable = (DrawablePtr) pScreen->root; | |||
#endif | |||
(*pScreen->GetImage) (pDrawable, i->tl.x, y, i->width(), 1, | |||
ZPixmap, (unsigned long)~0L, | |||
((char*)data | |||
+ y * bytesPerRow + i->tl.x * bytesPerPixel)); | |||
} | |||
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); | |||
} | |||
grabbing = false; | |||
} | |||
void XserverDesktop::keyEvent(rdr::U32 keysym, bool down) | |||
{ | |||
if (down) | |||
vncInputDevice->KeyboardPress(keysym); | |||
else | |||
vncInputDevice->KeyboardRelease(keysym); | |||
vncKeyboardEvent(keysym, down); | |||
} |
@@ -1,5 +1,5 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* Copyright 2009-2011 Pierre Ossman for Cendio AB | |||
* 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 | |||
@@ -29,6 +29,8 @@ | |||
#include <map> | |||
#include <stdint.h> | |||
#include <rfb/SDesktop.h> | |||
#include <rfb/HTTPServer.h> | |||
#include <rfb/PixelBuffer.h> | |||
@@ -37,16 +39,6 @@ | |||
#include <rdr/SubstitutingInStream.h> | |||
#include "Input.h" | |||
extern "C" { | |||
#define class c_class | |||
#include <scrnintstr.h> | |||
#include <os.h> | |||
#ifdef RANDR | |||
#include <randrstr.h> | |||
#endif | |||
#undef class | |||
} | |||
namespace rfb { | |||
class VNCServerST; | |||
} | |||
@@ -58,10 +50,10 @@ class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer, | |||
public rfb::VNCServerST::QueryConnectionHandler { | |||
public: | |||
XserverDesktop(ScreenPtr pScreen, network::TcpListener* listener, | |||
XserverDesktop(int screenIndex, network::TcpListener* listener, | |||
network::TcpListener* httpListener_, | |||
const char* name, const rfb::PixelFormat &pf, | |||
void* fbptr, int stride); | |||
int width, int height, void* fbptr, int stride); | |||
virtual ~XserverDesktop(); | |||
// methods called from X server code | |||
@@ -72,31 +64,27 @@ public: | |||
void bell(); | |||
void serverCutText(const char* str, int len); | |||
void setDesktopName(const char* name); | |||
void setCursor(CursorPtr cursor); | |||
void add_changed(RegionPtr reg); | |||
void add_copied(RegionPtr dst, int dx, int dy); | |||
void ignoreHooks(bool b) { ignoreHooks_ = b; } | |||
void blockHandler(fd_set* fds, OSTimePtr timeout); | |||
void wakeupHandler(fd_set* fds, int nfds); | |||
void writeBlockHandler(fd_set* fds); | |||
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 readBlockHandler(fd_set* fds, struct timeval ** timeout); | |||
void readWakeupHandler(fd_set* fds, int nfds); | |||
void writeBlockHandler(fd_set* fds, struct timeval ** timeout); | |||
void writeWakeupHandler(fd_set* fds, int nfds); | |||
void addClient(network::Socket* sock, bool reverse); | |||
void disconnectClients(); | |||
// QueryConnect methods called from X server code | |||
// getQueryTimeout() | |||
// Returns the timeout associated with a particular | |||
// connection, identified by an opaque Id passed to the | |||
// X code earlier. Also optionally gets the address and | |||
// name associated with that connection. | |||
// Returns zero if the Id is not recognised. | |||
int getQueryTimeout(void* opaqueId, | |||
const char** address=0, | |||
const char** username=0); | |||
// 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(void* opaqueId, bool accept, | |||
void approveConnection(uint32_t opaqueId, bool accept, | |||
const char* rejectMsg=0); | |||
// rfb::SDesktop callbacks | |||
@@ -120,26 +108,23 @@ public: | |||
private: | |||
rfb::ScreenSet computeScreenLayout(); | |||
#ifdef RANDR | |||
RRModePtr findRandRMode(RROutputPtr output, int width, int height); | |||
#endif | |||
ScreenPtr pScreen; | |||
int screenIndex; | |||
rfb::VNCServerST* server; | |||
rfb::HTTPServer* httpServer; | |||
network::TcpListener* listener; | |||
network::TcpListener* httpListener; | |||
bool deferredUpdateTimerSet; | |||
bool grabbing; | |||
bool ignoreHooks_; | |||
bool directFbptr; | |||
struct timeval dixTimeout; | |||
void* queryConnectId; | |||
uint32_t queryConnectId; | |||
network::Socket* queryConnectSocket; | |||
rfb::CharArray queryConnectAddress; | |||
rfb::CharArray queryConnectUsername; | |||
#ifdef RANDR | |||
typedef std::map<RROutputPtr, rdr::U32> OutputIdMap; | |||
typedef std::map<intptr_t, rdr::U32> OutputIdMap; | |||
OutputIdMap outputIdMap; | |||
#endif | |||
@@ -0,0 +1,143 @@ | |||
/* 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 "dix.h" | |||
#include "scrnintstr.h" | |||
#include "vncExtInit.h" | |||
#include "vncBlockHandler.h" | |||
static void vncBlockHandler(void * data, OSTimePtr t, void * readmask); | |||
static void vncWakeupHandler(void * data, int nfds, void * readmask); | |||
void vncWriteBlockHandler(fd_set *fds); | |||
void vncWriteWakeupHandler(int nfds, fd_set *fds); | |||
void vncRegisterBlockHandlers(void) | |||
{ | |||
if (!RegisterBlockAndWakeupHandlers(vncBlockHandler, vncWakeupHandler, 0)) | |||
FatalError("RegisterBlockAndWakeupHandlers() failed\n"); | |||
} | |||
static void vncWriteBlockHandlerFallback(OSTimePtr timeout); | |||
static void vncWriteWakeupHandlerFallback(void); | |||
// | |||
// vncBlockHandler - called just before the X server goes into select(). Call | |||
// on to the block handler for each desktop. Then check whether any of the | |||
// selections have changed, and if so, notify any interested X clients. | |||
// | |||
static void vncBlockHandler(void * data, OSTimePtr timeout, void * readmask) | |||
{ | |||
fd_set* fds = (fd_set*)readmask; | |||
vncWriteBlockHandlerFallback(timeout); | |||
vncCallReadBlockHandlers(fds, timeout); | |||
} | |||
static void vncWakeupHandler(void * data, int nfds, void * readmask) | |||
{ | |||
fd_set* fds = (fd_set*)readmask; | |||
vncCallReadWakeupHandlers(fds, nfds); | |||
vncWriteWakeupHandlerFallback(); | |||
} | |||
// | |||
// vncWriteBlockHandler - extra hack to be able to get the main select loop | |||
// to monitor writeable fds and not just readable. This requirers a modified | |||
// Xorg and might therefore not be called. When it is called though, it will | |||
// do so before vncBlockHandler (and vncWriteWakeupHandler called after | |||
// vncWakeupHandler). | |||
// | |||
static Bool needFallback = TRUE; | |||
static fd_set fallbackFds; | |||
static struct timeval tw; | |||
void vncWriteBlockHandler(fd_set *fds) | |||
{ | |||
struct timeval *dummy; | |||
needFallback = FALSE; | |||
dummy = NULL; | |||
vncCallWriteBlockHandlers(fds, &dummy); | |||
} | |||
void vncWriteWakeupHandler(int nfds, fd_set *fds) | |||
{ | |||
vncCallWriteWakeupHandlers(fds, nfds); | |||
} | |||
static void vncWriteBlockHandlerFallback(OSTimePtr timeout) | |||
{ | |||
if (!needFallback) | |||
return; | |||
FD_ZERO(&fallbackFds); | |||
vncWriteBlockHandler(&fallbackFds); | |||
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); | |||
} |
@@ -0,0 +1,33 @@ | |||
/* 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); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif |
@@ -0,0 +1,771 @@ | |||
/* 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" | |||
#include "selection.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 void vncSelectionCallback(CallbackListPtr *callbacks, | |||
void * data, void * args); | |||
static int vncErrorBase = 0; | |||
static int vncEventBase = 0; | |||
int vncNoClipboard = 0; | |||
static char* clientCutText = NULL; | |||
static int clientCutTextLen = 0; | |||
static struct VncInputSelect* vncInputSelectHead = NULL; | |||
struct VncInputSelect { | |||
ClientPtr client; | |||
Window window; | |||
int mask; | |||
struct VncInputSelect* next; | |||
}; | |||
int vncAddExtension(void) | |||
{ | |||
ExtensionEntry* extEntry; | |||
extEntry = AddExtension(VNCEXTNAME, VncExtNumberEvents, VncExtNumberErrors, | |||
ProcVncExtDispatch, SProcVncExtDispatch, vncResetProc, | |||
StandardMinorOpcode); | |||
if (!extEntry) { | |||
ErrorF("vncAddExtension: AddExtension failed\n"); | |||
return -1; | |||
} | |||
vncErrorBase = extEntry->errorBase; | |||
vncEventBase = extEntry->eventBase; | |||
if (!AddCallback(&ClientStateCallback, vncClientStateChange, 0)) { | |||
FatalError("Add ClientStateCallback failed\n"); | |||
} | |||
if (!AddCallback(&SelectionCallback, vncSelectionCallback, 0)) { | |||
FatalError("Add SelectionCallback failed\n"); | |||
} | |||
return 0; | |||
} | |||
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; | |||
} | |||
void vncClientCutText(const char* str, int len) | |||
{ | |||
if (clientCutText != NULL) | |||
free(clientCutText); | |||
clientCutTextLen = 0; | |||
clientCutText = malloc(len); | |||
if (clientCutText == NULL) { | |||
ErrorF("Could not allocate clipboard buffer\n"); | |||
return; | |||
} | |||
memcpy(clientCutText, str, len); | |||
clientCutTextLen = len; | |||
xVncExtClientCutTextNotifyEvent ev; | |||
ev.type = vncEventBase + VncExtClientCutTextNotify; | |||
for (struct VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) { | |||
if (cur->mask & VncExtClientCutTextMask) { | |||
ev.sequenceNumber = cur->client->sequence; | |||
ev.window = cur->window; | |||
ev.time = GetTimeInMillis(); | |||
if (cur->client->swapped) { | |||
#if XORG < 112 | |||
int n; | |||
swaps(&ev.sequenceNumber, n); | |||
swapl(&ev.window, n); | |||
swapl(&ev.time, n); | |||
#else | |||
swaps(&ev.sequenceNumber); | |||
swapl(&ev.window); | |||
swapl(&ev.time); | |||
#endif | |||
} | |||
WriteToClient(cur->client, sizeof(xVncExtClientCutTextNotifyEvent), | |||
(char *)&ev); | |||
} | |||
} | |||
} | |||
static int ProcVncExtSetParam(ClientPtr client) | |||
{ | |||
char *param; | |||
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'; | |||
xVncExtSetParamReply rep; | |||
rep.type = X_Reply; | |||
rep.length = 0; | |||
rep.success = 0; | |||
rep.sequenceNumber = client->sequence; | |||
/* | |||
* Allow to change only certain parameters. | |||
* Changing other parameters (for example PAM service name) | |||
* could have negative security impact. | |||
*/ | |||
if (strncasecmp(param, "desktop", 7) != 0 && | |||
strncasecmp(param, "AcceptPointerEvents", 19) != 0 && | |||
(vncNoClipboard || strncasecmp(param, "SendCutText", 11) != 0) && | |||
(vncNoClipboard || strncasecmp(param, "AcceptCutText", 13) != 0)) | |||
goto deny; | |||
vncSetParamSimple(param); | |||
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; | |||
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); | |||
xVncExtGetParamReply rep; | |||
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; | |||
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); | |||
xVncExtGetParamDescReply rep; | |||
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, (char*)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) | |||
{ | |||
REQUEST(xVncExtListParamsReq); | |||
REQUEST_SIZE_MATCH(xVncExtListParamsReq); | |||
xVncExtListParamsReply rep; | |||
rep.type = X_Reply; | |||
rep.sequenceNumber = client->sequence; | |||
char *params; | |||
size_t len; | |||
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 ProcVncExtSetServerCutText(ClientPtr client) | |||
{ | |||
REQUEST(xVncExtSetServerCutTextReq); | |||
REQUEST_FIXED_SIZE(xVncExtSetServerCutTextReq, stuff->textLen); | |||
vncServerCutText((const char*)&stuff[1], stuff->textLen); | |||
return (client->noClientException); | |||
} | |||
static int SProcVncExtSetServerCutText(ClientPtr client) | |||
{ | |||
REQUEST(xVncExtSetServerCutTextReq); | |||
#if XORG < 112 | |||
register char n; | |||
swaps(&stuff->length, n); | |||
#else | |||
swaps(&stuff->length); | |||
#endif | |||
REQUEST_AT_LEAST_SIZE(xVncExtSetServerCutTextReq); | |||
#if XORG < 112 | |||
swapl(&stuff->textLen, n); | |||
#else | |||
swapl(&stuff->textLen); | |||
#endif | |||
return ProcVncExtSetServerCutText(client); | |||
} | |||
static int ProcVncExtGetClientCutText(ClientPtr client) | |||
{ | |||
REQUEST(xVncExtGetClientCutTextReq); | |||
REQUEST_SIZE_MATCH(xVncExtGetClientCutTextReq); | |||
xVncExtGetClientCutTextReply rep; | |||
rep.type = X_Reply; | |||
rep.length = (clientCutTextLen + 3) >> 2; | |||
rep.sequenceNumber = client->sequence; | |||
rep.textLen = clientCutTextLen; | |||
if (client->swapped) { | |||
#if XORG < 112 | |||
int n; | |||
swaps(&rep.sequenceNumber, n); | |||
swapl(&rep.length, n); | |||
swapl(&rep.textLen, n); | |||
#else | |||
swaps(&rep.sequenceNumber); | |||
swapl(&rep.length); | |||
swapl(&rep.textLen); | |||
#endif | |||
} | |||
WriteToClient(client, sizeof(xVncExtGetClientCutTextReply), (char *)&rep); | |||
if (clientCutText) | |||
WriteToClient(client, clientCutTextLen, clientCutText); | |||
return (client->noClientException); | |||
} | |||
static int SProcVncExtGetClientCutText(ClientPtr client) | |||
{ | |||
REQUEST(xVncExtGetClientCutTextReq); | |||
#if XORG < 112 | |||
register char n; | |||
swaps(&stuff->length, n); | |||
#else | |||
swaps(&stuff->length); | |||
#endif | |||
REQUEST_SIZE_MATCH(xVncExtGetClientCutTextReq); | |||
return ProcVncExtGetClientCutText(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; | |||
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; | |||
xVncExtConnectReply rep; | |||
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) | |||
{ | |||
REQUEST(xVncExtGetQueryConnectReq); | |||
REQUEST_SIZE_MATCH(xVncExtGetQueryConnectReq); | |||
uint32_t opaqueId; | |||
const char *qcAddress, *qcUsername; | |||
int qcTimeout; | |||
vncGetQueryConnect(&opaqueId, &qcAddress, &qcUsername, &qcTimeout); | |||
xVncExtGetQueryConnectReply rep; | |||
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), (char*)qcAddress); | |||
if (qcTimeout) | |||
WriteToClient(client, strlen(qcUsername), (char*)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_VncExtSetServerCutText: | |||
return ProcVncExtSetServerCutText(client); | |||
case X_VncExtGetClientCutText: | |||
return ProcVncExtGetClientCutText(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_VncExtSetServerCutText: | |||
return SProcVncExtSetServerCutText(client); | |||
case X_VncExtGetClientCutText: | |||
return SProcVncExtGetClientCutText(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) | |||
{ | |||
} | |||
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; | |||
} | |||
} | |||
} | |||
static void SendSelectionChangeEvent(Atom selection) | |||
{ | |||
xVncExtSelectionChangeNotifyEvent ev; | |||
ev.type = vncEventBase + VncExtSelectionChangeNotify; | |||
for (struct VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) { | |||
if (cur->mask & VncExtSelectionChangeMask) { | |||
ev.sequenceNumber = cur->client->sequence; | |||
ev.window = cur->window; | |||
ev.selection = selection; | |||
if (cur->client->swapped) { | |||
#if XORG < 112 | |||
int n; | |||
swaps(&ev.sequenceNumber, n); | |||
swapl(&ev.window, n); | |||
swapl(&ev.selection, n); | |||
#else | |||
swaps(&ev.sequenceNumber); | |||
swapl(&ev.window); | |||
swapl(&ev.selection); | |||
#endif | |||
} | |||
WriteToClient(cur->client, sizeof(xVncExtSelectionChangeNotifyEvent), | |||
(char *)&ev); | |||
} | |||
} | |||
} | |||
static void vncSelectionCallback(CallbackListPtr *callbacks, void * data, void * args) | |||
{ | |||
SelectionInfoRec *info = (SelectionInfoRec *) args; | |||
Selection *selection = info->selection; | |||
SendSelectionChangeEvent(selection->selection); | |||
} | |||
@@ -1,4 +1,5 @@ | |||
/* 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 | |||
@@ -18,21 +19,77 @@ | |||
#ifndef __VNCEXTINIT_H__ | |||
#define __VNCEXTINIT_H__ | |||
#ifdef HAVE_DIX_CONFIG_H | |||
#include <dix-config.h> | |||
#include <stdint.h> | |||
#include <sys/select.h> | |||
// Only from C++ | |||
#ifdef __cplusplus | |||
namespace rfb { class StringParameter; }; | |||
extern rfb::StringParameter httpDir; | |||
#endif | |||
#include <rfb/Configuration.h> | |||
#include "XserverDesktop.h" | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
// vncExt.c | |||
extern int vncNoClipboard; | |||
int vncAddExtension(void); | |||
extern void vncClientCutText(const char* str, int len); | |||
extern void vncQueryConnect(XserverDesktop* desktop, void* opaqueId); | |||
extern void vncClientGone(int fd); | |||
extern void vncBell(); | |||
int vncNotifyQueryConnect(void); | |||
void vncClientCutText(const char* str, int len); | |||
// vncExtInit.cc | |||
extern void* vncFbptr[]; | |||
extern int vncFbstride[]; | |||
extern bool noclipboard; | |||
extern int vncInetdSock; | |||
extern rfb::StringParameter httpDir; | |||
void vncExtensionInit(void); | |||
void vncCallReadBlockHandlers(fd_set * fds, struct timeval ** timeout); | |||
void vncCallReadWakeupHandlers(fd_set * fds, int nfds); | |||
void vncCallWriteBlockHandlers(fd_set * fds, struct timeval ** timeout); | |||
void vncCallWriteWakeupHandlers(fd_set * fds, int nfds); | |||
int vncGetAvoidShiftNumLock(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); | |||
// 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 scrIdx, 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); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif |
@@ -1,4 +1,5 @@ | |||
/* 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 | |||
@@ -18,13 +19,17 @@ | |||
#ifndef __VNCHOOKS_H__ | |||
#define __VNCHOOKS_H__ | |||
#ifdef HAVE_DIX_CONFIG_H | |||
#include <dix-config.h> | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
extern "C" { | |||
#include <screenint.h> | |||
extern Bool vncHooksInit(ScreenPtr pScreen, XserverDesktop* desktop); | |||
int vncHooksInit(int scrIdx); | |||
void vncGetScreenImage(int scrIdx, int x, int y, int width, int height, | |||
char *buffer, int strideBytes); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif |
@@ -1,4 +1,5 @@ | |||
/* 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 | |||
@@ -22,41 +23,26 @@ | |||
#include <dix-config.h> | |||
#endif | |||
#include <rfb/Configuration.h> | |||
#include <rfb/Logger_stdio.h> | |||
#include <rfb/LogWriter.h> | |||
#include <rfb/ScreenSet.h> | |||
#include <rfb/screenTypes.h> | |||
#include "opaque.h" | |||
#ifdef RANDR | |||
#include "randrstr.h" | |||
#endif | |||
#include "xorg-version.h" | |||
extern "C" { | |||
#define class c_class | |||
#define private c_private | |||
#define bool c_bool | |||
#define new c_new | |||
#include "xf86.h" | |||
#include "xf86Module.h" | |||
#ifdef RANDR | |||
#include "randrstr.h" | |||
#endif /* RANDR */ | |||
#undef class | |||
#undef private | |||
#undef bool | |||
#undef new | |||
using namespace rfb; | |||
extern void vncExtensionInit(); | |||
static void vncExtensionInitWithParams(INITARGS); | |||
#include "vncExtInit.h" | |||
#include "RFBGlue.h" | |||
char *listenaddr = NULL; | |||
static void vncModuleInit(INITARGS); | |||
static MODULESETUPPROTO(vncSetup); | |||
ExtensionModule vncExt = | |||
{ | |||
vncExtensionInitWithParams, | |||
vncModuleInit, | |||
"VNC", | |||
#if XORG < 112 | |||
NULL, | |||
@@ -92,42 +78,39 @@ vncSetup(void * module, void * opts, int *errmaj, int *errmin) { | |||
return (void *)1; | |||
} | |||
static void vncExtensionInitWithParams(INITARGS) | |||
static void vncModuleInit(INITARGS) | |||
{ | |||
static char once = 0; | |||
if (!once) { | |||
once++; | |||
rfb::initStdIOLoggers(); | |||
rfb::LogWriter::setLogParams("*:stderr:30"); | |||
rfb::Configuration::enableServerParams(); | |||
for (int scr = 0; scr < screenInfo.numScreens; scr++) { | |||
ScrnInfoPtr pScrn = xf86Screens[scr]; | |||
vncInitRFB(); | |||
for (ParameterIterator i; i.param; i.next()) { | |||
const char *val; | |||
#if XORG < 112 | |||
val = xf86FindOptionValue(pScrn->options, i.param->getName()); | |||
#else | |||
val = xf86FindOptionValue((XF86OptionPtr)pScrn->options, i.param->getName()); | |||
#endif | |||
if (val) | |||
i.param->setParam(val); | |||
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(); | |||
} | |||
} | |||
#ifdef RANDR | |||
RRModePtr vncRandRModeGet(int width, int height) | |||
{ | |||
return NULL; | |||
} | |||
RROutputPtr vncRandROutputCreate(ScreenPtr pScreen) | |||
int vncRandRCreateOutputs(int scrIdx, int extraOutputs) | |||
{ | |||
return NULL; | |||
return -1; | |||
} | |||
#endif |
@@ -1,6 +1,6 @@ | |||
/* Copyright (c) 1993 X Consortium | |||
Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
Copyright 2009 Pierre Ossman for Cendio AB | |||
Copyright 2009-2015 Pierre Ossman for Cendio AB | |||
Permission is hereby granted, free of charge, to any person obtaining | |||
a copy of this software and associated documentation files (the | |||
@@ -32,16 +32,10 @@ from the X Consortium. | |||
#include <dix-config.h> | |||
#endif | |||
#include <rfb/Configuration.h> | |||
#include <rfb/Logger_stdio.h> | |||
#include <rfb/LogWriter.h> | |||
#include <network/TcpSocket.h> | |||
#include "vncExtInit.h" | |||
#include "RFBGlue.h" | |||
#include "xorg-version.h" | |||
extern "C" { | |||
#define class c_class | |||
#define public c_public | |||
#ifdef WIN32 | |||
#include <X11/Xwinsock.h> | |||
#endif | |||
@@ -61,9 +55,7 @@ extern "C" { | |||
#include "gcstruct.h" | |||
#include "input.h" | |||
#include "mipointer.h" | |||
#define new New | |||
#include "micmap.h" | |||
#undef new | |||
#include <sys/types.h> | |||
#include <sys/stat.h> | |||
#include <errno.h> | |||
@@ -89,9 +81,6 @@ extern "C" { | |||
#include "version-config.h" | |||
#endif | |||
#include "site.h" | |||
#undef class | |||
#undef public | |||
} | |||
#if XORG >= 110 | |||
#define Xalloc malloc | |||
@@ -164,11 +153,9 @@ static fbMemType fbmemtype = NORMAL_MEMORY_FB; | |||
static int lastScreen = -1; | |||
static Bool Render = TRUE; | |||
static bool displaySpecified = false; | |||
static Bool displaySpecified = FALSE; | |||
static char displayNumStr[16]; | |||
char *listenaddr = NULL; | |||
static int vncVerbose = DEFAULT_LOG_VERBOSITY; | |||
@@ -212,8 +199,6 @@ vfbBitsPerPixel(int depth) | |||
static void vfbFreeFramebufferMemory(vfbFramebufferInfoPtr pfb); | |||
extern "C" { | |||
#ifdef DPMSExtension | |||
/* Why support DPMS? Because stupid modern desktop environments | |||
such as Unity 2D on Ubuntu 11.10 crashes if DPMS is not | |||
@@ -325,7 +310,6 @@ ddxUseMsg() | |||
ErrorF("-depth D set screen 0's depth\n"); | |||
ErrorF("-pixelformat fmt set pixel format (rgbNNN or bgrNNN)\n"); | |||
ErrorF("-inetd has been launched from inetd\n"); | |||
ErrorF("-interface IP_address listen on specified interface\n"); | |||
ErrorF("-noclipboard disable clipboard settings modification via vncconfig utility\n"); | |||
ErrorF("-verbose [n] verbose startup messages\n"); | |||
ErrorF("-quiet minimal startup messages\n"); | |||
@@ -338,8 +322,7 @@ ddxUseMsg() | |||
"Other valid forms are <param>=<value> -<param>=<value> " | |||
"--<param>=<value>\n" | |||
"Parameter names are case-insensitive. The parameters are:\n\n"); | |||
rfb::Configuration::listParams(79, 14); | |||
} | |||
vncListParams(79, 14); | |||
} | |||
/* ddxInitGlobals - called by |InitGlobals| from os/util.c */ | |||
@@ -348,21 +331,21 @@ void ddxInitGlobals(void) | |||
} | |||
static | |||
bool displayNumFree(int num) | |||
Bool displayNumFree(int num) | |||
{ | |||
try { | |||
network::TcpListener l(NULL, 6000+num); | |||
} catch (rdr::Exception& e) { | |||
return false; | |||
} | |||
char file[256]; | |||
if (vncIsTCPPortUsed(6000+num)) | |||
return FALSE; | |||
sprintf(file, "/tmp/.X%d-lock", num); | |||
if (access(file, F_OK) == 0) return false; | |||
if (access(file, F_OK) == 0) | |||
return FALSE; | |||
sprintf(file, "/tmp/.X11-unix/X%d", num); | |||
if (access(file, F_OK) == 0) return false; | |||
if (access(file, F_OK) == 0) | |||
return FALSE; | |||
sprintf(file, "/usr/spool/sockets/X11/%d", num); | |||
if (access(file, F_OK) == 0) return false; | |||
return true; | |||
if (access(file, F_OK) == 0) | |||
return FALSE; | |||
return TRUE; | |||
} | |||
int | |||
@@ -375,13 +358,11 @@ ddxProcessArgument(int argc, char *argv[], int i) | |||
vfbInitializeDefaultScreens(); | |||
vfbInitializePixmapDepths(); | |||
firstTime = FALSE; | |||
rfb::initStdIOLoggers(); | |||
rfb::LogWriter::setLogParams("*:stderr:30"); | |||
rfb::Configuration::enableServerParams(); | |||
vncInitRFB(); | |||
} | |||
if (argv[i][0] == ':') | |||
displaySpecified = true; | |||
displaySpecified = TRUE; | |||
if (strcmp (argv[i], "-screen") == 0) /* -screen n WxHxD */ | |||
{ | |||
@@ -574,7 +555,7 @@ ddxProcessArgument(int argc, char *argv[], int i) | |||
close(2); | |||
if (!displaySpecified) { | |||
int port = network::TcpSocket::getSockPort(vncInetdSock); | |||
int port = vncGetSocketPort(vncInetdSock); | |||
int displayNum = port - 5900; | |||
if (displayNum < 0 || displayNum > 99 || !displayNumFree(displayNum)) { | |||
for (displayNum = 1; displayNum < 100; displayNum++) | |||
@@ -591,25 +572,8 @@ ddxProcessArgument(int argc, char *argv[], int i) | |||
return 1; | |||
} | |||
if (strcmp(argv[i], "-interface") == 0 || | |||
strcmp(argv[i], "-i") == 0) { | |||
if (++i >= argc) { | |||
UseMsg(); | |||
return 2; | |||
} | |||
if (listenaddr != NULL) /* Only first -interface is valid */ | |||
return 2; | |||
listenaddr = strdup(argv[i]); | |||
if (listenaddr == NULL) | |||
FatalError("Not enough memory"); | |||
return 2; | |||
} | |||
if (strcmp(argv[i], "-noclipboard") == 0) { | |||
noclipboard = true; | |||
vncNoClipboard = 1; | |||
return 1; | |||
} | |||
@@ -636,11 +600,11 @@ ddxProcessArgument(int argc, char *argv[], int i) | |||
return 1; | |||
} | |||
if (rfb::Configuration::setParam(argv[i])) | |||
if (vncSetParamSimple(argv[i])) | |||
return 1; | |||
if (argv[i][0] == '-' && i+1 < argc) { | |||
if (rfb::Configuration::setParam(&argv[i][1], argv[i+1])) | |||
if (vncSetParam(&argv[i][1], argv[i+1])) | |||
return 2; | |||
} | |||
@@ -1337,15 +1301,18 @@ static RRCrtcPtr vncRandRCrtcCreate(ScreenPtr pScreen) | |||
} | |||
/* Used from XserverDesktop when it needs more outputs... */ | |||
RROutputPtr vncRandROutputCreate(ScreenPtr pScreen) | |||
int vncRandRCreateOutputs(int scrIdx, int extraOutputs) | |||
{ | |||
RRCrtcPtr crtc; | |||
crtc = vncRandRCrtcCreate(pScreen); | |||
if (crtc == NULL) | |||
return NULL; | |||
while (extraOutputs > 0) { | |||
crtc = vncRandRCrtcCreate(screenInfo.screens[scrIdx]); | |||
if (crtc == NULL) | |||
return -1; | |||
extraOutputs--; | |||
} | |||
return crtc->outputs[0]; | |||
return 0; | |||
} | |||
static Bool vncRandRInit(ScreenPtr pScreen) | |||
@@ -1596,13 +1563,13 @@ vfbScreenInit(ScreenPtr pScreen, int argc, char **argv) | |||
} /* end vfbScreenInit */ | |||
static void vfbClientStateChange(CallbackListPtr*, void *, void *) { | |||
static void vfbClientStateChange(CallbackListPtr *a, void *b, void *c) { | |||
dispatchException &= ~DE_RESET; | |||
} | |||
#if XORG >= 113 | |||
#ifdef GLXEXT | |||
extern "C" void GlxExtensionInit(void); | |||
extern void GlxExtensionInit(void); | |||
static ExtensionModule glxExt = { | |||
GlxExtensionInit, | |||
@@ -1615,11 +1582,12 @@ static ExtensionModule glxExt = { | |||
void | |||
InitOutput(ScreenInfo *screenInfo, int argc, char **argv) | |||
{ | |||
int i; | |||
int NumFormats = 0; | |||
ErrorF("\nXvnc %s - built %s\n%s", XVNCVERSION, buildtime, XVNCCOPYRIGHT); | |||
ErrorF("Underlying X server release %d, %s\n\n", VENDOR_RELEASE, | |||
VENDOR_STRING); | |||
int i; | |||
int NumFormats = 0; | |||
#if XORG >= 113 | |||
#ifdef GLXEXT | |||
@@ -1722,3 +1690,11 @@ void CloseInput(void) | |||
{ | |||
} | |||
#endif | |||
void vncClientGone(int fd) | |||
{ | |||
if (fd == vncInetdSock) { | |||
ErrorF("inetdSock client gone\n"); | |||
GiveUp(0); | |||
} | |||
} |
@@ -798,8 +798,11 @@ void DesktopWindow::remoteResize(int width, int height) | |||
(layout == cc->cp.screenLayout)) | |||
return; | |||
vlog.debug("Requesting framebuffer resize from %dx%d to %dx%d (%d screens)", | |||
cc->cp.width, cc->cp.height, width, height, layout.num_screens()); | |||
char buffer[2048]; | |||
vlog.debug("Requesting framebuffer resize from %dx%d to %dx%d", | |||
cc->cp.width, cc->cp.height, width, height); | |||
layout.print(buffer, sizeof(buffer)); | |||
vlog.debug("%s", buffer); | |||
if (!layout.validate(width, height)) { | |||
vlog.error(_("Invalid screen layout computed for resize request!")); |