@@ -34,6 +34,7 @@ Incomplete and generally out of date copyright list: | |||
Copyright (C) 2009-2011 D. R. Commander | |||
Copyright (C) 2009-2011 Pierre Ossman for Cendio AB | |||
Copyright (C) 2004, 2009-2011 Red Hat, Inc. | |||
Copyright (C) 2017 Peter Astrand for Cendio AB | |||
Copyright (C) 2009-2017 TigerVNC Team | |||
All Rights Reserved. | |||
@@ -50,6 +50,7 @@ namespace network { | |||
// if shutdown() is overridden then the override MUST call on to here | |||
virtual void shutdown() {isShutdown_ = true;} | |||
bool isShutdown() const {return isShutdown_;} | |||
virtual bool cork(bool enable) = 0; | |||
// information about this end of the socket | |||
virtual int getMyPort() = 0; |
@@ -349,12 +349,12 @@ bool TcpSocket::enableNagles(int sock, bool enable) { | |||
return true; | |||
} | |||
bool TcpSocket::cork(int sock, bool enable) { | |||
bool TcpSocket::cork(bool enable) { | |||
#ifndef TCP_CORK | |||
return false; | |||
#else | |||
int one = enable ? 1 : 0; | |||
if (setsockopt(sock, IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0) | |||
if (setsockopt(getFd(), IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0) | |||
return false; | |||
return true; | |||
#endif |
@@ -62,9 +62,9 @@ namespace network { | |||
virtual bool sameMachine(); | |||
virtual void shutdown(); | |||
virtual bool cork(bool enable); | |||
static bool enableNagles(int sock, bool enable); | |||
static bool cork(int sock, bool enable); | |||
static bool isListening(int sock); | |||
static int getSockPort(int sock); | |||
private: |
@@ -1,5 +1,6 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* Copyright 2011 Pierre Ossman for Cendio AB | |||
* Copyright 2017 Peter Astrand <astrand@cendio.se> 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 | |||
@@ -34,6 +35,7 @@ | |||
#include <sys/types.h> | |||
#include <unistd.h> | |||
#include <sys/time.h> | |||
#include <sys/socket.h> | |||
#endif | |||
/* Old systems have select() in sys/time.h */ | |||
@@ -193,7 +195,14 @@ int FdOutStream::writeWithTimeout(const void* data, int length, int timeoutms) | |||
return 0; | |||
do { | |||
n = ::write(fd, data, length); | |||
// select only guarantees that you can write SO_SNDLOWAT without | |||
// blocking, which is normally 1. Use MSG_DONTWAIT to avoid | |||
// blocking, when possible. | |||
#ifndef MSG_DONTWAIT | |||
n = ::send(fd, (const char*)data, length, 0); | |||
#else | |||
n = ::send(fd, (const char*)data, length, MSG_DONTWAIT); | |||
#endif | |||
} while (n < 0 && (errno == EINTR)); | |||
if (n < 0) |
@@ -1,5 +1,6 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* Copyright 2004-2005 Cendio AB. | |||
* Copyright 2017 Peter Astrand <astrand@cendio.se> 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 | |||
@@ -166,6 +167,23 @@ void Configuration::list(int width, int nameWidth) { | |||
} | |||
bool Configuration::remove(const char* param) { | |||
VoidParameter *current = head; | |||
VoidParameter **prevnext = &head; | |||
while (current) { | |||
if (strcasecmp(current->getName(), param) == 0) { | |||
*prevnext = current->_next; | |||
return true; | |||
} | |||
prevnext = ¤t->_next; | |||
current = current->_next; | |||
} | |||
return false; | |||
} | |||
// -=- VoidParameter | |||
VoidParameter::VoidParameter(const char* name_, const char* desc_, |
@@ -80,6 +80,9 @@ namespace rfb { | |||
// - List the parameters of this Configuration group | |||
void list(int width=79, int nameWidth=10); | |||
// - Remove a parameter from this Configuration group | |||
bool remove(const char* param); | |||
// - readFromFile | |||
// Read configuration parameters from the specified file. | |||
void readFromFile(const char* filename); | |||
@@ -116,6 +119,9 @@ namespace rfb { | |||
static void listParams(int width=79, int nameWidth=10) { | |||
global()->list(width, nameWidth); | |||
} | |||
static bool removeParam(const char* param) { | |||
return global()->remove(param); | |||
} | |||
private: | |||
friend class VoidParameter; |
@@ -26,11 +26,6 @@ | |||
#include <rfb/util.h> | |||
#include <rfb/LogWriter.h> | |||
// XXX Lynx/OS 2.3: proto for gettimeofday() | |||
#ifdef Lynx | |||
#include <sys/proto.h> | |||
#endif | |||
using namespace rfb; | |||
#ifndef __NO_DEFINE_VLOG__ |
@@ -168,7 +168,7 @@ void VNCSConnectionST::processMessages() | |||
// Get the underlying TCP layer to build large packets if we send | |||
// multiple small responses. | |||
network::TcpSocket::cork(sock->getFd(), true); | |||
sock->cork(true); | |||
while (getInStream()->checkNoWait(1)) { | |||
if (pendingSyncFence) { | |||
@@ -185,7 +185,7 @@ void VNCSConnectionST::processMessages() | |||
} | |||
// Flush out everything in case we go idle after this. | |||
network::TcpSocket::cork(sock->getFd(), false); | |||
sock->cork(false); | |||
inProcessMessages = false; | |||
@@ -1094,7 +1094,7 @@ void VNCSConnectionST::writeFramebufferUpdate() | |||
// mode, we will also have small fence messages around the update. We | |||
// need to aggregate these in order to not clog up TCP's congestion | |||
// window. | |||
network::TcpSocket::cork(sock->getFd(), true); | |||
sock->cork(true); | |||
// First take care of any updates that cannot contain framebuffer data | |||
// changes. | |||
@@ -1103,7 +1103,7 @@ void VNCSConnectionST::writeFramebufferUpdate() | |||
// Then real data (if possible) | |||
writeDataUpdate(); | |||
network::TcpSocket::cork(sock->getFd(), false); | |||
sock->cork(false); | |||
} | |||
void VNCSConnectionST::writeNoDataUpdate() |
@@ -31,11 +31,6 @@ | |||
#include "TXWindow.h" | |||
#include <errno.h> | |||
// XXX Lynx/OS 2.3: protos for bzero(), select() | |||
#ifdef Lynx | |||
#include <sys/proto.h> | |||
#endif | |||
class TXDialog : public TXWindow, public TXDeleteWindowCallback { | |||
public: | |||
TXDialog(Display* dpy, int width, int height, const char* name, |
@@ -14,6 +14,7 @@ add_executable(x0vncserver | |||
qnum_to_xorgkbd.c | |||
x0vncserver.cxx | |||
XPixelBuffer.cxx | |||
XDesktop.cxx | |||
../vncconfig/QueryConnectDialog.cxx | |||
) | |||
@@ -23,6 +23,8 @@ | |||
#include <rfb/LogWriter.h> | |||
#include <x0vncserver/Geometry.h> | |||
using namespace rfb; | |||
static LogWriter vlog("Geometry"); | |||
StringParameter Geometry::m_geometryParam("Geometry", |
@@ -26,8 +26,6 @@ | |||
#include <rfb/Rect.h> | |||
#include <rfb/Configuration.h> | |||
using namespace rfb; | |||
class Geometry | |||
{ | |||
public: | |||
@@ -43,18 +41,18 @@ public: | |||
int offsetTop() const { return m_rect.tl.y; } | |||
// Return the same information as a Rect structure. | |||
const Rect& getRect() const { return m_rect; } | |||
const rfb::Rect& getRect() const { return m_rect; } | |||
protected: | |||
// Parse a string, extract size and coordinates, | |||
// and return that rectangle clipped to m_rect. | |||
Rect parseString(const char *arg) const; | |||
rfb::Rect parseString(const char *arg) const; | |||
static StringParameter m_geometryParam; | |||
static rfb::StringParameter m_geometryParam; | |||
int m_fullWidth; | |||
int m_fullHeight; | |||
Rect m_rect; | |||
rfb::Rect m_rect; | |||
}; | |||
#endif // __GEOMETRY_H__ |
@@ -24,10 +24,8 @@ | |||
#include <stdlib.h> | |||
#include <sys/types.h> | |||
#ifdef HAVE_MITSHM | |||
#include <sys/ipc.h> | |||
#include <sys/shm.h> | |||
#endif | |||
#include <rfb/LogWriter.h> | |||
#include <x0vncserver/Image.h> | |||
@@ -196,8 +194,6 @@ void Image::updateRect(Image *src, int dst_x, int dst_y, | |||
updateRect(src->xim, dst_x, dst_y, src_x, src_y, w, h); | |||
} | |||
#ifdef HAVE_MITSHM | |||
// | |||
// ShmImage class implementation. | |||
// | |||
@@ -323,182 +319,14 @@ void ShmImage::get(Window wnd, int x, int y, int w, int h, | |||
XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, dst_x, dst_y); | |||
} | |||
#ifdef HAVE_READDISPLAY | |||
// | |||
// IrixOverlayShmImage class implementation. | |||
// | |||
IrixOverlayShmImage::IrixOverlayShmImage(Display *d) | |||
: ShmImage(d), readDisplayBuf(NULL) | |||
{ | |||
} | |||
IrixOverlayShmImage::IrixOverlayShmImage(Display *d, int width, int height) | |||
: ShmImage(d), readDisplayBuf(NULL) | |||
{ | |||
Init(width, height); | |||
} | |||
void IrixOverlayShmImage::Init(int width, int height) | |||
{ | |||
// First determine the pixel format used by XReadDisplay. | |||
XVisualInfo vinfo; | |||
if (!getOverlayVisualInfo(&vinfo)) | |||
return; | |||
// Create an SHM image of the same format. | |||
ShmImage::Init(width, height, &vinfo); | |||
if (xim == NULL) | |||
return; | |||
// FIXME: Check if the extension is available at run time. | |||
readDisplayBuf = XShmCreateReadDisplayBuf(dpy, NULL, shminfo, width, height); | |||
} | |||
bool IrixOverlayShmImage::getOverlayVisualInfo(XVisualInfo *vinfo_ret) | |||
{ | |||
// First, get an image in the format returned by XReadDisplay. | |||
unsigned long hints = 0, hints_ret; | |||
XImage *testImage = XReadDisplay(dpy, DefaultRootWindow(dpy), | |||
0, 0, 8, 8, hints, &hints_ret); | |||
if (testImage == NULL) | |||
return false; | |||
// Fill in a template for matching visuals. | |||
XVisualInfo tmpl; | |||
tmpl.c_class = TrueColor; | |||
tmpl.depth = 24; | |||
tmpl.red_mask = testImage->red_mask; | |||
tmpl.green_mask = testImage->green_mask; | |||
tmpl.blue_mask = testImage->blue_mask; | |||
// List fields in template that make sense. | |||
long mask = (VisualClassMask | | |||
VisualRedMaskMask | | |||
VisualGreenMaskMask | | |||
VisualBlueMaskMask); | |||
// We don't need that image any more. | |||
XDestroyImage(testImage); | |||
// Now, get a list of matching visuals available. | |||
int nVisuals; | |||
XVisualInfo *vinfo = XGetVisualInfo(dpy, mask, &tmpl, &nVisuals); | |||
if (vinfo == NULL || nVisuals <= 0) { | |||
if (vinfo != NULL) { | |||
XFree(vinfo); | |||
} | |||
return false; | |||
} | |||
// Use first visual from the list. | |||
*vinfo_ret = vinfo[0]; | |||
XFree(vinfo); | |||
return true; | |||
} | |||
IrixOverlayShmImage::~IrixOverlayShmImage() | |||
{ | |||
if (readDisplayBuf != NULL) | |||
XShmDestroyReadDisplayBuf(readDisplayBuf); | |||
} | |||
void IrixOverlayShmImage::get(Window wnd, int x, int y) | |||
{ | |||
get(wnd, x, y, xim->width, xim->height); | |||
} | |||
void IrixOverlayShmImage::get(Window wnd, int x, int y, int w, int h, | |||
int dst_x, int dst_y) | |||
{ | |||
XRectangle rect; | |||
unsigned long hints = XRD_TRANSPARENT | XRD_READ_POINTER; | |||
rect.x = x; | |||
rect.y = y; | |||
rect.width = w; | |||
rect.height = h; | |||
XShmReadDisplayRects(dpy, wnd, | |||
&rect, 1, readDisplayBuf, | |||
dst_x - x, dst_y - y, | |||
hints, &hints); | |||
} | |||
#endif // HAVE_READDISPLAY | |||
#endif // HAVE_MITSHM | |||
#ifdef HAVE_SUN_OVL | |||
// | |||
// SolarisOverlayImage class implementation | |||
// | |||
SolarisOverlayImage::SolarisOverlayImage(Display *d) | |||
: Image(d) | |||
{ | |||
} | |||
SolarisOverlayImage::SolarisOverlayImage(Display *d, int width, int height) | |||
: Image(d) | |||
{ | |||
Init(width, height); | |||
} | |||
void SolarisOverlayImage::Init(int width, int height) | |||
{ | |||
// FIXME: Check if the extension is available at run time. | |||
// FIXME: Maybe just read a small (e.g. 8x8) screen area then | |||
// reallocate xim->data[] and correct width and height? | |||
xim = XReadScreen(dpy, DefaultRootWindow(dpy), 0, 0, width, height, True); | |||
if (xim == NULL) { | |||
vlog.error("XReadScreen() failed"); | |||
return; | |||
} | |||
} | |||
SolarisOverlayImage::~SolarisOverlayImage() | |||
{ | |||
} | |||
void SolarisOverlayImage::get(Window wnd, int x, int y) | |||
{ | |||
get(wnd, x, y, xim->width, xim->height); | |||
} | |||
void SolarisOverlayImage::get(Window wnd, int x, int y, int w, int h, | |||
int dst_x, int dst_y) | |||
{ | |||
XImage *tmp_xim = XReadScreen(dpy, wnd, x, y, w, h, True); | |||
if (tmp_xim == NULL) | |||
return; | |||
updateRect(tmp_xim, dst_x, dst_y); | |||
XDestroyImage(tmp_xim); | |||
} | |||
#endif // HAVE_SUN_OVL | |||
// | |||
// ImageFactory class implementation | |||
// | |||
// FIXME: Make ImageFactory always create images of the same class? | |||
// | |||
// Prepare useful shortcuts for compile-time options. | |||
#if defined(HAVE_READDISPLAY) && defined(HAVE_MITSHM) | |||
#define HAVE_SHM_READDISPLAY | |||
#endif | |||
#if defined(HAVE_SHM_READDISPLAY) || defined(HAVE_SUN_OVL) | |||
#define HAVE_OVERLAY_EXT | |||
#endif | |||
ImageFactory::ImageFactory(bool allowShm, bool allowOverlay) | |||
: mayUseShm(allowShm), mayUseOverlay(allowOverlay) | |||
ImageFactory::ImageFactory(bool allowShm) | |||
: mayUseShm(allowShm) | |||
{ | |||
} | |||
@@ -510,33 +338,8 @@ Image *ImageFactory::newImage(Display *d, int width, int height) | |||
{ | |||
Image *image = NULL; | |||
// First, try to create an image with overlay support. | |||
#ifdef HAVE_OVERLAY_EXT | |||
if (mayUseOverlay) { | |||
#if defined(HAVE_SHM_READDISPLAY) | |||
if (mayUseShm) { | |||
image = new IrixOverlayShmImage(d, width, height); | |||
if (image->xim != NULL) { | |||
return image; | |||
} | |||
} | |||
#elif defined(HAVE_SUN_OVL) | |||
image = new SolarisOverlayImage(d, width, height); | |||
if (image->xim != NULL) { | |||
return image; | |||
} | |||
#endif | |||
if (image != NULL) { | |||
delete image; | |||
vlog.error("Failed to create overlay image, trying other options"); | |||
} | |||
} | |||
#endif // HAVE_OVERLAY_EXT | |||
// Now, try to use shared memory image. | |||
#ifdef HAVE_MITSHM | |||
if (mayUseShm) { | |||
image = new ShmImage(d, width, height); | |||
if (image->xim != NULL) { | |||
@@ -546,7 +349,6 @@ Image *ImageFactory::newImage(Display *d, int width, int height) | |||
delete image; | |||
vlog.error("Failed to create SHM image, falling back to Xlib image"); | |||
} | |||
#endif // HAVE_MITSHM | |||
// Fall back to Xlib image. | |||
@@ -92,8 +92,6 @@ protected: | |||
// ShmImage uses MIT-SHM extension of an X server to get image data. | |||
// | |||
#ifdef HAVE_MITSHM | |||
#include <X11/extensions/XShm.h> | |||
class ShmImage : public Image { | |||
@@ -123,89 +121,6 @@ protected: | |||
}; | |||
// | |||
// IrixOverlayShmImage uses ReadDisplay extension of an X server to | |||
// get truecolor image data, regardless of the default X visual type. | |||
// This method is available on Irix only. | |||
// | |||
#ifdef HAVE_READDISPLAY | |||
#include <X11/extensions/readdisplay.h> | |||
class IrixOverlayShmImage : public ShmImage { | |||
public: | |||
IrixOverlayShmImage(Display *d); | |||
IrixOverlayShmImage(Display *d, int width, int height); | |||
virtual ~IrixOverlayShmImage(); | |||
virtual const char *className() const { | |||
return "IrixOverlayShmImage"; | |||
} | |||
virtual const char *classDesc() const { | |||
return "IRIX-specific SHM-aware overlay image"; | |||
} | |||
virtual void get(Window wnd, int x = 0, int y = 0); | |||
virtual void get(Window wnd, int x, int y, int w, int h, | |||
int dst_x = 0, int dst_y = 0); | |||
protected: | |||
void Init(int width, int height); | |||
// This method searches available X visuals for one that matches | |||
// actual pixel format returned by XReadDisplay(). Returns true on | |||
// success, false if there is no matching visual. On success, visual | |||
// information is placed into the structure pointed by vinfo_ret. | |||
bool getOverlayVisualInfo(XVisualInfo *vinfo_ret); | |||
ShmReadDisplayBuf *readDisplayBuf; | |||
}; | |||
#endif // HAVE_READDISPLAY | |||
#endif // HAVE_MITSHM | |||
// | |||
// SolarisOverlayImage uses SUN_OVL extension of an X server to get | |||
// truecolor image data, regardless of the default X visual type. This | |||
// method is available on Solaris only. | |||
// | |||
#ifdef HAVE_SUN_OVL | |||
#include <X11/extensions/transovl.h> | |||
class SolarisOverlayImage : public Image { | |||
public: | |||
SolarisOverlayImage(Display *d); | |||
SolarisOverlayImage(Display *d, int width, int height); | |||
virtual ~SolarisOverlayImage(); | |||
virtual const char *className() const { | |||
return "SolarisOverlayImage"; | |||
} | |||
virtual const char *classDesc() const { | |||
return "Solaris-specific non-SHM overlay image"; | |||
} | |||
virtual void get(Window wnd, int x = 0, int y = 0); | |||
virtual void get(Window wnd, int x, int y, int w, int h, | |||
int dst_x = 0, int dst_y = 0); | |||
protected: | |||
void Init(int width, int height); | |||
}; | |||
#endif // HAVE_SUN_OVL | |||
// | |||
// ImageFactory class is used to produce instances of Image-derived | |||
// objects that are most appropriate for current X server and user | |||
@@ -216,18 +131,16 @@ class ImageFactory { | |||
public: | |||
ImageFactory(bool allowShm, bool allowOverlay); | |||
ImageFactory(bool allowShm); | |||
virtual ~ImageFactory(); | |||
bool isShmAllowed() { return mayUseShm; } | |||
bool isOverlayAllowed() { return mayUseOverlay; } | |||
virtual Image *newImage(Display *d, int width, int height); | |||
protected: | |||
bool mayUseShm; | |||
bool mayUseOverlay; | |||
}; | |||
@@ -31,6 +31,8 @@ | |||
#include <x0vncserver/PollingManager.h> | |||
using namespace rfb; | |||
static LogWriter vlog("PollingMgr"); | |||
const int PollingManager::m_pollingOrder[32] = { |
@@ -32,8 +32,6 @@ | |||
#include <x0vncserver/TimeMillis.h> | |||
#endif | |||
using namespace rfb; | |||
class PollingManager { | |||
public: | |||
@@ -42,12 +40,12 @@ public: | |||
int offsetLeft = 0, int offsetTop = 0); | |||
virtual ~PollingManager(); | |||
void poll(VNCServer *server); | |||
void poll(rfb::VNCServer *server); | |||
protected: | |||
// Screen polling. Returns true if some changes were detected. | |||
bool pollScreen(VNCServer *server); | |||
bool pollScreen(rfb::VNCServer *server); | |||
Display *m_dpy; | |||
@@ -85,7 +83,7 @@ private: | |||
int checkRow(int x, int y, int w); | |||
int checkColumn(int x, int y, int h, bool *pChangeFlags); | |||
int sendChanges(VNCServer *server) const; | |||
int sendChanges(rfb::VNCServer *server) const; | |||
// Check neighboring tiles and update m_changeFlags[]. | |||
void checkNeighbors(); |
@@ -22,11 +22,6 @@ | |||
#include <x0vncserver/TimeMillis.h> | |||
// XXX Lynx/OS 2.3: get proto for gettimeofday() | |||
#ifdef Lynx | |||
#include <sys/proto.h> | |||
#endif | |||
TimeMillis::TimeMillis() | |||
{ | |||
update(); |
@@ -0,0 +1,427 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* Copyright (C) 2004-2008 Constantin Kaplinsky. All Rights Reserved. | |||
* Copyright 2017 Peter Astrand <astrand@cendio.se> 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 <x0vncserver/XDesktop.h> | |||
#include <X11/XKBlib.h> | |||
#ifdef HAVE_XTEST | |||
#include <X11/extensions/XTest.h> | |||
#endif | |||
#ifdef HAVE_XDAMAGE | |||
#include <X11/extensions/Xdamage.h> | |||
#endif | |||
#ifdef HAVE_XFIXES | |||
#include <X11/extensions/Xfixes.h> | |||
#endif | |||
#include <x0vncserver/Geometry.h> | |||
#include <x0vncserver/XPixelBuffer.h> | |||
using namespace rfb; | |||
extern const unsigned short code_map_qnum_to_xorgevdev[]; | |||
extern const unsigned int code_map_qnum_to_xorgevdev_len; | |||
extern const unsigned short code_map_qnum_to_xorgkbd[]; | |||
extern const unsigned int code_map_qnum_to_xorgkbd_len; | |||
extern rfb::BoolParameter useShm; | |||
extern rfb::BoolParameter rawKeyboard; | |||
static rfb::LogWriter vlog("XDesktop"); | |||
// order is important as it must match RFB extension | |||
static const char * ledNames[XDESKTOP_N_LEDS] = { | |||
"Scroll Lock", "Num Lock", "Caps Lock" | |||
}; | |||
XDesktop::XDesktop(Display* dpy_, Geometry *geometry_) | |||
: dpy(dpy_), geometry(geometry_), pb(0), server(0), | |||
oldButtonMask(0), haveXtest(false), haveDamage(false), | |||
maxButtons(0), running(false), ledMasks(), ledState(0), | |||
codeMap(0), codeMapLen(0) | |||
{ | |||
int major, minor; | |||
int xkbOpcode, xkbErrorBase; | |||
major = XkbMajorVersion; | |||
minor = XkbMinorVersion; | |||
if (!XkbQueryExtension(dpy, &xkbOpcode, &xkbEventBase, | |||
&xkbErrorBase, &major, &minor)) { | |||
vlog.error("XKEYBOARD extension not present"); | |||
throw Exception(); | |||
} | |||
XkbSelectEvents(dpy, XkbUseCoreKbd, XkbIndicatorStateNotifyMask, | |||
XkbIndicatorStateNotifyMask); | |||
// figure out bit masks for the indicators we are interested in | |||
for (int i = 0; i < XDESKTOP_N_LEDS; i++) { | |||
Atom a; | |||
int shift; | |||
Bool on; | |||
a = XInternAtom(dpy, ledNames[i], True); | |||
if (!a || !XkbGetNamedIndicator(dpy, a, &shift, &on, NULL, NULL)) | |||
continue; | |||
ledMasks[i] = 1u << shift; | |||
vlog.debug("Mask for '%s' is 0x%x", ledNames[i], ledMasks[i]); | |||
if (on) | |||
ledState |= 1u << i; | |||
} | |||
// X11 unfortunately uses keyboard driver specific keycodes and provides no | |||
// direct way to query this, so guess based on the keyboard mapping | |||
XkbDescPtr desc = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd); | |||
if (desc && desc->names) { | |||
char *keycodes = XGetAtomName(dpy, desc->names->keycodes); | |||
if (keycodes) { | |||
if (strncmp("evdev", keycodes, strlen("evdev")) == 0) { | |||
codeMap = code_map_qnum_to_xorgevdev; | |||
codeMapLen = code_map_qnum_to_xorgevdev_len; | |||
vlog.info("Using evdev codemap\n"); | |||
} else if (strncmp("xfree86", keycodes, strlen("xfree86")) == 0) { | |||
codeMap = code_map_qnum_to_xorgkbd; | |||
codeMapLen = code_map_qnum_to_xorgkbd_len; | |||
vlog.info("Using xorgkbd codemap\n"); | |||
} else { | |||
vlog.info("Unknown keycode '%s', no codemap\n", keycodes); | |||
} | |||
XFree(keycodes); | |||
} else { | |||
vlog.debug("Unable to get keycode map\n"); | |||
} | |||
XkbFreeKeyboard(desc, XkbAllComponentsMask, True); | |||
} | |||
#ifdef HAVE_XTEST | |||
int xtestEventBase; | |||
int xtestErrorBase; | |||
if (XTestQueryExtension(dpy, &xtestEventBase, | |||
&xtestErrorBase, &major, &minor)) { | |||
XTestGrabControl(dpy, True); | |||
vlog.info("XTest extension present - version %d.%d",major,minor); | |||
haveXtest = true; | |||
} else { | |||
#endif | |||
vlog.info("XTest extension not present"); | |||
vlog.info("Unable to inject events or display while server is grabbed"); | |||
#ifdef HAVE_XTEST | |||
} | |||
#endif | |||
#ifdef HAVE_XDAMAGE | |||
int xdamageErrorBase; | |||
if (XDamageQueryExtension(dpy, &xdamageEventBase, &xdamageErrorBase)) { | |||
haveDamage = true; | |||
} else { | |||
#endif | |||
vlog.info("DAMAGE extension not present"); | |||
vlog.info("Will have to poll screen for changes"); | |||
#ifdef HAVE_XDAMAGE | |||
} | |||
#endif | |||
#ifdef HAVE_XFIXES | |||
int xfixesErrorBase; | |||
if (XFixesQueryExtension(dpy, &xfixesEventBase, &xfixesErrorBase)) { | |||
XFixesSelectCursorInput(dpy, DefaultRootWindow(dpy), | |||
XFixesDisplayCursorNotifyMask); | |||
} else { | |||
#endif | |||
vlog.info("XFIXES extension not present"); | |||
vlog.info("Will not be able to display cursors"); | |||
#ifdef HAVE_XFIXES | |||
} | |||
#endif | |||
TXWindow::setGlobalEventHandler(this); | |||
} | |||
XDesktop::~XDesktop() { | |||
stop(); | |||
} | |||
void XDesktop::poll() { | |||
if (pb and not haveDamage) | |||
pb->poll(server); | |||
if (running) { | |||
Window root, child; | |||
int x, y, wx, wy; | |||
unsigned int mask; | |||
XQueryPointer(dpy, DefaultRootWindow(dpy), &root, &child, | |||
&x, &y, &wx, &wy, &mask); | |||
server->setCursorPos(rfb::Point(x, y)); | |||
} | |||
} | |||
void XDesktop::start(VNCServer* vs) { | |||
// Determine actual number of buttons of the X pointer device. | |||
unsigned char btnMap[8]; | |||
int numButtons = XGetPointerMapping(dpy, btnMap, 8); | |||
maxButtons = (numButtons > 8) ? 8 : numButtons; | |||
vlog.info("Enabling %d button%s of X pointer device", | |||
maxButtons, (maxButtons != 1) ? "s" : ""); | |||
// Create an ImageFactory instance for producing Image objects. | |||
ImageFactory factory((bool)useShm); | |||
// Create pixel buffer and provide it to the server object. | |||
pb = new XPixelBuffer(dpy, factory, geometry->getRect()); | |||
vlog.info("Allocated %s", pb->getImage()->classDesc()); | |||
server = (VNCServerST *)vs; | |||
server->setPixelBuffer(pb); | |||
#ifdef HAVE_XDAMAGE | |||
if (haveDamage) { | |||
damage = XDamageCreate(dpy, DefaultRootWindow(dpy), | |||
XDamageReportRawRectangles); | |||
} | |||
#endif | |||
#ifdef HAVE_XFIXES | |||
setCursor(); | |||
#endif | |||
server->setLEDState(ledState); | |||
running = true; | |||
} | |||
void XDesktop::stop() { | |||
running = false; | |||
#ifdef HAVE_XDAMAGE | |||
if (haveDamage) | |||
XDamageDestroy(dpy, damage); | |||
#endif | |||
delete pb; | |||
pb = 0; | |||
} | |||
bool XDesktop::isRunning() { | |||
return running; | |||
} | |||
void XDesktop::pointerEvent(const Point& pos, int buttonMask) { | |||
#ifdef HAVE_XTEST | |||
if (!haveXtest) return; | |||
XTestFakeMotionEvent(dpy, DefaultScreen(dpy), | |||
geometry->offsetLeft() + pos.x, | |||
geometry->offsetTop() + pos.y, | |||
CurrentTime); | |||
if (buttonMask != oldButtonMask) { | |||
for (int i = 0; i < maxButtons; i++) { | |||
if ((buttonMask ^ oldButtonMask) & (1<<i)) { | |||
if (buttonMask & (1<<i)) { | |||
XTestFakeButtonEvent(dpy, i+1, True, CurrentTime); | |||
} else { | |||
XTestFakeButtonEvent(dpy, i+1, False, CurrentTime); | |||
} | |||
} | |||
} | |||
} | |||
oldButtonMask = buttonMask; | |||
#endif | |||
} | |||
#ifdef HAVE_XTEST | |||
KeyCode XDesktop::XkbKeysymToKeycode(Display* dpy, KeySym keysym) { | |||
XkbDescPtr xkb; | |||
XkbStateRec state; | |||
unsigned keycode; | |||
xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd); | |||
if (!xkb) | |||
return 0; | |||
XkbGetState(dpy, XkbUseCoreKbd, &state); | |||
for (keycode = xkb->min_key_code; | |||
keycode <= xkb->max_key_code; | |||
keycode++) { | |||
KeySym cursym; | |||
unsigned int mods, out_mods; | |||
// XkbStateFieldFromRec() doesn't work properly because | |||
// state.lookup_mods isn't properly updated, so we do this manually | |||
mods = XkbBuildCoreState(XkbStateMods(&state), state.group); | |||
XkbTranslateKeyCode(xkb, keycode, mods, &out_mods, &cursym); | |||
if (cursym == keysym) | |||
break; | |||
} | |||
if (keycode > xkb->max_key_code) | |||
keycode = 0; | |||
XkbFreeKeyboard(xkb, XkbAllComponentsMask, True); | |||
return keycode; | |||
} | |||
#endif | |||
void XDesktop::keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) { | |||
#ifdef HAVE_XTEST | |||
int keycode = 0; | |||
if (!haveXtest) | |||
return; | |||
// Use scan code if provided and mapping exists | |||
if (codeMap && rawKeyboard && xtcode < codeMapLen) | |||
keycode = codeMap[xtcode]; | |||
if (!keycode) { | |||
if (pressedKeys.find(keysym) != pressedKeys.end()) | |||
keycode = pressedKeys[keysym]; | |||
else { | |||
// XKeysymToKeycode() doesn't respect state, so we have to use | |||
// something slightly more complex | |||
keycode = XkbKeysymToKeycode(dpy, keysym); | |||
} | |||
} | |||
if (!keycode) | |||
return; | |||
if (down) | |||
pressedKeys[keysym] = keycode; | |||
else | |||
pressedKeys.erase(keysym); | |||
XTestFakeKeyEvent(dpy, keycode, down, CurrentTime); | |||
#endif | |||
} | |||
void XDesktop::clientCutText(const char* str, int len) { | |||
} | |||
bool XDesktop::handleGlobalEvent(XEvent* ev) { | |||
if (ev->type == xkbEventBase + XkbEventCode) { | |||
XkbEvent *kb = (XkbEvent *)ev; | |||
if (kb->any.xkb_type != XkbIndicatorStateNotify) | |||
return false; | |||
vlog.debug("Got indicator update, mask is now 0x%x", kb->indicators.state); | |||
ledState = 0; | |||
for (int i = 0; i < XDESKTOP_N_LEDS; i++) { | |||
if (kb->indicators.state & ledMasks[i]) | |||
ledState |= 1u << i; | |||
} | |||
if (running) | |||
server->setLEDState(ledState); | |||
return true; | |||
#ifdef HAVE_XDAMAGE | |||
} else if (ev->type == xdamageEventBase) { | |||
XDamageNotifyEvent* dev; | |||
Rect rect; | |||
if (!running) | |||
return true; | |||
dev = (XDamageNotifyEvent*)ev; | |||
rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height); | |||
server->add_changed(rect); | |||
return true; | |||
#endif | |||
#ifdef HAVE_XFIXES | |||
} else if (ev->type == xfixesEventBase + XFixesCursorNotify) { | |||
XFixesCursorNotifyEvent* cev; | |||
if (!running) | |||
return true; | |||
cev = (XFixesCursorNotifyEvent*)ev; | |||
if (cev->subtype != XFixesDisplayCursorNotify) | |||
return false; | |||
return setCursor(); | |||
#endif | |||
} | |||
return false; | |||
} | |||
bool XDesktop::setCursor() | |||
{ | |||
XFixesCursorImage *cim; | |||
cim = XFixesGetCursorImage(dpy); | |||
if (cim == NULL) | |||
return false; | |||
// Copied from XserverDesktop::setCursor() in | |||
// unix/xserver/hw/vnc/XserverDesktop.cc and adapted to | |||
// handle long -> U32 conversion for 64-bit Xlib | |||
rdr::U8* cursorData; | |||
rdr::U8 *out; | |||
const unsigned long *pixels; | |||
cursorData = new rdr::U8[cim->width * cim->height * 4]; | |||
// Un-premultiply alpha | |||
pixels = cim->pixels; | |||
out = cursorData; | |||
for (int y = 0; y < cim->height; y++) { | |||
for (int x = 0; x < cim->width; x++) { | |||
rdr::U8 alpha; | |||
rdr::U32 pixel = *pixels++; | |||
rdr::U8 *in = (rdr::U8 *) &pixel; | |||
alpha = in[3]; | |||
if (alpha == 0) | |||
alpha = 1; // Avoid division by zero | |||
*out++ = (unsigned)*in++ * 255/alpha; | |||
*out++ = (unsigned)*in++ * 255/alpha; | |||
*out++ = (unsigned)*in++ * 255/alpha; | |||
*out++ = *in++; | |||
} | |||
} | |||
try { | |||
server->setCursor(cim->width, cim->height, Point(cim->xhot, cim->yhot), | |||
cursorData); | |||
} catch (rdr::Exception& e) { | |||
vlog.error("XserverDesktop::setCursor: %s",e.str()); | |||
} | |||
delete [] cursorData; | |||
XFree(cim); | |||
return true; | |||
} |
@@ -0,0 +1,81 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* Copyright (C) 2004-2008 Constantin Kaplinsky. All Rights Reserved. | |||
* Copyright 2017 Peter Astrand <astrand@cendio.se> 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 __XDESKTOP_H__ | |||
#define __XDESKTOP_H__ | |||
#include <rfb/VNCServerST.h> | |||
#include <tx/TXWindow.h> | |||
#include <X11/XKBlib.h> | |||
#ifdef HAVE_XDAMAGE | |||
#include <X11/extensions/Xdamage.h> | |||
#endif | |||
class Geometry; | |||
class XPixelBuffer; | |||
// number of XKb indicator leds to handle | |||
#define XDESKTOP_N_LEDS 3 | |||
class XDesktop : public rfb::SDesktop, public TXGlobalEventHandler | |||
{ | |||
public: | |||
XDesktop(Display* dpy_, Geometry *geometry); | |||
virtual ~XDesktop(); | |||
void poll(); | |||
// -=- SDesktop interface | |||
virtual void start(rfb::VNCServer* vs); | |||
virtual void stop(); | |||
bool isRunning(); | |||
virtual void pointerEvent(const rfb::Point& pos, int buttonMask); | |||
KeyCode XkbKeysymToKeycode(Display* dpy, KeySym keysym); | |||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down); | |||
virtual void clientCutText(const char* str, int len); | |||
// -=- TXGlobalEventHandler interface | |||
virtual bool handleGlobalEvent(XEvent* ev); | |||
protected: | |||
Display* dpy; | |||
Geometry* geometry; | |||
XPixelBuffer* pb; | |||
rfb::VNCServerST* server; | |||
int oldButtonMask; | |||
bool haveXtest; | |||
bool haveDamage; | |||
int maxButtons; | |||
std::map<KeySym, KeyCode> pressedKeys; | |||
bool running; | |||
#ifdef HAVE_XDAMAGE | |||
Damage damage; | |||
int xdamageEventBase; | |||
#endif | |||
int xkbEventBase; | |||
#ifdef HAVE_XFIXES | |||
int xfixesEventBase; | |||
#endif | |||
int ledMasks[XDESKTOP_N_LEDS]; | |||
unsigned ledState; | |||
const unsigned short *codeMap; | |||
unsigned codeMapLen; | |||
bool setCursor(); | |||
}; | |||
#endif // __XDESKTOP_H__ |
@@ -28,23 +28,21 @@ | |||
#include <x0vncserver/Image.h> | |||
#include <x0vncserver/PollingManager.h> | |||
using namespace rfb; | |||
// | |||
// XPixelBuffer is an Image-based implementation of FullFramePixelBuffer. | |||
// | |||
class XPixelBuffer : public FullFramePixelBuffer | |||
class XPixelBuffer : public rfb::FullFramePixelBuffer | |||
{ | |||
public: | |||
XPixelBuffer(Display *dpy, ImageFactory &factory, const Rect &rect); | |||
XPixelBuffer(Display *dpy, ImageFactory &factory, const rfb::Rect &rect); | |||
virtual ~XPixelBuffer(); | |||
// Provide access to the underlying Image object. | |||
const Image *getImage() const { return m_image; } | |||
// Detect changed pixels, notify the server. | |||
inline void poll(VNCServer *server) { m_poller->poll(server); } | |||
inline void poll(rfb::VNCServer *server) { m_poller->poll(server); } | |||
// Override PixelBuffer::grabRegion(). | |||
virtual void grabRegion(const rfb::Region& region); | |||
@@ -59,7 +57,7 @@ protected: | |||
// Copy pixels from the screen to the pixel buffer, | |||
// for the specified rectangular area of the buffer. | |||
inline void grabRect(const Rect &r) { | |||
inline void grabRect(const rfb::Rect &r) { | |||
m_image->get(DefaultRootWindow(m_dpy), | |||
m_offsetLeft + r.tl.x, m_offsetTop + r.tl.y, | |||
r.width(), r.height(), r.tl.x, r.tl.y); |
@@ -31,7 +31,6 @@ | |||
#include <rfb/Configuration.h> | |||
#include <rfb/Timer.h> | |||
#include <network/TcpSocket.h> | |||
#include <tx/TXWindow.h> | |||
#include <vncconfig/QueryConnectDialog.h> | |||
@@ -39,46 +38,17 @@ | |||
#include <X11/X.h> | |||
#include <X11/Xlib.h> | |||
#include <X11/Xutil.h> | |||
#include <X11/XKBlib.h> | |||
#ifdef HAVE_XTEST | |||
#include <X11/extensions/XTest.h> | |||
#endif | |||
#ifdef HAVE_XDAMAGE | |||
#include <X11/extensions/Xdamage.h> | |||
#endif | |||
#ifdef HAVE_XFIXES | |||
#include <X11/extensions/Xfixes.h> | |||
#endif | |||
#include <x0vncserver/XDesktop.h> | |||
#include <x0vncserver/Geometry.h> | |||
#include <x0vncserver/Image.h> | |||
#include <x0vncserver/XPixelBuffer.h> | |||
#include <x0vncserver/PollingScheduler.h> | |||
extern const unsigned short code_map_qnum_to_xorgevdev[]; | |||
extern const unsigned int code_map_qnum_to_xorgevdev_len; | |||
extern const unsigned short code_map_qnum_to_xorgkbd[]; | |||
extern const unsigned int code_map_qnum_to_xorgkbd_len; | |||
// XXX Lynx/OS 2.3: protos for select(), bzero() | |||
#ifdef Lynx | |||
#include <sys/proto.h> | |||
#endif | |||
extern char buildtime[]; | |||
using namespace rfb; | |||
using namespace network; | |||
// number of XKb indicator leds to handle | |||
static const int N_LEDS = 3; | |||
// order is important as it must match RFB extension | |||
static const char * ledNames[N_LEDS] = { | |||
"Scroll Lock", "Num Lock", "Caps Lock" | |||
}; | |||
static LogWriter vlog("Main"); | |||
IntParameter pollingCycle("PollingCycle", "Milliseconds per one polling " | |||
@@ -87,8 +57,6 @@ IntParameter pollingCycle("PollingCycle", "Milliseconds per one polling " | |||
IntParameter maxProcessorUsage("MaxProcessorUsage", "Maximum percentage of " | |||
"CPU time to be consumed", 35); | |||
BoolParameter useShm("UseSHM", "Use MIT-SHM extension if available", true); | |||
BoolParameter useOverlay("OverlayMode", "Use overlay mode under " | |||
"IRIX or Solaris", true); | |||
StringParameter displayname("display", "The X display", ""); | |||
IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900); | |||
IntParameter queryConnectTimeout("QueryConnectTimeout", | |||
@@ -157,399 +125,6 @@ private: | |||
}; | |||
class XDesktop : public SDesktop, public TXGlobalEventHandler | |||
{ | |||
public: | |||
XDesktop(Display* dpy_, Geometry *geometry_) | |||
: dpy(dpy_), geometry(geometry_), pb(0), server(0), | |||
oldButtonMask(0), haveXtest(false), haveDamage(false), | |||
maxButtons(0), running(false), ledMasks(), ledState(0), | |||
codeMap(0), codeMapLen(0) | |||
{ | |||
int major, minor; | |||
int xkbOpcode, xkbErrorBase; | |||
major = XkbMajorVersion; | |||
minor = XkbMinorVersion; | |||
if (!XkbQueryExtension(dpy, &xkbOpcode, &xkbEventBase, | |||
&xkbErrorBase, &major, &minor)) { | |||
vlog.error("XKEYBOARD extension not present"); | |||
throw Exception(); | |||
} | |||
XkbSelectEvents(dpy, XkbUseCoreKbd, XkbIndicatorStateNotifyMask, | |||
XkbIndicatorStateNotifyMask); | |||
// figure out bit masks for the indicators we are interested in | |||
for (int i = 0; i < N_LEDS; i++) { | |||
Atom a; | |||
int shift; | |||
Bool on; | |||
a = XInternAtom(dpy, ledNames[i], True); | |||
if (!a || !XkbGetNamedIndicator(dpy, a, &shift, &on, NULL, NULL)) | |||
continue; | |||
ledMasks[i] = 1u << shift; | |||
vlog.debug("Mask for '%s' is 0x%x", ledNames[i], ledMasks[i]); | |||
if (on) | |||
ledState |= 1u << i; | |||
} | |||
// X11 unfortunately uses keyboard driver specific keycodes and provides no | |||
// direct way to query this, so guess based on the keyboard mapping | |||
XkbDescPtr desc = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd); | |||
if (desc && desc->names) { | |||
char *keycodes = XGetAtomName(dpy, desc->names->keycodes); | |||
if (keycodes) { | |||
if (strncmp("evdev", keycodes, strlen("evdev")) == 0) { | |||
codeMap = code_map_qnum_to_xorgevdev; | |||
codeMapLen = code_map_qnum_to_xorgevdev_len; | |||
vlog.info("Using evdev codemap\n"); | |||
} else if (strncmp("xfree86", keycodes, strlen("xfree86")) == 0) { | |||
codeMap = code_map_qnum_to_xorgkbd; | |||
codeMapLen = code_map_qnum_to_xorgkbd_len; | |||
vlog.info("Using xorgkbd codemap\n"); | |||
} else { | |||
vlog.info("Unknown keycode '%s', no codemap\n", keycodes); | |||
} | |||
XFree(keycodes); | |||
} else { | |||
vlog.debug("Unable to get keycode map\n"); | |||
} | |||
XkbFreeKeyboard(desc, XkbAllComponentsMask, True); | |||
} | |||
#ifdef HAVE_XTEST | |||
int xtestEventBase; | |||
int xtestErrorBase; | |||
if (XTestQueryExtension(dpy, &xtestEventBase, | |||
&xtestErrorBase, &major, &minor)) { | |||
XTestGrabControl(dpy, True); | |||
vlog.info("XTest extension present - version %d.%d",major,minor); | |||
haveXtest = true; | |||
} else { | |||
#endif | |||
vlog.info("XTest extension not present"); | |||
vlog.info("Unable to inject events or display while server is grabbed"); | |||
#ifdef HAVE_XTEST | |||
} | |||
#endif | |||
#ifdef HAVE_XDAMAGE | |||
int xdamageErrorBase; | |||
if (XDamageQueryExtension(dpy, &xdamageEventBase, &xdamageErrorBase)) { | |||
haveDamage = true; | |||
} else { | |||
#endif | |||
vlog.info("DAMAGE extension not present"); | |||
vlog.info("Will have to poll screen for changes"); | |||
#ifdef HAVE_XDAMAGE | |||
} | |||
#endif | |||
#ifdef HAVE_XFIXES | |||
int xfixesErrorBase; | |||
if (XFixesQueryExtension(dpy, &xfixesEventBase, &xfixesErrorBase)) { | |||
XFixesSelectCursorInput(dpy, DefaultRootWindow(dpy), | |||
XFixesDisplayCursorNotifyMask); | |||
} else { | |||
#endif | |||
vlog.info("XFIXES extension not present"); | |||
vlog.info("Will not be able to display cursors"); | |||
#ifdef HAVE_XFIXES | |||
} | |||
#endif | |||
TXWindow::setGlobalEventHandler(this); | |||
} | |||
virtual ~XDesktop() { | |||
stop(); | |||
} | |||
inline void poll() { | |||
if (pb and not haveDamage) | |||
pb->poll(server); | |||
if (running) { | |||
Window root, child; | |||
int x, y, wx, wy; | |||
unsigned int mask; | |||
XQueryPointer(dpy, DefaultRootWindow(dpy), &root, &child, | |||
&x, &y, &wx, &wy, &mask); | |||
server->setCursorPos(rfb::Point(x, y)); | |||
} | |||
} | |||
// -=- SDesktop interface | |||
virtual void start(VNCServer* vs) { | |||
// Determine actual number of buttons of the X pointer device. | |||
unsigned char btnMap[8]; | |||
int numButtons = XGetPointerMapping(dpy, btnMap, 8); | |||
maxButtons = (numButtons > 8) ? 8 : numButtons; | |||
vlog.info("Enabling %d button%s of X pointer device", | |||
maxButtons, (maxButtons != 1) ? "s" : ""); | |||
// Create an ImageFactory instance for producing Image objects. | |||
ImageFactory factory((bool)useShm, (bool)useOverlay); | |||
// Create pixel buffer and provide it to the server object. | |||
pb = new XPixelBuffer(dpy, factory, geometry->getRect()); | |||
vlog.info("Allocated %s", pb->getImage()->classDesc()); | |||
server = (VNCServerST *)vs; | |||
server->setPixelBuffer(pb); | |||
#ifdef HAVE_XDAMAGE | |||
if (haveDamage) { | |||
damage = XDamageCreate(dpy, DefaultRootWindow(dpy), | |||
XDamageReportRawRectangles); | |||
} | |||
#endif | |||
server->setLEDState(ledState); | |||
running = true; | |||
} | |||
virtual void stop() { | |||
running = false; | |||
#ifdef HAVE_XDAMAGE | |||
if (haveDamage) | |||
XDamageDestroy(dpy, damage); | |||
#endif | |||
delete pb; | |||
pb = 0; | |||
} | |||
inline bool isRunning() { | |||
return running; | |||
} | |||
virtual void pointerEvent(const Point& pos, int buttonMask) { | |||
#ifdef HAVE_XTEST | |||
if (!haveXtest) return; | |||
XTestFakeMotionEvent(dpy, DefaultScreen(dpy), | |||
geometry->offsetLeft() + pos.x, | |||
geometry->offsetTop() + pos.y, | |||
CurrentTime); | |||
if (buttonMask != oldButtonMask) { | |||
for (int i = 0; i < maxButtons; i++) { | |||
if ((buttonMask ^ oldButtonMask) & (1<<i)) { | |||
if (buttonMask & (1<<i)) { | |||
XTestFakeButtonEvent(dpy, i+1, True, CurrentTime); | |||
} else { | |||
XTestFakeButtonEvent(dpy, i+1, False, CurrentTime); | |||
} | |||
} | |||
} | |||
} | |||
oldButtonMask = buttonMask; | |||
#endif | |||
} | |||
#ifdef HAVE_XTEST | |||
KeyCode XkbKeysymToKeycode(Display* dpy, KeySym keysym) { | |||
XkbDescPtr xkb; | |||
XkbStateRec state; | |||
unsigned keycode; | |||
xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd); | |||
if (!xkb) | |||
return 0; | |||
XkbGetState(dpy, XkbUseCoreKbd, &state); | |||
for (keycode = xkb->min_key_code; | |||
keycode <= xkb->max_key_code; | |||
keycode++) { | |||
KeySym cursym; | |||
unsigned int mods, out_mods; | |||
// XkbStateFieldFromRec() doesn't work properly because | |||
// state.lookup_mods isn't properly updated, so we do this manually | |||
mods = XkbBuildCoreState(XkbStateMods(&state), state.group); | |||
XkbTranslateKeyCode(xkb, keycode, mods, &out_mods, &cursym); | |||
if (cursym == keysym) | |||
break; | |||
} | |||
if (keycode > xkb->max_key_code) | |||
keycode = 0; | |||
XkbFreeKeyboard(xkb, XkbAllComponentsMask, True); | |||
return keycode; | |||
} | |||
#endif | |||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) { | |||
#ifdef HAVE_XTEST | |||
int keycode = 0; | |||
if (!haveXtest) | |||
return; | |||
// Use scan code if provided and mapping exists | |||
if (codeMap && rawKeyboard && xtcode < codeMapLen) | |||
keycode = codeMap[xtcode]; | |||
if (!keycode) { | |||
if (pressedKeys.find(keysym) != pressedKeys.end()) | |||
keycode = pressedKeys[keysym]; | |||
else { | |||
// XKeysymToKeycode() doesn't respect state, so we have to use | |||
// something slightly more complex | |||
keycode = XkbKeysymToKeycode(dpy, keysym); | |||
} | |||
} | |||
if (!keycode) | |||
return; | |||
if (down) | |||
pressedKeys[keysym] = keycode; | |||
else | |||
pressedKeys.erase(keysym); | |||
XTestFakeKeyEvent(dpy, keycode, down, CurrentTime); | |||
#endif | |||
} | |||
virtual void clientCutText(const char* str, int len) { | |||
} | |||
// -=- TXGlobalEventHandler interface | |||
virtual bool handleGlobalEvent(XEvent* ev) { | |||
if (ev->type == xkbEventBase + XkbEventCode) { | |||
XkbEvent *kb = (XkbEvent *)ev; | |||
if (kb->any.xkb_type != XkbIndicatorStateNotify) | |||
return false; | |||
vlog.debug("Got indicator update, mask is now 0x%x", kb->indicators.state); | |||
ledState = 0; | |||
for (int i = 0; i < N_LEDS; i++) { | |||
if (kb->indicators.state & ledMasks[i]) | |||
ledState |= 1u << i; | |||
} | |||
if (running) | |||
server->setLEDState(ledState); | |||
return true; | |||
#ifdef HAVE_XDAMAGE | |||
} else if (ev->type == xdamageEventBase) { | |||
XDamageNotifyEvent* dev; | |||
Rect rect; | |||
if (!running) | |||
return true; | |||
dev = (XDamageNotifyEvent*)ev; | |||
rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height); | |||
server->add_changed(rect); | |||
return true; | |||
#endif | |||
#ifdef HAVE_XFIXES | |||
} else if (ev->type == xfixesEventBase + XFixesCursorNotify) { | |||
XFixesCursorNotifyEvent* cev; | |||
XFixesCursorImage *cim; | |||
if (!running) | |||
return true; | |||
cev = (XFixesCursorNotifyEvent*)ev; | |||
if (cev->subtype != XFixesDisplayCursorNotify) | |||
return false; | |||
cim = XFixesGetCursorImage(dpy); | |||
if (cim == NULL) | |||
return false; | |||
// Copied from XserverDesktop::setCursor() in | |||
// unix/xserver/hw/vnc/XserverDesktop.cc and adapted to | |||
// handle long -> U32 conversion for 64-bit Xlib | |||
rdr::U8* cursorData; | |||
rdr::U8 *out; | |||
const unsigned long *pixels; | |||
cursorData = new rdr::U8[cim->width * cim->height * 4]; | |||
// Un-premultiply alpha | |||
pixels = cim->pixels; | |||
out = cursorData; | |||
for (int y = 0; y < cim->height; y++) { | |||
for (int x = 0; x < cim->width; x++) { | |||
rdr::U8 alpha; | |||
rdr::U32 pixel = *pixels++; | |||
rdr::U8 *in = (rdr::U8 *) &pixel; | |||
alpha = in[3]; | |||
if (alpha == 0) | |||
alpha = 1; // Avoid division by zero | |||
*out++ = (unsigned)*in++ * 255/alpha; | |||
*out++ = (unsigned)*in++ * 255/alpha; | |||
*out++ = (unsigned)*in++ * 255/alpha; | |||
*out++ = *in++; | |||
} | |||
} | |||
try { | |||
server->setCursor(cim->width, cim->height, Point(cim->xhot, cim->yhot), | |||
cursorData); | |||
} catch (rdr::Exception& e) { | |||
vlog.error("XserverDesktop::setCursor: %s",e.str()); | |||
} | |||
delete [] cursorData; | |||
XFree(cim); | |||
return true; | |||
#endif | |||
} | |||
return false; | |||
} | |||
protected: | |||
Display* dpy; | |||
Geometry* geometry; | |||
XPixelBuffer* pb; | |||
VNCServerST* server; | |||
int oldButtonMask; | |||
bool haveXtest; | |||
bool haveDamage; | |||
int maxButtons; | |||
std::map<KeySym, KeyCode> pressedKeys; | |||
bool running; | |||
#ifdef HAVE_XDAMAGE | |||
Damage damage; | |||
int xdamageEventBase; | |||
#endif | |||
int xkbEventBase; | |||
#ifdef HAVE_XFIXES | |||
int xfixesEventBase; | |||
#endif | |||
int ledMasks[N_LEDS]; | |||
unsigned ledState; | |||
const unsigned short *codeMap; | |||
unsigned codeMapLen; | |||
}; | |||
class FileTcpFilter : public TcpFilter | |||
{ | |||
@@ -683,6 +258,9 @@ int main(int argc, char** argv) | |||
Configuration::enableServerParams(); | |||
// Disable configuration parameters which we do not support | |||
Configuration::removeParam("AcceptSetDesktopSize"); | |||
for (int i = 1; i < argc; i++) { | |||
if (Configuration::setParam(argv[i])) | |||
continue; |
@@ -227,13 +227,6 @@ Use MIT-SHM extension if available. Using that extension accelerates reading | |||
the screen. Default is on. | |||
. | |||
.TP | |||
.B OverlayMode | |||
Use overlay mode in IRIX or Solaris (does not have effect in other systems). | |||
This enables system-specific access to complete full-color version of the | |||
screen (the default X visual often provides 256 colors). Also, in overlay | |||
mode, \fBx0vncserver\fP can show correct mouse cursor. Default is on. | |||
. | |||
.TP | |||
.B ZlibLevel | |||
Zlib compression level for ZRLE encoding (it does not affect Tight encoding). | |||
Acceptable values are between 0 and 9. Default is to use the standard |
@@ -78,7 +78,7 @@ static void programUsage() { | |||
printf("\nLog destinations:\n"); | |||
Logger::listLoggers(); | |||
printf("\nAvailable configuration parameters:\n"); | |||
Configuration::listParams(ConfServer); | |||
Configuration::listParams(79, 14); | |||
} | |||
static void MsgBoxOrLog(const char* msg, bool isError=false) { |