diff options
Diffstat (limited to 'unix/xc/programs')
-rw-r--r-- | unix/xc/programs/Xserver/Xvnc.man | 284 | ||||
-rw-r--r-- | unix/xc/programs/Xserver/vnc/Imakefile | 44 | ||||
-rw-r--r-- | unix/xc/programs/Xserver/vnc/RegionHelper.h | 84 | ||||
-rw-r--r-- | unix/xc/programs/Xserver/vnc/XserverDesktop.cc | 1147 | ||||
-rw-r--r-- | unix/xc/programs/Xserver/vnc/XserverDesktop.h | 130 | ||||
-rw-r--r-- | unix/xc/programs/Xserver/vnc/Xvnc/Imakefile | 95 | ||||
-rw-r--r-- | unix/xc/programs/Xserver/vnc/Xvnc/buildtime.c | 18 | ||||
-rw-r--r-- | unix/xc/programs/Xserver/vnc/Xvnc/xvnc.cc | 1475 | ||||
-rw-r--r-- | unix/xc/programs/Xserver/vnc/module/Imakefile | 60 | ||||
-rw-r--r-- | unix/xc/programs/Xserver/vnc/vncExtInit.cc | 866 | ||||
-rw-r--r-- | unix/xc/programs/Xserver/vnc/vncExtInit.h | 32 | ||||
-rw-r--r-- | unix/xc/programs/Xserver/vnc/vncHooks.cc | 1533 | ||||
-rw-r--r-- | unix/xc/programs/Xserver/vnc/vncHooks.h | 26 | ||||
-rw-r--r-- | unix/xc/programs/Xserver/vnc/xf86vncModule.cc | 97 |
14 files changed, 5891 insertions, 0 deletions
diff --git a/unix/xc/programs/Xserver/Xvnc.man b/unix/xc/programs/Xserver/Xvnc.man new file mode 100644 index 00000000..128bc937 --- /dev/null +++ b/unix/xc/programs/Xserver/Xvnc.man @@ -0,0 +1,284 @@ +.TH Xvnc 1 "17 Apr 2006" "TightVNC" "Virtual Network Computing" +.SH NAME +Xvnc \- the X VNC server +.SH SYNOPSIS +.B Xvnc +.RI [ options ] +.RI : display# +.SH DESCRIPTION +.B Xvnc +is the X VNC (Virtual Network Computing) server. It is based on a standard X +server, but it has a "virtual" screen rather than a physical one. X +applications display themselves on it as if it were a normal X display, but +they can only be accessed via a VNC viewer - see \fBvncviewer\fP(1). + +So Xvnc is really two servers in one. To the applications it is an X server, +and to the remote VNC users it is a VNC server. By convention we have arranged +that the VNC server display number will be the same as the X server display +number, which means you can use eg. snoopy:2 to refer to display 2 on machine +"snoopy" in both the X world and the VNC world. + +The best way of starting \fBXvnc\fP is via the \fBvncserver\fP script. This +sets up the environment appropriately and runs some X applications to get you +going. See the manual page for \fBvncserver\fP(1) for more information. + +.SH OPTIONS +.B Xvnc +takes lots of options - running \fBXvnc -help\fP gives a list. Many of these +are standard X server options, which are described in the \fBXserver\fP(1) +manual page. In addition to options which can only be set via the +command-line, there are also "parameters" which can be set both via the +command-line and through the \fBvncconfig\fP(1) program. + +.TP +.B \-geometry \fIwidth\fPx\fIheight\fP +Specify the size of the desktop to be created. Default is 1024x768. + +.TP +.B \-depth \fIdepth\fP +Specify the pixel depth in bits of the desktop to be created. Default is 16, +other possible values are 8, 15, and 24 - anything else is likely to cause +strange behaviour by applications. + +.TP +.B \-pixelformat \fIformat\fP +Specify pixel format for server to use (BGRnnn or RGBnnn). The default for +depth 8 is BGR233 (meaning the most significant two bits represent blue, the +next three green, and the least significant three represent red), the default +for depth 16 is RGB565 and for depth 24 is RGB888. + +.TP +.B \-cc 3 +As an alternative to the default TrueColor visual, this allows you to run an +Xvnc server with a PseudoColor visual (i.e. one which uses a color map or +palette), which can be useful for running some old X applications which only +work on such a display. Values other than 3 (PseudoColor) and 4 (TrueColor) +for the \-cc option may result in strange behaviour, and PseudoColor desktops +must be 8 bits deep (i.e. \fB-depth 8\fP). + +.TP +.B \-inetd +This significantly changes Xvnc's behaviour so that it can be launched from +inetd. See the section below on usage with inetd. + +.TP +.B \-help +List all the options and parameters + +.SH PARAMETERS +VNC parameters can be set both via the command-line and through the +\fBvncconfig\fP(1) program, and with a VNC-enabled XFree86 server via Options +entries in the XF86Config file. + +Parameters can be turned on with -\fIparam\fP or off with +-\fIparam\fP=0. Parameters which take a value can be specified as +-\fIparam\fP \fIvalue\fP. Other valid forms are \fIparam\fP\fB=\fP\fIvalue\fP +-\fIparam\fP=\fIvalue\fP --\fIparam\fP=\fIvalue\fP. Parameter names are +case-insensitive. + +.TP +.B \-desktop \fIdesktop-name\fP +Each desktop has a name which may be displayed by the viewer. It defaults to +"x11". + +.TP +.B \-rfbport \fIport\fP +Specifies the TCP port on which Xvnc listens for connections from viewers (the +protocol used in VNC is called RFB - "remote framebuffer"). The default is +5900 plus the display number. + +.TP +.B \-rfbwait \fItime\fP, \-ClientWaitTimeMillis \fItime\fP + +Time in milliseconds to wait for a viewer which is blocking Xvnc. This is +necessary because Xvnc is single-threaded and sometimes blocks until the viewer +has finished sending or receiving a message - note that this does not mean an +update will be aborted after this time. Default is 20000 (20 seconds). + +.TP +.B \-httpd \fIdirectory\fP +Run a mini-HTTP server which serves files from the given directory. Normally +the directory will contain the classes for the Java viewer. In addition, files +with a .vnc extension will have certain substitutions made so that a single +installation of the Java VNC viewer can be served by separate instances of +Xvnc. + +.TP +.B \-httpPort \fIport\fP +Specifies the port on which the mini-HTTP server runs. Default is 5800 plus +the display number. + +.TP +.B \-rfbauth \fIpasswd-file\fP, \-PasswordFile \fIpasswd-file\fP +Specifies the file containing the password used to authenticate viewers. The +file is accessed each time a connection comes in, so it can be changed on the +fly via \fBvncpasswd\fP(1). + +.TP +.B \-deferUpdate \fItime\fP +Xvnc uses a "deferred update" mechanism which enhances performance in many +cases. After any change to the framebuffer, Xvnc waits for this number of +milliseconds (default 40) before sending an update to any waiting clients. This +means that more changes tend to get coalesced together in a single +update. Setting it to 0 results in the same behaviour as earlier versions of +Xvnc, where the first change to the framebuffer causes an immediate update to +any waiting clients. + +.TP +.B \-SendCutText +Send clipboard changes to clients (default is on). Note that you must also run +\fBvncconfig\fP(1) to get the clipboard to work. + +.TP +.B \-AcceptCutText +Accept clipboard updates from clients (default is on). Note that you must also +run \fBvncconfig\fP(1) to get the clipboard to work. + +.TP +.B \-AcceptPointerEvents +Accept pointer press and release events from clients (default is on). + +.TP +.B \-AcceptKeyEvents +Accept key press and release events from clients (default is on). + +.TP +.B \-DisconnectClients +Disconnect existing clients if an incoming connection is non-shared (default is +on). If \fBDisconnectClients\fP is false, then a new non-shared connection will +be refused while there is a client active. When combined with +\fBNeverShared\fP this means only one client is allowed at a time. + +.TP +.B \-NeverShared +Never treat incoming connections as shared, regardless of the client-specified +setting (default is off). + +.TP +.B \-AlwaysShared +Always treat incoming connections as shared, regardless of the client-specified +setting (default is off). + +.TP +.B \-Protocol3.3 +Always use protocol version 3.3 for backwards compatibility with badly-behaved +clients (default is off). + +.TP +.B \-CompareFB +Perform pixel comparison on framebuffer to reduce unnecessary updates (default +is on). + +.TP +.B \-SecurityTypes \fIsec-types\fP +Specify which security schemes to use separated by commas. At present only +"None" and "VncAuth" are supported. The default is "VncAuth" - note that if +you want a server which does not require a password, you must set this +parameter to "None". + +.TP +.B \-IdleTimeout \fIseconds\fP +The number of seconds after which an idle VNC connection will be dropped +(default is 0, which means that idle connections will never be dropped). + +.TP +.B \-QueryConnect +Prompts the user of the desktop to explicitly accept or reject incoming +connections. This is most useful when using the vnc.so module or +\fBx0vncserver\fP(1) program to access an existing X desktop via VNC. + +The \fBvncconfig\fP(1) program must be running on the desktop in order for +QueryConnect to be supported by the \fBvnc.so\fP(1) module or +\fBXvnc\fP(1) program. The \fBx0vncserver\fP(1) program does not require +\fBvncconfig\fP(1) to be running. + +.TP +.B \-localhost +Only allow connections from the same machine. Useful if you use SSH and want to +stop non-SSH connections from any other hosts. See the guide to using VNC with +SSH on the web site. + +.TP +.B \-log \fIlogname\fP:\fIdest\fP:\fIlevel\fP +Configures the debug log settings. \fIdest\fP can currently be \fBstderr\fP or +\fBstdout\fP, and \fIlevel\fP is between 0 and 100, 100 meaning most verbose +output. \fIlogname\fP is usually \fB*\fP meaning all, but you can target a +specific source file if you know the name of its "LogWriter". Default is +\fB*:stderr:30\fP. + +.TP +.B \-RemapKeys \fImapping +Sets up a keyboard mapping. +.I mapping +is a comma-separated string of character mappings, each of the form +.IR char -> char , +or +.IR char <> char , +where +.I char +is a hexadecimal keysym. For example, to exchange the " and @ symbols you would specify the following: +.IP "" 10 +RemapKeys=0x22<>0x40 + +.SH USAGE WITH INETD +By configuring the \fBinetd\fP(1) service appropriately, Xvnc can be launched +on demand when a connection comes in, rather than having to be started +manually. When given the \fB-inetd\fP option, instead of listening for TCP +connections on a given port it uses its standard input and standard output. +There are two modes controlled by the wait/nowait entry in the inetd.conf file. + +In the nowait mode, Xvnc uses its standard input and output directly as the +connection to a viewer. It never has a listening socket, so cannot accept +further connections from viewers (it can however connect out to listening +viewers by use of the vncconfig program). Further viewer connections to the +same TCP port result in inetd spawning off a new Xvnc to deal with each +connection. When the connection to the viewer dies, the Xvnc and any +associated X clients die. This behaviour is most useful when combined with the +XDMCP options -query and -once. An typical example in inetd.conf might be (all +on one line): + +5950 stream tcp nowait nobody /usr/local/bin/Xvnc Xvnc -inetd -query +localhost -once securitytypes=none + +In this example a viewer connection to :50 will result in a new Xvnc for that +connection which should display the standard XDM login screen on that machine. +Because the user needs to login via XDM, it is usually OK to accept connections +without a VNC password in this case. + +In the wait mode, when the first connection comes in, inetd gives the listening +socket to Xvnc. This means that for a given TCP port, there is only ever one +Xvnc at a time. Further viewer connections to the same port are accepted by +the same Xvnc in the normal way. Even when the original connection is broken, +the Xvnc will continue to run. If this is used with the XDMCP options -query +and -once, the Xvnc and associated X clients will die when the user logs out of +the X session in the normal way. It is important to use a VNC password in this +case. A typical entry in inetd.conf might be: + +5951 stream tcp wait james /usr/local/bin/Xvnc Xvnc -inetd -query localhost -once passwordFile=/home/james/.vnc/passwd + +In fact typically, you would have one entry for each user who uses VNC +regularly, each of whom has their own dedicated TCP port which they use. In +this example, when user "james" connects to :51, he enters his VNC password, +then gets the XDM login screen where he logs in in the normal way. However, +unlike the previous example, if he disconnects, the session remains persistent, +and when he reconnects he will get the same session back again. When he logs +out of the X session, the Xvnc will die, but of course a new one will be +created automatically the next time he connects. + +.SH SEE ALSO +.BR vncconfig (1), +.BR vncpasswd (1), +.BR vncserver (1), +.BR vncviewer (1), +.BR Xserver (1), +.BR inetd (1) +.br +http://www.tightvnc.com + +.SH AUTHOR +Tristan Richardson, RealVNC Ltd. + +VNC was originally developed by the RealVNC team while at Olivetti +Research Ltd / AT&T Laboratories Cambridge. TightVNC additions was +implemented by Constantin Kaplinsky. Many other people participated in +development, testing and support. diff --git a/unix/xc/programs/Xserver/vnc/Imakefile b/unix/xc/programs/Xserver/vnc/Imakefile new file mode 100644 index 00000000..1c8132d9 --- /dev/null +++ b/unix/xc/programs/Xserver/vnc/Imakefile @@ -0,0 +1,44 @@ +XCOMM CDEBUGFLAGS = -g +XCOMM CXXDEBUGFLAGS = -g + + VNCTOP = $(TOP)/.. + VNCINCLUDE = -I$(VNCTOP) -I$(VNCTOP)/vncconfig_unix + +#define CplusplusSource + +#if DoLoadableServer +#define IHaveSubdirs +#endif + +#include <Server.tmpl> + +#if DoLoadableServer + MODULE_SUBDIRS = module +#endif + SRCS = vncExtInit.cc vncHooks.cc XserverDesktop.cc + OBJS = vncExtInit.o vncHooks.o XserverDesktop.o + INCLUDES = -I../include -I$(EXTINCSRC) -I$(XINCLUDESRC) -I$(FONTINCSRC) \ + -I../render $(VNCINCLUDE) +#if defined(XFree86Version) && XFree86Version >= 4000 + VNCDEFINES = -DGC_HAS_COMPOSITE_CLIP +#endif +#if defined(ProjectX) && (ProjectX >= 604) + VNCDEFINES = -DGC_HAS_COMPOSITE_CLIP +#endif + DEFINES = $(STD_DEFINES) $(VNCDEFINES) -UXFree86LOADER + +#define IHaveSubdirs +SUBDIRS = Xvnc $(MODULE_SUBDIRS) + +NormalLibraryTarget(vnc,$(OBJS)) +LintLibraryTarget(vnc,$(SRCS)) +NormalLintTarget($(SRCS)) + +NormalLibraryObjectRule() +NormalCplusplusObjectRule() + + +MakeSubdirs($(SUBDIRS)) +DependSubdirs($(SUBDIRS)) + +DependTarget() diff --git a/unix/xc/programs/Xserver/vnc/RegionHelper.h b/unix/xc/programs/Xserver/vnc/RegionHelper.h new file mode 100644 index 00000000..61dc89ff --- /dev/null +++ b/unix/xc/programs/Xserver/vnc/RegionHelper.h @@ -0,0 +1,84 @@ +/* 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 diff --git a/unix/xc/programs/Xserver/vnc/XserverDesktop.cc b/unix/xc/programs/Xserver/vnc/XserverDesktop.cc new file mode 100644 index 00000000..9e5ea0f7 --- /dev/null +++ b/unix/xc/programs/Xserver/vnc/XserverDesktop.cc @@ -0,0 +1,1147 @@ +/* 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. + */ +// +// XserverDesktop.cxx +// + +#include <stdio.h> +#include <strings.h> +#include <unistd.h> +#include <pwd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/utsname.h> +#include <network/TcpSocket.h> +#include <rfb/Exception.h> +#include <rfb/VNCServerST.h> +#include <rfb/HTTPServer.h> +#include <rfb/LogWriter.h> +#include <rfb/Configuration.h> +#include "XserverDesktop.h" +#include "vncExtInit.h" + +extern "C" { +#define public c_public +#define class c_class + + // windowTable is in globals.h in XFree 4, but not in XFree 3 unfortunately +extern WindowPtr *WindowTable; +extern char *display; + +#include "inputstr.h" +#include "servermd.h" +#include "colormapst.h" +#include "resource.h" +#include "cursorstr.h" +#include "windowstr.h" +#define XK_CYRILLIC +#include "keysym.h" +#undef public +#undef class +} + +using namespace rfb; +using namespace network; + +static LogWriter vlog("XserverDesktop"); + +rfb::IntParameter deferUpdateTime("DeferUpdate", + "Time in milliseconds to defer updates",40); + +rfb::BoolParameter alwaysSetDeferUpdateTimer("AlwaysSetDeferUpdateTimer", + "Always reset the defer update timer on every change",false); + +IntParameter queryConnectTimeout("QueryConnectTimeout", + "Number of seconds to show the Accept Connection dialog before " + "rejecting the connection", + 10); + +static KeyCode KeysymToKeycode(KeySymsPtr keymap, KeySym ks, int* col); + +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) {} + virtual ~FileHTTPServer() {} + + virtual rdr::InStream* getFile(const char* name, const char** contentType, + int* contentLength, time_t* lastModified) + { + if (name[0] != '/' || strstr(name, "..") != 0) { + vlog.info("http request was for invalid file name"); + return 0; + } + + if (strcmp(name, "/") == 0) name = "/index.vnc"; + + CharArray httpDirStr(httpDir.getData()); + CharArray fname(strlen(httpDirStr.buf)+strlen(name)+1); + sprintf(fname.buf, "%s%s", httpDirStr.buf, name); + int fd = open(fname.buf, O_RDONLY); + if (fd < 0) return 0; + rdr::InStream* is = new rdr::FdInStream(fd, -1, 0, true); + *contentType = guessContentType(name, *contentType); + if (strlen(name) > 4 && strcasecmp(&name[strlen(name)-4], ".vnc") == 0) { + is = new rdr::SubstitutingInStream(is, desktop, 20); + *contentType = "text/html"; + } else { + struct stat st; + if (fstat(fd, &st) == 0) { + *contentLength = st.st_size; + *lastModified = st.st_mtime; + } + } + return is; + } + + XserverDesktop* desktop; +}; + + +XserverDesktop::XserverDesktop(ScreenPtr pScreen_, + network::TcpListener* listener_, + network::TcpListener* httpListener_, + const char* name, void* fbptr) + : pScreen(pScreen_), deferredUpdateTimer(0), dummyTimer(0), + server(0), httpServer(0), + listener(listener_), httpListener(httpListener_), + cmap(0), deferredUpdateTimerSet(false), + grabbing(false), ignoreHooks_(false), directFbptr(fbptr != 0), + oldButtonMask(0), + queryConnectId(0) +{ + int i; + format.depth = pScreen->rootDepth; + for (i = 0; i < screenInfo.numPixmapFormats; i++) { + if (screenInfo.formats[i].depth == format.depth) { + format.bpp = screenInfo.formats[i].bitsPerPixel; + break; + } + } + if (i == screenInfo.numPixmapFormats) { + fprintf(stderr,"no pixmap format for root depth???\n"); + abort(); + } + format.bigEndian = (screenInfo.imageByteOrder == MSBFirst); + + VisualPtr vis; + for (i = 0; i < pScreen->numVisuals; i++) { + if (pScreen->visuals[i].vid == pScreen->rootVisual) { + vis = &pScreen->visuals[i]; + break; + } + } + if (i == pScreen->numVisuals) { + fprintf(stderr,"no visual rec for root visual???\n"); + abort(); + } + format.trueColour = (vis->c_class == TrueColor); + if (!format.trueColour && format.bpp != 8) + throw rfb::Exception("X server uses unsupported visual"); + format.redShift = ffs(vis->redMask) - 1; + format.greenShift = ffs(vis->greenMask) - 1; + format.blueShift = ffs(vis->blueMask) - 1; + format.redMax = vis->redMask >> format.redShift; + format.greenMax = vis->greenMask >> format.greenShift; + format.blueMax = vis->blueMask >> format.blueShift; + + width_ = pScreen->width; + height_ = pScreen->height; + if (fbptr) + data = (rdr::U8*)fbptr; + else + data = new rdr::U8[pScreen->width * pScreen->height * (format.bpp/8)]; + colourmap = this; + + serverReset(pScreen); + + server = new VNCServerST(name, this); + server->setPixelBuffer(this); + server->setQueryConnectionHandler(this); + + if (httpListener) + httpServer = new FileHTTPServer(this); +} + +XserverDesktop::~XserverDesktop() +{ + if (!directFbptr) + delete [] data; + TimerFree(deferredUpdateTimer); + TimerFree(dummyTimer); + delete httpServer; + delete server; +} + +void XserverDesktop::serverReset(ScreenPtr pScreen_) +{ + pScreen = pScreen_; + XID* ids = new XID[pScreen->maxInstalledCmaps]; + int nmaps = (*pScreen->ListInstalledColormaps)(pScreen, ids); + cmap = (ColormapPtr)LookupIDByType(ids[0], RT_COLORMAP); + delete [] ids; +} + +char* XserverDesktop::substitute(const char* varName) +{ + if (strcmp(varName, "$$") == 0) { + return rfb::strDup("$"); + } + if (strcmp(varName, "$PORT") == 0) { + char* str = new char[10]; + sprintf(str, "%d", listener ? listener->getMyPort() : 0); + return str; + } + if (strcmp(varName, "$WIDTH") == 0) { + char* str = new char[10]; + sprintf(str, "%d", width()); + return str; + } + if (strcmp(varName, "$HEIGHT") == 0) { + char* str = new char[10]; + sprintf(str, "%d", height()); + return str; + } + if (strcmp(varName, "$APPLETWIDTH") == 0) { + char* str = new char[10]; + sprintf(str, "%d", width()); + return str; + } + if (strcmp(varName, "$APPLETHEIGHT") == 0) { + char* str = new char[10]; + sprintf(str, "%d", height() + 32); + return str; + } + if (strcmp(varName, "$DESKTOP") == 0) { + return rfb::strDup(server->getName()); + } + if (strcmp(varName, "$DISPLAY") == 0) { + struct utsname uts; + uname(&uts); + char* str = new char[256]; + strncat(str, uts.nodename, 240); + strcat(str, ":"); + strncat(str, display, 10); + return str; + } + if (strcmp(varName, "$USER") == 0) { + struct passwd* user = getpwuid(getuid()); + return rfb::strDup(user ? user->pw_name : "?"); + } + return 0; +} + +rfb::VNCServerST::queryResult +XserverDesktop::queryConnection(network::Socket* sock, + const char* userName, + char** reason) { + 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); + return rfb::VNCServerST::PENDING; +} + + +void XserverDesktop::setColormap(ColormapPtr cmap_) +{ + if (cmap != cmap_) { + cmap = cmap_; + setColourMapEntries(0, 0); + } +} + +void XserverDesktop::setColourMapEntries(ColormapPtr pColormap, int ndef, + xColorItem* pdef) +{ + if (cmap != pColormap || ndef <= 0) return; + + int first = pdef[0].pixel; + int n = 1; + + for (int i = 1; i < ndef; i++) { + if (first + n == pdef[i].pixel) { + n++; + } else { + setColourMapEntries(first, n); + first = pdef[i].pixel; + n = 1; + } + } + setColourMapEntries(first, n); +} + +void XserverDesktop::setColourMapEntries(int firstColour, int nColours) +{ + try { + server->setColourMapEntries(firstColour, nColours); + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::setColourMapEntries: %s",e.str()); + } +} + +void XserverDesktop::bell() +{ + server->bell(); +} + +void XserverDesktop::serverCutText(const char* str, int len) +{ + try { + server->serverCutText(str, len); + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::serverCutText: %s",e.str()); + } +} + +void XserverDesktop::setCursor(CursorPtr cursor) +{ + try { + int w = cursor->bits->width; + int h = cursor->bits->height; + rdr::U8* cursorData = new rdr::U8[w * h * (getPF().bpp / 8)]; + + xColorItem fg, bg; + fg.red = cursor->foreRed; + fg.green = cursor->foreGreen; + fg.blue = cursor->foreBlue; + FakeAllocColor(cmap, &fg); + bg.red = cursor->backRed; + bg.green = cursor->backGreen; + bg.blue = cursor->backBlue; + FakeAllocColor(cmap, &bg); + FakeFreeColor(cmap, fg.pixel); + FakeFreeColor(cmap, bg.pixel); + + int xMaskBytesPerRow = BitmapBytePad(w); + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int byte = y * xMaskBytesPerRow + x / 8; +#if (BITMAP_BIT_ORDER == MSBFirst) + int bit = 7 - x % 8; +#else + int bit = x % 8; +#endif + switch (getPF().bpp) { + case 8: + ((rdr::U8*)cursorData)[y * w + x] + = (cursor->bits->source[byte] & (1 << bit)) ? fg.pixel : bg.pixel; + break; + case 16: + ((rdr::U16*)cursorData)[y * w + x] + = (cursor->bits->source[byte] & (1 << bit)) ? fg.pixel : bg.pixel; + break; + case 32: + ((rdr::U32*)cursorData)[y * w + x] + = (cursor->bits->source[byte] & (1 << bit)) ? fg.pixel : bg.pixel; + break; + } + } + } + + int rfbMaskBytesPerRow = (w + 7) / 8; + + rdr::U8* cursorMask = new rdr::U8[rfbMaskBytesPerRow * h]; + + 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 + } + + server->setCursor(cursor->bits->width, cursor->bits->height, + Point(cursor->bits->xhot, cursor->bits->yhot), + cursorData, cursorMask); + server->tryUpdate(); + delete [] cursorData; + delete [] cursorMask; + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::setCursor: %s",e.str()); + } +} + +static void printRegion(RegionPtr reg) +{ + int nrects = REGION_NUM_RECTS(reg); + + fprintf(stderr,"Region num rects %2d extents %3d,%3d %3dx%3d\n",nrects, + (REGION_EXTENTS(pScreen,reg))->x1, + (REGION_EXTENTS(pScreen,reg))->y1, + (REGION_EXTENTS(pScreen,reg))->x2-(REGION_EXTENTS(pScreen,reg))->x1, + (REGION_EXTENTS(pScreen,reg))->y2-(REGION_EXTENTS(pScreen,reg))->y1); + + for (int i = 0; i < nrects; i++) { + fprintf(stderr," rect %3d,%3d %3dx%3d\n", + REGION_RECTS(reg)[i].x1, + REGION_RECTS(reg)[i].y1, + REGION_RECTS(reg)[i].x2-REGION_RECTS(reg)[i].x1, + REGION_RECTS(reg)[i].y2-REGION_RECTS(reg)[i].y1); + } +} + +CARD32 XserverDesktop::deferredUpdateTimerCallback(OsTimerPtr timer, + CARD32 now, pointer arg) +{ + XserverDesktop* desktop = (XserverDesktop*)arg; + desktop->deferredUpdateTimerSet = false; + try { + desktop->server->tryUpdate(); + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::deferredUpdateTimerCallback: %s",e.str()); + } + return 0; +} + +void XserverDesktop::deferUpdate() +{ + if (deferUpdateTime != 0) { + if (!deferredUpdateTimerSet || alwaysSetDeferUpdateTimer) { + deferredUpdateTimerSet = true; + deferredUpdateTimer = TimerSet(deferredUpdateTimer, 0, + deferUpdateTime, + deferredUpdateTimerCallback, this); + } + } else { + server->tryUpdate(); + } +} + +void XserverDesktop::add_changed(RegionPtr reg) +{ + 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); + deferUpdate(); + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::add_changed: %s",e.str()); + } +} + +void XserverDesktop::add_copied(RegionPtr dst, int dx, int dy) +{ + 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)); + deferUpdate(); + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::add_copied: %s",e.str()); + } +} + +void XserverDesktop::positionCursor() +{ + if (!cursorPos.equals(oldCursorPos)) { + oldCursorPos = cursorPos; + (*pScreen->SetCursorPosition) (pScreen, cursorPos.x, cursorPos.y, FALSE); + server->setCursorPos(cursorPos); + server->tryUpdate(); + } +} + +void XserverDesktop::blockHandler(fd_set* fds) +{ + try { + ScreenPtr screenWithCursor = GetCurrentRootWindow()->drawable.pScreen; + if (screenWithCursor == pScreen) { + int x, y; + GetSpritePosition(&x, &y); + if (x != cursorPos.x || y != cursorPos.y) { + cursorPos = oldCursorPos = Point(x, y); + server->setCursorPos(cursorPos); + server->tryUpdate(); + } + } + + if (listener) + FD_SET(listener->getFd(), fds); + if (httpListener) + FD_SET(httpListener->getFd(), fds); + + std::list<Socket*> sockets; + server->getSockets(&sockets); + std::list<Socket*>::iterator i; + for (i = sockets.begin(); i != sockets.end(); i++) { + int fd = (*i)->getFd(); + if ((*i)->isShutdown()) { + vlog.debug("client gone, sock %d",fd); + server->removeSocket(*i); + vncClientGone(fd); + delete (*i); + } else { + FD_SET(fd, fds); + } + } + if (httpServer) { + httpServer->getSockets(&sockets); + for (i = sockets.begin(); i != sockets.end(); i++) { + int fd = (*i)->getFd(); + if ((*i)->isShutdown()) { + vlog.debug("http client gone, sock %d",fd); + httpServer->removeSocket(*i); + delete (*i); + } else { + FD_SET(fd, fds); + } + } + } + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::blockHandler: %s",e.str()); + } +} + +static CARD32 dummyTimerCallback(OsTimerPtr timer, CARD32 now, pointer arg) { + return 0; +} + +void XserverDesktop::wakeupHandler(fd_set* fds, int nfds) +{ + try { + if (nfds >= 1) { + + if (listener) { + if (FD_ISSET(listener->getFd(), fds)) { + FD_CLR(listener->getFd(), fds); + Socket* sock = listener->accept(); + server->addSocket(sock); + vlog.debug("new client, sock %d",sock->getFd()); + } + } + + if (httpListener) { + if (FD_ISSET(httpListener->getFd(), fds)) { + FD_CLR(httpListener->getFd(), fds); + Socket* sock = httpListener->accept(); + httpServer->addSocket(sock); + vlog.debug("new http client, sock %d",sock->getFd()); + } + } + + std::list<Socket*> sockets; + server->getSockets(&sockets); + std::list<Socket*>::iterator i; + for (i = sockets.begin(); i != sockets.end(); i++) { + int fd = (*i)->getFd(); + if (FD_ISSET(fd, fds)) { + FD_CLR(fd, fds); + server->processSocketEvent(*i); + } + } + + if (httpServer) { + httpServer->getSockets(&sockets); + for (i = sockets.begin(); i != sockets.end(); i++) { + int fd = (*i)->getFd(); + if (FD_ISSET(fd, fds)) { + FD_CLR(fd, fds); + httpServer->processSocketEvent(*i); + } + } + } + + positionCursor(); + } + + int timeout = server->checkTimeouts(); + if (timeout > 0) { + // set a dummy timer just so we are guaranteed be called again next time. + dummyTimer = TimerSet(dummyTimer, 0, timeout, + dummyTimerCallback, 0); + } + + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::wakeupHandler: %s",e.str()); + } +} + +void XserverDesktop::addClient(Socket* sock, bool reverse) +{ + vlog.debug("new client, sock %d reverse %d",sock->getFd(),reverse); + server->addSocket(sock, reverse); +} + +void XserverDesktop::disconnectClients() +{ + vlog.debug("disconnecting all clients"); + return server->closeClients("Disconnection from server end"); +} + + +int XserverDesktop::getQueryTimeout(void* opaqueId, + const char** address, + const char** username) +{ + 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; + } + return 0; +} + +void XserverDesktop::approveConnection(void* opaqueId, bool accept, + const char* rejectMsg) +{ + if (queryConnectId == opaqueId) { + server->approveConnection((network::Socket*)opaqueId, accept, rejectMsg); + queryConnectId = 0; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// SDesktop callbacks + + +void XserverDesktop::pointerEvent(const Point& pos, int buttonMask) +{ + xEvent ev; + DevicePtr dev = LookupPointerDevice(); + + // SetCursorPosition seems to be very expensive (at least on XFree86 3.3.6 + // for S3), so we delay calling it until positionCursor() is called at the + // end of processing a load of RFB. + //(*pScreen->SetCursorPosition) (pScreen, pos.x, pos.y, FALSE); + + NewCurrentScreen(pScreen, pos.x, pos.y); + + ev.u.u.type = MotionNotify; + ev.u.u.detail = 0; + ev.u.keyButtonPointer.rootX = pos.x; + ev.u.keyButtonPointer.rootY = pos.y; + ev.u.keyButtonPointer.time = GetTimeInMillis(); + + if (!pos.equals(cursorPos)) + (*dev->processInputProc)(&ev, (DeviceIntPtr)dev, 1); + + for (int i = 0; i < 5; i++) { + if ((buttonMask ^ oldButtonMask) & (1<<i)) { + // Do not use the pointer mapping. Treat VNC buttons as logical + // buttons. + ev.u.u.detail = i + 1; + ev.u.u.type = (buttonMask & (1<<i)) ? ButtonPress : ButtonRelease; + (*dev->processInputProc)(&ev, (DeviceIntPtr)dev, 1); + } + } + + cursorPos = pos; + oldButtonMask = buttonMask; +} + +void XserverDesktop::clientCutText(const char* str, int len) +{ + vncClientCutText(str, len); +} + +void XserverDesktop::grabRegion(const rfb::Region& region) +{ + if (directFbptr) return; + if (!pScreen->GetImage) { + vlog.error("VNC error: pScreen->GetImage == 0"); + 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++) { + (*pScreen->GetImage) ((DrawablePtr)WindowTable[pScreen->myNum], + i->tl.x, y, i->width(), 1, + ZPixmap, (unsigned long)~0L, + ((char*)data + + y * bytesPerRow + i->tl.x * bytesPerPixel)); + } + } + grabbing = false; +} + +void XserverDesktop::lookup(int index, int* r, int* g, int* b) +{ + if ((cmap->c_class | DynamicClass) == DirectColor) { + VisualPtr v = cmap->pVisual; + *r = cmap->red [(index & v->redMask ) >> v->offsetRed ].co.local.red; + *g = cmap->green[(index & v->greenMask) >> v->offsetGreen].co.local.green; + *b = cmap->blue [(index & v->blueMask ) >> v->offsetBlue ].co.local.blue; + } else { + EntryPtr pent; + pent = (EntryPtr)&cmap->red[index]; + if (pent->fShared) { + *r = pent->co.shco.red->color; + *g = pent->co.shco.green->color; + *b = pent->co.shco.blue->color; + } else { + *r = pent->co.local.red; + *g = pent->co.local.green; + *b = pent->co.local.blue; + } + } +} + +// +// Keyboard handling +// + +#define IS_PRESSED(keyc, keycode) \ + ((keyc)->down[(keycode) >> 3] & (1 << ((keycode) & 7))) + +// ModifierState is a class which helps simplify generating a "fake" press +// or release of shift, ctrl, alt, etc. An instance of the class is created +// for every modifier which may need to be pressed or released. Then either +// press() or release() may be called to make sure that the corresponding keys +// are in the right state. The destructor of the class automatically reverts +// to the previous state. Each modifier may have multiple keys associated with +// it, so in the case of a fake release, this may involve releasing more than +// one key. + +class ModifierState { +public: + ModifierState(DeviceIntPtr dev_, int modIndex_) + : dev(dev_), modIndex(modIndex_), nKeys(0), keys(0), pressed(false) + { + } + ~ModifierState() { + for (int i = 0; i < nKeys; i++) + generateXKeyEvent(keys[i], !pressed); + delete [] keys; + } + void press() { + KeyClassPtr keyc = dev->key; + if (!(keyc->state & (1<<modIndex))) { + tempKeyEvent(keyc->modifierKeyMap[modIndex * keyc->maxKeysPerModifier], + true); + pressed = true; + } + } + void release() { + KeyClassPtr keyc = dev->key; + if (keyc->state & (1<<modIndex)) { + for (int k = 0; k < keyc->maxKeysPerModifier; k++) { + int keycode + = keyc->modifierKeyMap[modIndex * keyc->maxKeysPerModifier + k]; + if (keycode && IS_PRESSED(keyc, keycode)) + tempKeyEvent(keycode, false); + } + } + } +private: + void tempKeyEvent(int keycode, bool down) { + if (keycode) { + if (!keys) keys = new int[dev->key->maxKeysPerModifier]; + keys[nKeys++] = keycode; + generateXKeyEvent(keycode, down); + } + } + void generateXKeyEvent(int keycode, bool down) { + xEvent ev; + ev.u.u.type = down ? KeyPress : KeyRelease; + ev.u.u.detail = keycode; + ev.u.keyButtonPointer.time = GetTimeInMillis(); + (*dev->c_public.processInputProc)(&ev, dev, 1); + vlog.debug("fake keycode %d %s", keycode, down ? "down" : "up"); + } + DeviceIntPtr dev; + int modIndex; + int nKeys; + int* keys; + bool pressed; +}; + + +// altKeysym is a table of alternative keysyms which have the same meaning. + +struct altKeysym_t { + KeySym a, b; +}; + +altKeysym_t altKeysym[] = { + { XK_Shift_L, XK_Shift_R }, + { XK_Control_L, XK_Control_R }, + { XK_Meta_L, XK_Meta_R }, + { XK_Alt_L, XK_Alt_R }, + { XK_Super_L, XK_Super_R }, + { XK_Hyper_L, XK_Hyper_R }, + { XK_KP_Space, XK_space }, + { XK_KP_Tab, XK_Tab }, + { XK_KP_Enter, XK_Return }, + { XK_KP_F1, XK_F1 }, + { XK_KP_F2, XK_F2 }, + { XK_KP_F3, XK_F3 }, + { XK_KP_F4, XK_F4 }, + { XK_KP_Home, XK_Home }, + { XK_KP_Left, XK_Left }, + { XK_KP_Up, XK_Up }, + { XK_KP_Right, XK_Right }, + { XK_KP_Down, XK_Down }, + { XK_KP_Page_Up, XK_Page_Up }, + { XK_KP_Page_Down, XK_Page_Down }, + { XK_KP_End, XK_End }, + { XK_KP_Begin, XK_Begin }, + { XK_KP_Insert, XK_Insert }, + { XK_KP_Delete, XK_Delete }, + { XK_KP_Equal, XK_equal }, + { XK_KP_Multiply, XK_asterisk }, + { XK_KP_Add, XK_plus }, + { XK_KP_Separator, XK_comma }, + { XK_KP_Subtract, XK_minus }, + { XK_KP_Decimal, XK_period }, + { XK_KP_Divide, XK_slash }, + { XK_KP_0, XK_0 }, + { XK_KP_1, XK_1 }, + { XK_KP_2, XK_2 }, + { XK_KP_3, XK_3 }, + { XK_KP_4, XK_4 }, + { XK_KP_5, XK_5 }, + { XK_KP_6, XK_6 }, + { XK_KP_7, XK_7 }, + { XK_KP_8, XK_8 }, + { XK_KP_9, XK_9 }, +}; + +// keyEvent() - work out the best keycode corresponding to the keysym sent by +// the viewer. This is non-trivial because we can't assume much about the +// local keyboard layout. We must also find out which column of the keyboard +// mapping the keysym is in, and alter the shift state appropriately. Column 0 +// means both shift and "mode_switch" (AltGr) must be released, column 1 means +// shift must be pressed and mode_switch released, column 2 means shift must be +// released and mode_switch pressed, and column 3 means both shift and +// mode_switch must be pressed. + +void XserverDesktop::keyEvent(rdr::U32 keysym, bool down) +{ + if (keysym == XK_Caps_Lock) { + vlog.debug("Ignoring caps lock"); + return; + } + DeviceIntPtr dev = (DeviceIntPtr)LookupKeyboardDevice(); + KeyClassPtr keyc = dev->key; + KeySymsPtr keymap = &keyc->curKeySyms; + + // find which modifier Mode_switch is on. + int modeSwitchMapIndex = 0; + for (int i = 3; i < 8; i++) { + for (int k = 0; k < keyc->maxKeysPerModifier; k++) { + int keycode = keyc->modifierKeyMap[i * keyc->maxKeysPerModifier + k]; + for (int j = 0; j < keymap->mapWidth; j++) { + if (keycode != 0 && + keymap->map[(keycode - keymap->minKeyCode) + * keymap->mapWidth + j] == XK_Mode_switch) + { + modeSwitchMapIndex = i; + break; + } + } + } + } + + int col = 0; + if (keyc->state & (1<<ShiftMapIndex)) col |= 1; + if (modeSwitchMapIndex && (keyc->state & (1<<modeSwitchMapIndex))) col |= 2; + + int kc = KeysymToKeycode(keymap, keysym, &col); + + // Sort out the "shifted Tab" mess. If we are sent a shifted Tab, generate a + // local shifted Tab regardless of what the "shifted Tab" keysym is on the + // local keyboard (it might be Tab, ISO_Left_Tab or HP's private BackTab + // keysym, and quite possibly some others too). We never get ISO_Left_Tab + // here because it's already been translated in VNCSConnectionST. + if (keysym == XK_Tab && (keyc->state & (1<<ShiftMapIndex))) + col |= 1; + + if (kc == 0) { + // Not a direct match in the local keyboard mapping. Check for alternative + // keysyms with the same meaning. + for (int i = 0; i < sizeof(altKeysym) / sizeof(altKeysym_t); i++) { + if (keysym == altKeysym[i].a) + kc = KeysymToKeycode(keymap, altKeysym[i].b, &col); + else if (keysym == altKeysym[i].b) + kc = KeysymToKeycode(keymap, altKeysym[i].a, &col); + if (kc) break; + } + } + + if (kc == 0) { + // Last resort - dynamically add a new key to the keyboard mapping. + for (kc = keymap->maxKeyCode; kc >= keymap->minKeyCode; kc--) { + if (!keymap->map[(kc - keymap->minKeyCode) * keymap->mapWidth]) { + keymap->map[(kc - keymap->minKeyCode) * keymap->mapWidth] = keysym; + col = 0; + SendMappingNotify(MappingKeyboard, kc, 1, serverClient); + vlog.info("Added unknown keysym 0x%x to keycode %d",keysym,kc); + break; + } + } + if (kc < keymap->minKeyCode) { + vlog.info("Keyboard mapping full - ignoring unknown keysym 0x%x",keysym); + return; + } + } + + // See if it's a modifier key. If so, then don't do any auto-repeat, because + // the X server will translate each press into a release followed by a press. + for (int i = 0; i < 8; i++) { + for (int k = 0; k < keyc->maxKeysPerModifier; k++) { + if (kc == keyc->modifierKeyMap[i * keyc->maxKeysPerModifier + k] && + IS_PRESSED(keyc,kc) && down) + return; + } + } + + ModifierState shift(dev, ShiftMapIndex); + ModifierState modeSwitch(dev, modeSwitchMapIndex); + if (down) { + if (col & 1) + shift.press(); + else + shift.release(); + if (modeSwitchMapIndex) { + if (col & 2) + modeSwitch.press(); + else + modeSwitch.release(); + } + } + vlog.debug("keycode %d %s", kc, down ? "down" : "up"); + xEvent ev; + ev.u.u.type = down ? KeyPress : KeyRelease; + ev.u.u.detail = kc; + ev.u.keyButtonPointer.time = GetTimeInMillis(); + (*dev->c_public.processInputProc)(&ev, dev, 1); +} + + +void XConvertCase(KeySym sym, KeySym *lower, KeySym *upper) +{ + *lower = sym; + *upper = sym; + switch(sym >> 8) { + case 0: /* Latin 1 */ + if ((sym >= XK_A) && (sym <= XK_Z)) + *lower += (XK_a - XK_A); + else if ((sym >= XK_a) && (sym <= XK_z)) + *upper -= (XK_a - XK_A); + else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis)) + *lower += (XK_agrave - XK_Agrave); + else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis)) + *upper -= (XK_agrave - XK_Agrave); + else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn)) + *lower += (XK_oslash - XK_Ooblique); + else if ((sym >= XK_oslash) && (sym <= XK_thorn)) + *upper -= (XK_oslash - XK_Ooblique); + break; + case 1: /* Latin 2 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym == XK_Aogonek) + *lower = XK_aogonek; + else if (sym >= XK_Lstroke && sym <= XK_Sacute) + *lower += (XK_lstroke - XK_Lstroke); + else if (sym >= XK_Scaron && sym <= XK_Zacute) + *lower += (XK_scaron - XK_Scaron); + else if (sym >= XK_Zcaron && sym <= XK_Zabovedot) + *lower += (XK_zcaron - XK_Zcaron); + else if (sym == XK_aogonek) + *upper = XK_Aogonek; + else if (sym >= XK_lstroke && sym <= XK_sacute) + *upper -= (XK_lstroke - XK_Lstroke); + else if (sym >= XK_scaron && sym <= XK_zacute) + *upper -= (XK_scaron - XK_Scaron); + else if (sym >= XK_zcaron && sym <= XK_zabovedot) + *upper -= (XK_zcaron - XK_Zcaron); + else if (sym >= XK_Racute && sym <= XK_Tcedilla) + *lower += (XK_racute - XK_Racute); + else if (sym >= XK_racute && sym <= XK_tcedilla) + *upper -= (XK_racute - XK_Racute); + break; + case 2: /* Latin 3 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XK_Hstroke && sym <= XK_Hcircumflex) + *lower += (XK_hstroke - XK_Hstroke); + else if (sym >= XK_Gbreve && sym <= XK_Jcircumflex) + *lower += (XK_gbreve - XK_Gbreve); + else if (sym >= XK_hstroke && sym <= XK_hcircumflex) + *upper -= (XK_hstroke - XK_Hstroke); + else if (sym >= XK_gbreve && sym <= XK_jcircumflex) + *upper -= (XK_gbreve - XK_Gbreve); + else if (sym >= XK_Cabovedot && sym <= XK_Scircumflex) + *lower += (XK_cabovedot - XK_Cabovedot); + else if (sym >= XK_cabovedot && sym <= XK_scircumflex) + *upper -= (XK_cabovedot - XK_Cabovedot); + break; + case 3: /* Latin 4 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XK_Rcedilla && sym <= XK_Tslash) + *lower += (XK_rcedilla - XK_Rcedilla); + else if (sym >= XK_rcedilla && sym <= XK_tslash) + *upper -= (XK_rcedilla - XK_Rcedilla); + else if (sym == XK_ENG) + *lower = XK_eng; + else if (sym == XK_eng) + *upper = XK_ENG; + else if (sym >= XK_Amacron && sym <= XK_Umacron) + *lower += (XK_amacron - XK_Amacron); + else if (sym >= XK_amacron && sym <= XK_umacron) + *upper -= (XK_amacron - XK_Amacron); + break; + case 6: /* Cyrillic */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XK_Serbian_DJE && sym <= XK_Serbian_DZE) + *lower -= (XK_Serbian_DJE - XK_Serbian_dje); + else if (sym >= XK_Serbian_dje && sym <= XK_Serbian_dze) + *upper += (XK_Serbian_DJE - XK_Serbian_dje); + else if (sym >= XK_Cyrillic_YU && sym <= XK_Cyrillic_HARDSIGN) + *lower -= (XK_Cyrillic_YU - XK_Cyrillic_yu); + else if (sym >= XK_Cyrillic_yu && sym <= XK_Cyrillic_hardsign) + *upper += (XK_Cyrillic_YU - XK_Cyrillic_yu); + break; + case 7: /* Greek */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_OMEGAaccent) + *lower += (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent); + else if (sym >= XK_Greek_alphaaccent && sym <= XK_Greek_omegaaccent && + sym != XK_Greek_iotaaccentdieresis && + sym != XK_Greek_upsilonaccentdieresis) + *upper -= (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent); + else if (sym >= XK_Greek_ALPHA && sym <= XK_Greek_OMEGA) + *lower += (XK_Greek_alpha - XK_Greek_ALPHA); + else if (sym >= XK_Greek_alpha && sym <= XK_Greek_omega && + sym != XK_Greek_finalsmallsigma) + *upper -= (XK_Greek_alpha - XK_Greek_ALPHA); + break; + } +} + +static KeySym KeyCodetoKeySym(KeySymsPtr keymap, int keycode, int col) +{ + register int per = keymap->mapWidth; + register KeySym *syms; + KeySym lsym, usym; + + if ((col < 0) || ((col >= per) && (col > 3)) || + (keycode < keymap->minKeyCode) || (keycode > keymap->maxKeyCode)) + return NoSymbol; + + syms = &keymap->map[(keycode - keymap->minKeyCode) * per]; + if (col < 4) { + if (col > 1) { + while ((per > 2) && (syms[per - 1] == NoSymbol)) + per--; + if (per < 3) + col -= 2; + } + if ((per <= (col|1)) || (syms[col|1] == NoSymbol)) { + XConvertCase(syms[col&~1], &lsym, &usym); + if (!(col & 1)) + return lsym; + // I'm commenting out this logic because it's incorrect even though it + // was copied from the Xlib sources. The X protocol book quite clearly + // states that where a group consists of element 1 being a non-alphabetic + // keysym and element 2 being NoSymbol that you treat the second element + // as being the same as the first. This also tallies with the behaviour + // produced by the installed Xlib on my linux box (I believe this is + // because it uses some XKB code rather than the original Xlib code - + // compare XKBBind.c with KeyBind.c in lib/X11). + // else if (usym == lsym) + // return NoSymbol; + else + return usym; + } + } + return syms[col]; +} + +// KeysymToKeycode() - find the keycode and column corresponding to the given +// keysym. The value of col passed in should be the column determined from the +// current shift state. If the keysym can be found in that column we prefer +// that to finding it in a different column (which would require fake events to +// alter the shift state). + +static KeyCode KeysymToKeycode(KeySymsPtr keymap, KeySym ks, int* col) +{ + register int i, j; + + j = *col; + for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) { + if (KeyCodetoKeySym(keymap, i, j) == ks) + return i; + } + + for (j = 0; j < keymap->mapWidth; j++) { + for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) { + if (KeyCodetoKeySym(keymap, i, j) == ks) { + *col = j; + return i; + } + } + } + return 0; +} diff --git a/unix/xc/programs/Xserver/vnc/XserverDesktop.h b/unix/xc/programs/Xserver/vnc/XserverDesktop.h new file mode 100644 index 00000000..880acc21 --- /dev/null +++ b/unix/xc/programs/Xserver/vnc/XserverDesktop.h @@ -0,0 +1,130 @@ +/* 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. + */ +// +// XserverDesktop.h +// + +#ifndef __XSERVERDESKTOP_H__ +#define __XSERVERDESKTOP_H__ + +#include <rfb/SDesktop.h> +#include <rfb/HTTPServer.h> +#include <rfb/PixelBuffer.h> +#include <rfb/Configuration.h> +#include <rfb/VNCServerST.h> +#include <rdr/SubstitutingInStream.h> + +extern "C" { +#define class c_class; +#include <scrnintstr.h> +#include <os.h> +#undef class +} + +namespace rfb { + class VNCServerST; +} + +namespace network { class TcpListener; class Socket; } + +class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer, + public rfb::ColourMap, public rdr::Substitutor, + public rfb::VNCServerST::QueryConnectionHandler { +public: + + XserverDesktop(ScreenPtr pScreen, network::TcpListener* listener, + network::TcpListener* httpListener_, + const char* name, void* fbptr); + virtual ~XserverDesktop(); + + // methods called from X server code + void serverReset(ScreenPtr pScreen); + void setColormap(ColormapPtr cmap); + void setColourMapEntries(ColormapPtr pColormap, int ndef, xColorItem* pdef); + void bell(); + void serverCutText(const char* str, int len); + void setCursor(CursorPtr cursor); + void add_changed(RegionPtr reg); + void add_copied(RegionPtr dst, int dx, int dy); + void positionCursor(); + void ignoreHooks(bool b) { ignoreHooks_ = b; } + void blockHandler(fd_set* fds); + void wakeupHandler(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); + + // approveConnection() + // Used by X server code to supply the result of a query. + void approveConnection(void* opaqueId, bool accept, + const char* rejectMsg=0); + + // rfb::SDesktop callbacks + virtual void pointerEvent(const rfb::Point& pos, int buttonMask); + virtual void keyEvent(rdr::U32 key, bool down); + virtual void clientCutText(const char* str, int len); + virtual rfb::Point getFbSize() { return rfb::Point(width(), height()); } + + // rfb::PixelBuffer callbacks + virtual void grabRegion(const rfb::Region& r); + + // rfb::ColourMap callbacks + virtual void lookup(int index, int* r, int* g, int* b); + + // rdr::Substitutor callback + virtual char* substitute(const char* varName); + + // rfb::VNCServerST::QueryConnectionHandler callback + virtual rfb::VNCServerST::queryResult queryConnection(network::Socket* sock, + const char* userName, + char** reason); + +private: + void setColourMapEntries(int firstColour, int nColours); + static CARD32 deferredUpdateTimerCallback(OsTimerPtr timer, CARD32 now, + pointer arg); + void deferUpdate(); + ScreenPtr pScreen; + OsTimerPtr deferredUpdateTimer, dummyTimer; + rfb::VNCServerST* server; + rfb::HTTPServer* httpServer; + network::TcpListener* listener; + network::TcpListener* httpListener; + ColormapPtr cmap; + bool deferredUpdateTimerSet; + bool grabbing; + bool ignoreHooks_; + bool directFbptr; + int oldButtonMask; + rfb::Point cursorPos, oldCursorPos; + + void* queryConnectId; + rfb::CharArray queryConnectAddress; + rfb::CharArray queryConnectUsername; +}; +#endif diff --git a/unix/xc/programs/Xserver/vnc/Xvnc/Imakefile b/unix/xc/programs/Xserver/vnc/Xvnc/Imakefile new file mode 100644 index 00000000..93885ae3 --- /dev/null +++ b/unix/xc/programs/Xserver/vnc/Xvnc/Imakefile @@ -0,0 +1,95 @@ + + VNCTOP = $(TOP)/.. + VNCLIBS = VncExtLibs + VNCINCLUDE = -I$(VNCTOP) -I$(VNCTOP)/vncconfig_unix + +#if defined(XFree86Version) && XFree86Version < 4000 + VNCDEFINES = -DNO_INIT_BACKING_STORE +#endif + +#define CplusplusSource + +#include <Server.tmpl> + +#if HasShm +SHMDEF = -DHAS_SHM +#endif + +XCOMM add more architectures here as we discover them +#if defined(HPArchitecture) || \ + (defined(SparcArchitecture) && !defined(LynxOSArchitecture)) || \ + SystemV4 || \ + defined(OSF1Architecture) || \ + defined(i386BsdArchitecture) || \ + defined(LinuxArchitecture) || \ + defined(DarwinArchitecture) +MMAPDEF = -DHAS_MMAP +#endif + +#ifdef XVendorString +VENDORSTRING = XVendorString +#else +VENDORSTRING = "unknown" +#endif + +#ifdef XVendorRelease +VENDORRELEASE = XVendorRelease +#else +VENDORRELEASE = 0 +#endif + + VENDOR_STRING = -DVENDOR_STRING=\"$(VENDORSTRING)\" + VENDOR_RELEASE = -DVENDOR_RELEASE="$(VENDORRELEASE)" + +#ifdef OS2Architecture +SRCS1 = os2_stubs.c +OBJS1 = os2_stubs.o +#endif + +FBINCLUDE = -I../../fb + +SRCSA = xvnc.cc stubs.c $(SRCS1) miinitext.c $(SRCS2) + +OBJSA = xvnc.o stubs.o $(OBJS1) miinitext.o $(OBJS2) + +INCLUDES = -I. -I.. -I$(XBUILDINCDIR) -I$(FONTINCSRC) \ + $(FBINCLUDE) -I../../mfb -I../../mi -I../../include -I../../os \ + -I$(EXTINCSRC) -I$(XINCLUDESRC) -I$(SERVERSRC)/render $(VNCINCLUDE) + +DEFINES = $(OS_DEFINES) $(SHMDEF) $(MMAPDEF) $(FB_DEFINES) \ + $(VENDOR_STRING) $(VENDOR_RELEASE) $(STD_DEFINES) ServerOSDefines \ + $(VNCDEFINES) -UXFree86LOADER + +#ifdef XFree86Version +/* + * Make sure XINPUT, XF86VidTune, etc arent defined for the miinitext.o + * used by Xvnc + */ +EXT_DEFINES = ExtensionDefines -UXF86VIDMODE -UXFreeXDGA -UXF86MISC +#endif + + +SRCS = $(SRCSA) $(SRCSB) $(SRCSC) +OBJS = $(OBJSA) $(OBJSB) $(OBJSC) + +NormalLibraryObjectRule() +NormalLibraryTarget(xvnc,$(OBJS) buildtime.o) + +#ifdef OS2Architecture +LinkSourceFile(os2_stubs.c,../xfree86/os-support/os2) +SpecialCObjectRule(os2_stubs,$(ICONFIGFILES),-DOS2NULLSELECT) +#endif + +#ifdef HasGcc +NO_OPERATOR_NAMES = -fno-operator-names +#endif +LinkSourceFile(stubs.c,../../Xi) +SpecialCplusplusObjectRule(xvnc,$(ICONFIGFILES) xvnc,$(EXT_DEFINES) $(NO_OPERATOR_NAMES)) + +LinkSourceFile(miinitext.c,$(SERVERSRC)/mi) +SpecialCObjectRule(miinitext,$(ICONFIGFILES),$(EXT_DEFINES) $(PAN_DEFINES) -DNO_MODULE_EXTS $(EXT_MODULE_DEFINES) -UXFree86LOADER) + +/* InstallManPage(Xvfb,$(MANDIR)) */ +DependTarget() + +buildtime.o: $(OBJS) ../LibraryTargetName(vnc) $(VNCLIBS) diff --git a/unix/xc/programs/Xserver/vnc/Xvnc/buildtime.c b/unix/xc/programs/Xserver/vnc/Xvnc/buildtime.c new file mode 100644 index 00000000..3f4c3690 --- /dev/null +++ b/unix/xc/programs/Xserver/vnc/Xvnc/buildtime.c @@ -0,0 +1,18 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +char buildtime[] = __DATE__ " " __TIME__; diff --git a/unix/xc/programs/Xserver/vnc/Xvnc/xvnc.cc b/unix/xc/programs/Xserver/vnc/Xvnc/xvnc.cc new file mode 100644 index 00000000..fcb73e65 --- /dev/null +++ b/unix/xc/programs/Xserver/vnc/Xvnc/xvnc.cc @@ -0,0 +1,1475 @@ +/* Copyright (c) 1993 X Consortium + Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the X Consortium. + +*/ + +#include <rfb/Configuration.h> +#include <rfb/Logger_stdio.h> +#include <rfb/LogWriter.h> +#include <network/TcpSocket.h> +#include "vncExtInit.h" + +extern "C" { +#define class c_class +#define public c_public +#define xor c_xor +#define and c_and +#ifdef WIN32 +#include <X11/Xwinsock.h> +#endif +#include <stdio.h> +#include "X11/X.h" +#define NEED_EVENTS +#include "X11/Xproto.h" +#include "X11/Xos.h" +#include "scrnintstr.h" +#include "servermd.h" +#include "fb.h" +#include "mi.h" +#include "mibstore.h" +#include "colormapst.h" +#include "gcstruct.h" +#include "input.h" +#include "mipointer.h" +#define new New +#include "micmap.h" +#undef new +#include <sys/types.h> +#ifdef HAS_MMAP +#include <sys/mman.h> +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif +#endif /* HAS_MMAP */ +#include <sys/stat.h> +#include <errno.h> +#ifndef WIN32 +#include <sys/param.h> +#endif +#include <X11/XWDFile.h> +#ifdef HAS_SHM +#include <sys/ipc.h> +#include <sys/shm.h> +#endif /* HAS_SHM */ +#include "dix.h" +#include "miline.h" +#include "inputstr.h" +#include "keysym.h" + extern int defaultColorVisualClass; + extern char buildtime[]; +#undef class +#undef public +#undef xor +#undef and +} + +#define XVNCVERSION "Free Edition 4.1.1" +#define XVNCCOPYRIGHT ("Copyright (C) 2002-2005 RealVNC Ltd.\n" \ + "See http://www.realvnc.com for information on VNC.\n") + + +extern char *display; +extern int monitorResolution; + +#define VFB_DEFAULT_WIDTH 1024 +#define VFB_DEFAULT_HEIGHT 768 +#define VFB_DEFAULT_DEPTH 16 +#define VFB_DEFAULT_WHITEPIXEL 0xffff +#define VFB_DEFAULT_BLACKPIXEL 0 +#define VFB_DEFAULT_LINEBIAS 0 +#define XWD_WINDOW_NAME_LEN 60 + +typedef struct +{ + int scrnum; + int width; + int paddedBytesWidth; + int paddedWidth; + int height; + int depth; + int bitsPerPixel; + int sizeInBytes; + int ncolors; + char *pfbMemory; + XWDColor *pXWDCmap; + XWDFileHeader *pXWDHeader; + Pixel blackPixel; + Pixel whitePixel; + unsigned int lineBias; + CloseScreenProcPtr closeScreen; + +#ifdef HAS_MMAP + int mmap_fd; + char mmap_file[MAXPATHLEN]; +#endif + +#ifdef HAS_SHM + int shmid; +#endif + + Bool pixelFormatDefined; + Bool rgbNotBgr; + int redBits, greenBits, blueBits; + +} vfbScreenInfo, *vfbScreenInfoPtr; + +static int vfbNumScreens; +static vfbScreenInfo vfbScreens[MAXSCREENS]; +static Bool vfbPixmapDepths[33]; +#ifdef HAS_MMAP +static char *pfbdir = NULL; +#endif +typedef enum { NORMAL_MEMORY_FB, SHARED_MEMORY_FB, MMAPPED_FILE_FB } fbMemType; +static fbMemType fbmemtype = NORMAL_MEMORY_FB; +static char needswap = 0; +static int lastScreen = -1; +static Bool Render = TRUE; + +static bool displaySpecified = false; +static bool wellKnownSocketsCreated = false; +static char displayNumStr[16]; + +#define swapcopy16(_dst, _src) \ + if (needswap) { CARD16 _s = _src; cpswaps(_s, _dst); } \ + else _dst = _src; + +#define swapcopy32(_dst, _src) \ + if (needswap) { CARD32 _s = _src; cpswapl(_s, _dst); } \ + else _dst = _src; + + +static void +vfbInitializePixmapDepths(void) +{ + int i; + vfbPixmapDepths[1] = TRUE; /* always need bitmaps */ + for (i = 2; i <= 32; i++) + vfbPixmapDepths[i] = FALSE; +} + +static void +vfbInitializeDefaultScreens(void) +{ + int i; + + for (i = 0; i < MAXSCREENS; i++) + { + vfbScreens[i].scrnum = i; + vfbScreens[i].width = VFB_DEFAULT_WIDTH; + vfbScreens[i].height = VFB_DEFAULT_HEIGHT; + vfbScreens[i].depth = VFB_DEFAULT_DEPTH; + vfbScreens[i].blackPixel = VFB_DEFAULT_BLACKPIXEL; + vfbScreens[i].whitePixel = VFB_DEFAULT_WHITEPIXEL; + vfbScreens[i].lineBias = VFB_DEFAULT_LINEBIAS; + vfbScreens[i].pixelFormatDefined = FALSE; + vfbScreens[i].pfbMemory = NULL; + } + vfbNumScreens = 1; +} + +static int +vfbBitsPerPixel(int depth) +{ + if (depth == 1) return 1; + else if (depth <= 8) return 8; + else if (depth <= 16) return 16; + else return 32; +} + + +extern "C" { + + /* ddxInitGlobals - called by |InitGlobals| from os/util.c in XOrg */ + void ddxInitGlobals(void) + { + } + + void ddxGiveUp() + { + int i; + + /* clean up the framebuffers */ + + switch (fbmemtype) + { +#ifdef HAS_MMAP + case MMAPPED_FILE_FB: + for (i = 0; i < vfbNumScreens; i++) + { + if (-1 == unlink(vfbScreens[i].mmap_file)) + { + perror("unlink"); + ErrorF("unlink %s failed, errno %d", + vfbScreens[i].mmap_file, errno); + } + } + break; +#else /* HAS_MMAP */ + case MMAPPED_FILE_FB: + break; +#endif /* HAS_MMAP */ + +#ifdef HAS_SHM + case SHARED_MEMORY_FB: + for (i = 0; i < vfbNumScreens; i++) + { + if (-1 == shmdt((char *)vfbScreens[i].pXWDHeader)) + { + perror("shmdt"); + ErrorF("shmdt failed, errno %d", errno); + } + } + break; +#else /* HAS_SHM */ + case SHARED_MEMORY_FB: + break; +#endif /* HAS_SHM */ + + case NORMAL_MEMORY_FB: + for (i = 0; i < vfbNumScreens; i++) + { + Xfree(vfbScreens[i].pXWDHeader); + } + break; + } +} + +void +AbortDDX() +{ + ddxGiveUp(); +} + +#ifdef __DARWIN__ +void +DarwinHandleGUI(int argc, char *argv[]) +{ +} + +void GlxExtensionInit(); +void GlxWrapInitVisuals(void *procPtr); + +void +DarwinGlxExtensionInit() +{ + GlxExtensionInit(); +} + +void +DarwinGlxWrapInitVisuals( + void *procPtr) +{ + GlxWrapInitVisuals(procPtr); +} +#endif + +void +OsVendorInit() +{ +} + +void +OsVendorFatalError() +{ +} + +void ddxBeforeReset(void) +{ + return; +} + +void +ddxUseMsg() +{ + ErrorF("\nXvnc %s - built %s\n%s", XVNCVERSION, buildtime, XVNCCOPYRIGHT); + ErrorF("Underlying X server release %d, %s\n\n", VENDOR_RELEASE, + VENDOR_STRING); + ErrorF("-screen scrn WxHxD set screen's width, height, depth\n"); + ErrorF("-pixdepths list-of-int support given pixmap depths\n"); +#ifdef RENDER + ErrorF("+/-render turn on/off RENDER extension support" + "(default on)\n"); +#endif + ErrorF("-linebias n adjust thin line pixelization\n"); + ErrorF("-blackpixel n pixel value for black\n"); + ErrorF("-whitepixel n pixel value for white\n"); + +#ifdef HAS_MMAP + ErrorF("-fbdir directory put framebuffers in mmap'ed files in directory\n"); +#endif + +#ifdef HAS_SHM + ErrorF("-shmem put framebuffers in shared memory\n"); +#endif + + ErrorF("-geometry WxH set screen 0's width, height\n"); + 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("\nVNC parameters:\n"); + + fprintf(stderr,"\n" + "Parameters can be turned on with -<param> or off with -<param>=0\n" + "Parameters which take a value can be specified as " + "-<param> <value>\n" + "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); + } +} + +/* ddxInitGlobals - called by |InitGlobals| from os/util.c */ +void ddxInitGlobals(void) +{ +} + +static +bool displayNumFree(int num) +{ + try { + network::TcpListener l(6000+num); + } catch (rdr::Exception& e) { + return false; + } + char file[256]; + sprintf(file, "/tmp/.X%d-lock", num); + if (access(file, F_OK) == 0) return false; + sprintf(file, "/tmp/.X11-unix/X%d", num); + 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; +} + +int +ddxProcessArgument(int argc, char *argv[], int i) +{ + static Bool firstTime = TRUE; + + if (firstTime) + { + vfbInitializeDefaultScreens(); + vfbInitializePixmapDepths(); + firstTime = FALSE; + rfb::initStdIOLoggers(); + rfb::LogWriter::setLogParams("*:stderr:30"); + } + + if (argv[i][0] == ':') + displaySpecified = true; + + if (strcmp (argv[i], "-screen") == 0) /* -screen n WxHxD */ + { + int screenNum; + if (i + 2 >= argc) UseMsg(); + screenNum = atoi(argv[i+1]); + if (screenNum < 0 || screenNum >= MAXSCREENS) + { + ErrorF("Invalid screen number %d\n", screenNum); + UseMsg(); + } + if (3 != sscanf(argv[i+2], "%dx%dx%d", + &vfbScreens[screenNum].width, + &vfbScreens[screenNum].height, + &vfbScreens[screenNum].depth)) + { + ErrorF("Invalid screen configuration %s\n", argv[i+2]); + UseMsg(); + } + + if (screenNum >= vfbNumScreens) + vfbNumScreens = screenNum + 1; + lastScreen = screenNum; + return 3; + } + + if (strcmp (argv[i], "-pixdepths") == 0) /* -pixdepths list-of-depth */ + { + int depth, ret = 1; + + if (++i >= argc) UseMsg(); + while ((i < argc) && (depth = atoi(argv[i++])) != 0) + { + if (depth < 0 || depth > 32) + { + ErrorF("Invalid pixmap depth %d\n", depth); + UseMsg(); + } + vfbPixmapDepths[depth] = TRUE; + ret++; + } + return ret; + } + + if (strcmp (argv[i], "+render") == 0) /* +render */ + { + Render = TRUE; + return 1; + } + + if (strcmp (argv[i], "-render") == 0) /* -render */ + { + Render = FALSE; + return 1; + } + + if (strcmp (argv[i], "-blackpixel") == 0) /* -blackpixel n */ + { + Pixel pix; + if (++i >= argc) UseMsg(); + pix = atoi(argv[i]); + if (-1 == lastScreen) + { + int i; + for (i = 0; i < MAXSCREENS; i++) + { + vfbScreens[i].blackPixel = pix; + } + } + else + { + vfbScreens[lastScreen].blackPixel = pix; + } + return 2; + } + + if (strcmp (argv[i], "-whitepixel") == 0) /* -whitepixel n */ + { + Pixel pix; + if (++i >= argc) UseMsg(); + pix = atoi(argv[i]); + if (-1 == lastScreen) + { + int i; + for (i = 0; i < MAXSCREENS; i++) + { + vfbScreens[i].whitePixel = pix; + } + } + else + { + vfbScreens[lastScreen].whitePixel = pix; + } + return 2; + } + + if (strcmp (argv[i], "-linebias") == 0) /* -linebias n */ + { + unsigned int linebias; + if (++i >= argc) UseMsg(); + linebias = atoi(argv[i]); + if (-1 == lastScreen) + { + int i; + for (i = 0; i < MAXSCREENS; i++) + { + vfbScreens[i].lineBias = linebias; + } + } + else + { + vfbScreens[lastScreen].lineBias = linebias; + } + return 2; + } + +#ifdef HAS_MMAP + if (strcmp (argv[i], "-fbdir") == 0) /* -fbdir directory */ + { + if (++i >= argc) UseMsg(); + pfbdir = argv[i]; + fbmemtype = MMAPPED_FILE_FB; + return 2; + } +#endif /* HAS_MMAP */ + +#ifdef HAS_SHM + if (strcmp (argv[i], "-shmem") == 0) /* -shmem */ + { + fbmemtype = SHARED_MEMORY_FB; + return 1; + } +#endif + + if (strcmp(argv[i], "-geometry") == 0) + { + if (++i >= argc) UseMsg(); + if (sscanf(argv[i],"%dx%d",&vfbScreens[0].width, + &vfbScreens[0].height) != 2) { + ErrorF("Invalid geometry %s\n", argv[i]); + UseMsg(); + } + return 2; + } + + if (strcmp(argv[i], "-depth") == 0) + { + if (++i >= argc) UseMsg(); + vfbScreens[0].depth = atoi(argv[i]); + return 2; + } + + if (strcmp(argv[i], "-pixelformat") == 0) + { + char rgbbgr[4]; + int bits1, bits2, bits3; + if (++i >= argc) UseMsg(); + if (sscanf(argv[i], "%3s%1d%1d%1d", rgbbgr,&bits1,&bits2,&bits3) < 4) { + ErrorF("Invalid pixel format %s\n", argv[i]); + UseMsg(); + } + +#define SET_PIXEL_FORMAT(vfbScreen) \ + (vfbScreen).pixelFormatDefined = TRUE; \ + (vfbScreen).depth = bits1 + bits2 + bits3; \ + (vfbScreen).greenBits = bits2; \ + if (strcasecmp(rgbbgr, "bgr") == 0) { \ + (vfbScreen).rgbNotBgr = FALSE; \ + (vfbScreen).redBits = bits3; \ + (vfbScreen).blueBits = bits1; \ + } else if (strcasecmp(rgbbgr, "rgb") == 0) { \ + (vfbScreen).rgbNotBgr = TRUE; \ + (vfbScreen).redBits = bits1; \ + (vfbScreen).blueBits = bits3; \ + } else { \ + ErrorF("Invalid pixel format %s\n", argv[i]); \ + UseMsg(); \ + } + + if (-1 == lastScreen) + { + int i; + for (i = 0; i < MAXSCREENS; i++) + { + SET_PIXEL_FORMAT(vfbScreens[i]); + } + } + else + { + SET_PIXEL_FORMAT(vfbScreens[lastScreen]); + } + + return 2; + } + + if (strcmp(argv[i], "-inetd") == 0) + { + dup2(0,3); + vncInetdSock = 3; + close(2); + + if (!displaySpecified) { + int port = network::TcpSocket::getSockPort(vncInetdSock); + int displayNum = port - 5900; + if (displayNum < 0 || displayNum > 99 || !displayNumFree(displayNum)) { + for (displayNum = 1; displayNum < 100; displayNum++) + if (displayNumFree(displayNum)) break; + + if (displayNum == 100) + FatalError("Xvnc error: no free display number for -inetd"); + } + + display = displayNumStr; + sprintf(displayNumStr, "%d", displayNum); + } + + return 1; + } + + if (rfb::Configuration::setParam(argv[i])) + return 1; + + if (argv[i][0] == '-' && i+1 < argc) { + if (rfb::Configuration::setParam(&argv[i][1], argv[i+1])) + return 2; + } + + return 0; +} + +#ifdef DDXTIME /* from ServerOSDefines */ +CARD32 +GetTimeInMillis() +{ + struct timeval tp; + + X_GETTIMEOFDAY(&tp); + return(tp.tv_sec * 1000) + (tp.tv_usec / 1000); +} +#endif + +static ColormapPtr InstalledMaps[MAXSCREENS]; + +static int +vfbListInstalledColormaps(ScreenPtr pScreen, Colormap *pmaps) +{ + /* By the time we are processing requests, we can guarantee that there + * is always a colormap installed */ + *pmaps = InstalledMaps[pScreen->myNum]->mid; + return (1); +} + + +static void +vfbInstallColormap(ColormapPtr pmap) +{ + int index = pmap->pScreen->myNum; + ColormapPtr oldpmap = InstalledMaps[index]; + + if (pmap != oldpmap) + { + int entries; + XWDFileHeader *pXWDHeader; + XWDColor *pXWDCmap; + VisualPtr pVisual; + Pixel * ppix; + xrgb * prgb; + xColorItem *defs; + int i; + + if(oldpmap != (ColormapPtr)None) + WalkTree(pmap->pScreen, TellLostMap, (char *)&oldpmap->mid); + /* Install pmap */ + InstalledMaps[index] = pmap; + WalkTree(pmap->pScreen, TellGainedMap, (char *)&pmap->mid); + + entries = pmap->pVisual->ColormapEntries; + pXWDHeader = vfbScreens[pmap->pScreen->myNum].pXWDHeader; + pXWDCmap = vfbScreens[pmap->pScreen->myNum].pXWDCmap; + pVisual = pmap->pVisual; + + swapcopy32(pXWDHeader->visual_class, pVisual->c_class); + swapcopy32(pXWDHeader->red_mask, pVisual->redMask); + swapcopy32(pXWDHeader->green_mask, pVisual->greenMask); + swapcopy32(pXWDHeader->blue_mask, pVisual->blueMask); + swapcopy32(pXWDHeader->bits_per_rgb, pVisual->bitsPerRGBValue); + swapcopy32(pXWDHeader->colormap_entries, pVisual->ColormapEntries); + + ppix = (Pixel *)ALLOCATE_LOCAL(entries * sizeof(Pixel)); + prgb = (xrgb *)ALLOCATE_LOCAL(entries * sizeof(xrgb)); + defs = (xColorItem *)ALLOCATE_LOCAL(entries * sizeof(xColorItem)); + + for (i = 0; i < entries; i++) ppix[i] = i; + /* XXX truecolor */ + QueryColors(pmap, entries, ppix, prgb); + + for (i = 0; i < entries; i++) { /* convert xrgbs to xColorItems */ + defs[i].pixel = ppix[i] & 0xff; /* change pixel to index */ + defs[i].red = prgb[i].red; + defs[i].green = prgb[i].green; + defs[i].blue = prgb[i].blue; + defs[i].flags = DoRed|DoGreen|DoBlue; + } + (*pmap->pScreen->StoreColors)(pmap, entries, defs); + + DEALLOCATE_LOCAL(ppix); + DEALLOCATE_LOCAL(prgb); + DEALLOCATE_LOCAL(defs); + } +} + +static void +vfbUninstallColormap(ColormapPtr pmap) +{ + ColormapPtr curpmap = InstalledMaps[pmap->pScreen->myNum]; + + if(pmap == curpmap) + { + if (pmap->mid != pmap->pScreen->defColormap) + { + curpmap = (ColormapPtr) LookupIDByType(pmap->pScreen->defColormap, + RT_COLORMAP); + (*pmap->pScreen->InstallColormap)(curpmap); + } + } +} + +static void +vfbStoreColors(ColormapPtr pmap, int ndef, xColorItem *pdefs) +{ + XWDColor *pXWDCmap; + int i; + + if (pmap != InstalledMaps[pmap->pScreen->myNum]) + { + return; + } + + pXWDCmap = vfbScreens[pmap->pScreen->myNum].pXWDCmap; + + if ((pmap->pVisual->c_class | DynamicClass) == DirectColor) + { + return; + } + + for (i = 0; i < ndef; i++) + { + if (pdefs[i].flags & DoRed) + { + swapcopy16(pXWDCmap[pdefs[i].pixel].red, pdefs[i].red); + } + if (pdefs[i].flags & DoGreen) + { + swapcopy16(pXWDCmap[pdefs[i].pixel].green, pdefs[i].green); + } + if (pdefs[i].flags & DoBlue) + { + swapcopy16(pXWDCmap[pdefs[i].pixel].blue, pdefs[i].blue); + } + } +} + +static Bool +vfbSaveScreen(ScreenPtr pScreen, int on) +{ + return TRUE; +} + +#ifdef HAS_MMAP + +/* this flushes any changes to the screens out to the mmapped file */ +static void +vfbBlockHandler(pointer blockData, OSTimePtr pTimeout, pointer pReadmask) +{ + int i; + + for (i = 0; i < vfbNumScreens; i++) + { +#ifdef MS_ASYNC + if (-1 == msync((caddr_t)vfbScreens[i].pXWDHeader, + (size_t)vfbScreens[i].sizeInBytes, MS_ASYNC)) +#else + /* silly NetBSD and who else? */ + if (-1 == msync((caddr_t)vfbScreens[i].pXWDHeader, + (size_t)vfbScreens[i].sizeInBytes)) +#endif + { + perror("msync"); + ErrorF("msync failed, errno %d", errno); + } + } +} + + +static void +vfbWakeupHandler(pointer blockData, int result, pointer pReadmask) +{ +} + + +static void +vfbAllocateMmappedFramebuffer(vfbScreenInfoPtr pvfb) +{ +#define DUMMY_BUFFER_SIZE 65536 + char dummyBuffer[DUMMY_BUFFER_SIZE]; + int currentFileSize, writeThisTime; + + sprintf(pvfb->mmap_file, "%s/Xvfb_screen%d", pfbdir, pvfb->scrnum); + if (-1 == (pvfb->mmap_fd = open(pvfb->mmap_file, O_CREAT|O_RDWR, 0666))) + { + perror("open"); + ErrorF("open %s failed, errno %d", pvfb->mmap_file, errno); + return; + } + + /* Extend the file to be the proper size */ + + bzero(dummyBuffer, DUMMY_BUFFER_SIZE); + for (currentFileSize = 0; + currentFileSize < pvfb->sizeInBytes; + currentFileSize += writeThisTime) + { + writeThisTime = min(DUMMY_BUFFER_SIZE, + pvfb->sizeInBytes - currentFileSize); + if (-1 == write(pvfb->mmap_fd, dummyBuffer, writeThisTime)) + { + perror("write"); + ErrorF("write %s failed, errno %d", pvfb->mmap_file, errno); + return; + } + } + + /* try to mmap the file */ + + pvfb->pXWDHeader = (XWDFileHeader *)mmap((caddr_t)NULL, pvfb->sizeInBytes, + PROT_READ|PROT_WRITE, + MAP_FILE|MAP_SHARED, + pvfb->mmap_fd, 0); + if (-1 == (long)pvfb->pXWDHeader) + { + perror("mmap"); + ErrorF("mmap %s failed, errno %d", pvfb->mmap_file, errno); + pvfb->pXWDHeader = NULL; + return; + } + + if (!RegisterBlockAndWakeupHandlers(vfbBlockHandler, vfbWakeupHandler, + NULL)) + { + pvfb->pXWDHeader = NULL; + } +} +#endif /* HAS_MMAP */ + + +#ifdef HAS_SHM +static void +vfbAllocateSharedMemoryFramebuffer(vfbScreenInfoPtr pvfb) +{ + /* create the shared memory segment */ + + pvfb->shmid = shmget(IPC_PRIVATE, pvfb->sizeInBytes, IPC_CREAT|0777); + if (pvfb->shmid < 0) + { + perror("shmget"); + ErrorF("shmget %d bytes failed, errno %d", pvfb->sizeInBytes, errno); + return; + } + + /* try to attach it */ + + pvfb->pXWDHeader = (XWDFileHeader *)shmat(pvfb->shmid, 0, 0); + if (-1 == (long)pvfb->pXWDHeader) + { + perror("shmat"); + ErrorF("shmat failed, errno %d", errno); + pvfb->pXWDHeader = NULL; + return; + } + + ErrorF("screen %d shmid %d\n", pvfb->scrnum, pvfb->shmid); +} +#endif /* HAS_SHM */ + + +static char * +vfbAllocateFramebufferMemory(vfbScreenInfoPtr pvfb) +{ + if (pvfb->pfbMemory) return pvfb->pfbMemory; /* already done */ + + pvfb->sizeInBytes = pvfb->paddedBytesWidth * pvfb->height; + + /* Calculate how many entries in colormap. This is rather bogus, because + * the visuals haven't even been set up yet, but we need to know because we + * have to allocate space in the file for the colormap. The number 10 + * below comes from the MAX_PSEUDO_DEPTH define in cfbcmap.c. + */ + + if (pvfb->depth <= 10) + { /* single index colormaps */ + pvfb->ncolors = 1 << pvfb->depth; + } + else + { /* decomposed colormaps */ + int nplanes_per_color_component = pvfb->depth / 3; + if (pvfb->depth % 3) nplanes_per_color_component++; + pvfb->ncolors = 1 << nplanes_per_color_component; + } + + /* add extra bytes for XWDFileHeader, window name, and colormap */ + + pvfb->sizeInBytes += SIZEOF(XWDheader) + XWD_WINDOW_NAME_LEN + + pvfb->ncolors * SIZEOF(XWDColor); + + pvfb->pXWDHeader = NULL; + switch (fbmemtype) + { +#ifdef HAS_MMAP + case MMAPPED_FILE_FB: vfbAllocateMmappedFramebuffer(pvfb); break; +#else + case MMAPPED_FILE_FB: break; +#endif + +#ifdef HAS_SHM + case SHARED_MEMORY_FB: vfbAllocateSharedMemoryFramebuffer(pvfb); break; +#else + case SHARED_MEMORY_FB: break; +#endif + + case NORMAL_MEMORY_FB: + pvfb->pXWDHeader = (XWDFileHeader *)Xalloc(pvfb->sizeInBytes); + break; + } + + if (pvfb->pXWDHeader) + { + pvfb->pXWDCmap = (XWDColor *)((char *)pvfb->pXWDHeader + + SIZEOF(XWDheader) + XWD_WINDOW_NAME_LEN); + pvfb->pfbMemory = (char *)(pvfb->pXWDCmap + pvfb->ncolors); + + return pvfb->pfbMemory; + } + else + return NULL; +} + +static void +vfbWriteXWDFileHeader(ScreenPtr pScreen) +{ + vfbScreenInfoPtr pvfb = &vfbScreens[pScreen->myNum]; + XWDFileHeader *pXWDHeader = pvfb->pXWDHeader; + char hostname[XWD_WINDOW_NAME_LEN]; + unsigned long swaptest = 1; + int i; + + needswap = *(char *) &swaptest; + + pXWDHeader->header_size = (char *)pvfb->pXWDCmap - (char *)pvfb->pXWDHeader; + pXWDHeader->file_version = XWD_FILE_VERSION; + + pXWDHeader->pixmap_format = ZPixmap; + pXWDHeader->pixmap_depth = pvfb->depth; + pXWDHeader->pixmap_height = pXWDHeader->window_height = pvfb->height; + pXWDHeader->xoffset = 0; + pXWDHeader->byte_order = IMAGE_BYTE_ORDER; + pXWDHeader->bitmap_bit_order = BITMAP_BIT_ORDER; +#ifndef INTERNAL_VS_EXTERNAL_PADDING + pXWDHeader->pixmap_width = pXWDHeader->window_width = pvfb->width; + pXWDHeader->bitmap_unit = BITMAP_SCANLINE_UNIT; + pXWDHeader->bitmap_pad = BITMAP_SCANLINE_PAD; +#else + pXWDHeader->pixmap_width = pXWDHeader->window_width = pvfb->paddedWidth; + pXWDHeader->bitmap_unit = BITMAP_SCANLINE_UNIT_PROTO; + pXWDHeader->bitmap_pad = BITMAP_SCANLINE_PAD_PROTO; +#endif + pXWDHeader->bits_per_pixel = pvfb->bitsPerPixel; + pXWDHeader->bytes_per_line = pvfb->paddedBytesWidth; + pXWDHeader->ncolors = pvfb->ncolors; + + /* visual related fields are written when colormap is installed */ + + pXWDHeader->window_x = pXWDHeader->window_y = 0; + pXWDHeader->window_bdrwidth = 0; + + /* write xwd "window" name: Xvfb hostname:server.screen */ + + if (-1 == gethostname(hostname, sizeof(hostname))) + hostname[0] = 0; + else + hostname[XWD_WINDOW_NAME_LEN-1] = 0; + sprintf((char *)(pXWDHeader+1), "Xvfb %s:%s.%d", hostname, display, + pScreen->myNum); + + /* write colormap pixel slot values */ + + for (i = 0; i < pvfb->ncolors; i++) + { + pvfb->pXWDCmap[i].pixel = i; + } + + /* byte swap to most significant byte first */ + + if (needswap) + { + SwapLongs((CARD32 *)pXWDHeader, SIZEOF(XWDheader)/4); + for (i = 0; i < pvfb->ncolors; i++) + { + register char n; + swapl(&pvfb->pXWDCmap[i].pixel, n); + } + } +} + + +static Bool +vfbCursorOffScreen (ScreenPtr *ppScreen, int *x, int *y) +{ + return FALSE; +} + +static void +vfbCrossScreen (ScreenPtr pScreen, Bool entering) +{ +} + +static Bool vfbRealizeCursor(ScreenPtr pScreen, CursorPtr pCursor) { + return TRUE; +} + +static Bool vfbUnrealizeCursor(ScreenPtr pScreen, CursorPtr pCursor) { + return TRUE; +} + +static void vfbSetCursor(ScreenPtr pScreen, CursorPtr pCursor, int x, int y) +{ +} + +static void vfbMoveCursor(ScreenPtr pScreen, int x, int y) +{ +} + +static miPointerSpriteFuncRec vfbPointerSpriteFuncs = { + vfbRealizeCursor, + vfbUnrealizeCursor, + vfbSetCursor, + vfbMoveCursor +}; + +static miPointerScreenFuncRec vfbPointerCursorFuncs = { + vfbCursorOffScreen, + vfbCrossScreen, + miPointerWarpCursor +}; + +static Bool +vfbCloseScreen(int index, ScreenPtr pScreen) +{ + vfbScreenInfoPtr pvfb = &vfbScreens[index]; + int i; + + pScreen->CloseScreen = pvfb->closeScreen; + + /* + * XXX probably lots of stuff to clean. For now, + * clear InstalledMaps[] so that server reset works correctly. + */ + for (i = 0; i < MAXSCREENS; i++) + InstalledMaps[i] = NULL; + + return pScreen->CloseScreen(index, pScreen); +} + +static Bool +vfbScreenInit(int index, ScreenPtr pScreen, int argc, char **argv) +{ + vfbScreenInfoPtr pvfb = &vfbScreens[index]; + int dpi = 100; + int ret; + char *pbits; + + if (monitorResolution) dpi = monitorResolution; + + pvfb->paddedBytesWidth = PixmapBytePad(pvfb->width, pvfb->depth); + pvfb->bitsPerPixel = vfbBitsPerPixel(pvfb->depth); + pvfb->paddedWidth = pvfb->paddedBytesWidth * 8 / pvfb->bitsPerPixel; + pbits = vfbAllocateFramebufferMemory(pvfb); + if (!pbits) return FALSE; + vncFbptr[index] = pbits; + + defaultColorVisualClass + = (pvfb->bitsPerPixel > 8) ? TrueColor : PseudoColor; + + ret = fbScreenInit(pScreen, pbits, pvfb->width, pvfb->height, + dpi, dpi, pvfb->paddedWidth, pvfb->bitsPerPixel); + +#ifdef RENDER + if (ret && Render) + fbPictureInit (pScreen, 0, 0); +#endif + + if (!ret) return FALSE; + + /* miInitializeBackingStore(pScreen); */ + + /* + * Circumvent the backing store that was just initialised. This amounts + * to a truely bizarre way of initialising SaveDoomedAreas and friends. + */ + + pScreen->InstallColormap = vfbInstallColormap; + pScreen->UninstallColormap = vfbUninstallColormap; + pScreen->ListInstalledColormaps = vfbListInstalledColormaps; + + pScreen->SaveScreen = vfbSaveScreen; + pScreen->StoreColors = vfbStoreColors; + + miPointerInitialize(pScreen, &vfbPointerSpriteFuncs, &vfbPointerCursorFuncs, + FALSE); + + vfbWriteXWDFileHeader(pScreen); + + pScreen->blackPixel = pvfb->blackPixel; + pScreen->whitePixel = pvfb->whitePixel; + + if (!pvfb->pixelFormatDefined && pvfb->depth == 16) { + pvfb->pixelFormatDefined = TRUE; + pvfb->rgbNotBgr = TRUE; + pvfb->blueBits = pvfb->redBits = 5; + pvfb->greenBits = 6; + } + + if (pvfb->pixelFormatDefined) { + VisualPtr vis = pScreen->visuals; + for (int i = 0; i < pScreen->numVisuals; i++) { + if (pvfb->rgbNotBgr) { + vis->offsetBlue = 0; + vis->blueMask = (1 << pvfb->blueBits) - 1; + vis->offsetGreen = pvfb->blueBits; + vis->greenMask = ((1 << pvfb->greenBits) - 1) << vis->offsetGreen; + vis->offsetRed = vis->offsetGreen + pvfb->greenBits; + vis->redMask = ((1 << pvfb->redBits) - 1) << vis->offsetRed; + } else { + vis->offsetRed = 0; + vis->redMask = (1 << pvfb->redBits) - 1; + vis->offsetGreen = pvfb->redBits; + vis->greenMask = ((1 << pvfb->greenBits) - 1) << vis->offsetGreen; + vis->offsetBlue = vis->offsetGreen + pvfb->greenBits; + vis->blueMask = ((1 << pvfb->blueBits) - 1) << vis->offsetBlue; + } + vis++; + } + } + + ret = fbCreateDefColormap(pScreen); + + miSetZeroLineBias(pScreen, pvfb->lineBias); + + pvfb->closeScreen = pScreen->CloseScreen; + pScreen->CloseScreen = vfbCloseScreen; + +#ifndef NO_INIT_BACKING_STORE + miInitializeBackingStore(pScreen); + pScreen->backingStoreSupport = Always; +#endif + + return ret; + +} /* end vfbScreenInit */ + + +static void vfbClientStateChange(CallbackListPtr*, pointer, pointer) { + dispatchException &= ~DE_RESET; +} + +void +InitOutput(ScreenInfo *screenInfo, int argc, char **argv) +{ + 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; + + /* initialize pixmap formats */ + + /* must have a pixmap depth to match every screen depth */ + for (i = 0; i < vfbNumScreens; i++) + { + vfbPixmapDepths[vfbScreens[i].depth] = TRUE; + } + + /* RENDER needs a good set of pixmaps. */ + if (Render) { + vfbPixmapDepths[1] = TRUE; + vfbPixmapDepths[4] = TRUE; + vfbPixmapDepths[8] = TRUE; +/* vfbPixmapDepths[15] = TRUE; */ + vfbPixmapDepths[16] = TRUE; + vfbPixmapDepths[24] = TRUE; + vfbPixmapDepths[32] = TRUE; + } + + for (i = 1; i <= 32; i++) + { + if (vfbPixmapDepths[i]) + { + if (NumFormats >= MAXFORMATS) + FatalError ("MAXFORMATS is too small for this server\n"); + screenInfo->formats[NumFormats].depth = i; + screenInfo->formats[NumFormats].bitsPerPixel = vfbBitsPerPixel(i); + screenInfo->formats[NumFormats].scanlinePad = BITMAP_SCANLINE_PAD; + NumFormats++; + } + } + + screenInfo->imageByteOrder = IMAGE_BYTE_ORDER; + screenInfo->bitmapScanlineUnit = BITMAP_SCANLINE_UNIT; + screenInfo->bitmapScanlinePad = BITMAP_SCANLINE_PAD; + screenInfo->bitmapBitOrder = BITMAP_BIT_ORDER; + screenInfo->numPixmapFormats = NumFormats; + + /* initialize screens */ + + for (i = 0; i < vfbNumScreens; i++) + { + if (-1 == AddScreen(vfbScreenInit, argc, argv)) + { + FatalError("Couldn't add screen %d", i); + } + } + + if (!AddCallback(&ClientStateCallback, vfbClientStateChange, 0)) { + FatalError("AddCallback failed\n"); + } +} /* end InitOutput */ + +#ifdef DPMSExtension +extern "C" { +#if NeedFunctionPrototypes + void DPMSSet(CARD16 level) +#else + void DPMSSet(level) + CARD16 level; +#endif + { + return; + } + + Bool DPMSSupported() + { + return FALSE; + } +} +#endif + +/* this is just to get the server to link on AIX */ +#ifdef AIXV3 +int SelectWaitTime = 10000; /* usec */ +#endif + +Bool LegalModifier(unsigned int key, DevicePtr pDev) +{ + return TRUE; +} + +void ProcessInputEvents() +{ + mieqProcessInputEvents(); + miPointerUpdate(); +} + +/* Fairly standard US PC Keyboard */ + +#define VFB_MIN_KEY 8 +#define VFB_MAX_KEY 255 +#define VFB_MAP_LEN (VFB_MAX_KEY - VFB_MIN_KEY + 1) +#define KEYSYMS_PER_KEY 2 +KeySym keyboardMap[VFB_MAP_LEN * KEYSYMS_PER_KEY] = { + NoSymbol, NoSymbol, + XK_Escape, NoSymbol, + XK_1, XK_exclam, + XK_2, XK_at, + XK_3, XK_numbersign, + XK_4, XK_dollar, + XK_5, XK_percent, + XK_6, XK_asciicircum, + XK_7, XK_ampersand, + XK_8, XK_asterisk, + XK_9, XK_parenleft, + XK_0, XK_parenright, + XK_minus, XK_underscore, + XK_equal, XK_plus, + XK_BackSpace, NoSymbol, + XK_Tab, NoSymbol, + XK_q, XK_Q, + XK_w, XK_W, + XK_e, XK_E, + XK_r, XK_R, + XK_t, XK_T, + XK_y, XK_Y, + XK_u, XK_U, + XK_i, XK_I, + XK_o, XK_O, + XK_p, XK_P, + XK_bracketleft, XK_braceleft, + XK_bracketright, XK_braceright, + XK_Return, NoSymbol, + XK_Control_L, NoSymbol, + XK_a, XK_A, + XK_s, XK_S, + XK_d, XK_D, + XK_f, XK_F, + XK_g, XK_G, + XK_h, XK_H, + XK_j, XK_J, + XK_k, XK_K, + XK_l, XK_L, + XK_semicolon, XK_colon, + XK_apostrophe, XK_quotedbl, + XK_grave, XK_asciitilde, + XK_Shift_L, NoSymbol, + XK_backslash, XK_bar, + XK_z, XK_Z, + XK_x, XK_X, + XK_c, XK_C, + XK_v, XK_V, + XK_b, XK_B, + XK_n, XK_N, + XK_m, XK_M, + XK_comma, XK_less, + XK_period, XK_greater, + XK_slash, XK_question, + XK_Shift_R, NoSymbol, + XK_KP_Multiply, NoSymbol, + XK_Alt_L, XK_Meta_L, + XK_space, NoSymbol, + /*XK_Caps_Lock*/ NoSymbol, NoSymbol, + XK_F1, NoSymbol, + XK_F2, NoSymbol, + XK_F3, NoSymbol, + XK_F4, NoSymbol, + XK_F5, NoSymbol, + XK_F6, NoSymbol, + XK_F7, NoSymbol, + XK_F8, NoSymbol, + XK_F9, NoSymbol, + XK_F10, NoSymbol, + XK_Num_Lock, XK_Pointer_EnableKeys, + XK_Scroll_Lock, NoSymbol, + XK_KP_Home, XK_KP_7, + XK_KP_Up, XK_KP_8, + XK_KP_Prior, XK_KP_9, + XK_KP_Subtract, NoSymbol, + XK_KP_Left, XK_KP_4, + XK_KP_Begin, XK_KP_5, + XK_KP_Right, XK_KP_6, + XK_KP_Add, NoSymbol, + XK_KP_End, XK_KP_1, + XK_KP_Down, XK_KP_2, + XK_KP_Next, XK_KP_3, + XK_KP_Insert, XK_KP_0, + XK_KP_Delete, XK_KP_Decimal, + NoSymbol, NoSymbol, + NoSymbol, NoSymbol, + NoSymbol, NoSymbol, + XK_F11, NoSymbol, + XK_F12, NoSymbol, + XK_Home, NoSymbol, + XK_Up, NoSymbol, + XK_Prior, NoSymbol, + XK_Left, NoSymbol, + NoSymbol, NoSymbol, + XK_Right, NoSymbol, + XK_End, NoSymbol, + XK_Down, NoSymbol, + XK_Next, NoSymbol, + XK_Insert, NoSymbol, + XK_Delete, NoSymbol, + XK_KP_Enter, NoSymbol, + XK_Control_R, NoSymbol, + XK_Pause, XK_Break, + XK_Print, XK_Execute, + XK_KP_Divide, NoSymbol, + XK_Alt_R, XK_Meta_R, +}; + +static Bool GetMappings(KeySymsPtr pKeySyms, CARD8 *pModMap) +{ + int i; + + for (i = 0; i < MAP_LENGTH; i++) + pModMap[i] = NoSymbol; + + for (i = 0; i < VFB_MAP_LEN; i++) { + if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Caps_Lock) + pModMap[i + VFB_MIN_KEY] = LockMask; + else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Shift_L || + keyboardMap[i * KEYSYMS_PER_KEY] == XK_Shift_R) + pModMap[i + VFB_MIN_KEY] = ShiftMask; + else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Control_L || + keyboardMap[i * KEYSYMS_PER_KEY] == XK_Control_R) { + pModMap[i + VFB_MIN_KEY] = ControlMask; + } + else if (keyboardMap[i * KEYSYMS_PER_KEY] == XK_Alt_L || + keyboardMap[i * KEYSYMS_PER_KEY] == XK_Alt_R) + pModMap[i + VFB_MIN_KEY] = Mod1Mask; + } + + pKeySyms->minKeyCode = VFB_MIN_KEY; + pKeySyms->maxKeyCode = VFB_MAX_KEY; + pKeySyms->mapWidth = KEYSYMS_PER_KEY; + pKeySyms->map = keyboardMap; + + return TRUE; +} + +static void vfbBell(int percent, DeviceIntPtr device, pointer ctrl, int class_) +{ + if (percent > 0) + vncBell(); +} + +static int vfbKeybdProc(DeviceIntPtr pDevice, int onoff) +{ + KeySymsRec keySyms; + CARD8 modMap[MAP_LENGTH]; + DevicePtr pDev = (DevicePtr)pDevice; + + switch (onoff) + { + case DEVICE_INIT: + GetMappings(&keySyms, modMap); + InitKeyboardDeviceStruct(pDev, &keySyms, modMap, + (BellProcPtr)vfbBell, (KbdCtrlProcPtr)NoopDDA); + break; + case DEVICE_ON: + pDev->on = TRUE; + break; + case DEVICE_OFF: + pDev->on = FALSE; + break; + case DEVICE_CLOSE: + break; + } + return Success; +} + +static int vfbMouseProc(DeviceIntPtr pDevice, int onoff) +{ + BYTE map[6]; + DevicePtr pDev = (DevicePtr)pDevice; + + switch (onoff) + { + case DEVICE_INIT: + map[1] = 1; + map[2] = 2; + map[3] = 3; + map[4] = 4; + map[5] = 5; + InitPointerDeviceStruct(pDev, map, 5, miPointerGetMotionEvents, + (PtrCtrlProcPtr)NoopDDA, miPointerGetMotionBufferSize()); + break; + + case DEVICE_ON: + pDev->on = TRUE; + break; + + case DEVICE_OFF: + pDev->on = FALSE; + break; + + case DEVICE_CLOSE: + break; + } + return Success; +} + +// InitInput is called after InitExtensions, so we're guaranteed that +// vncExtensionInit() has already been called. + +void InitInput(int argc, char *argv[]) +{ + DeviceIntPtr p, k; + p = AddInputDevice(vfbMouseProc, TRUE); + k = AddInputDevice(vfbKeybdProc, TRUE); + RegisterPointerDevice(p); + RegisterKeyboardDevice(k); + miRegisterPointerDevice(screenInfo.screens[0], p); + (void)mieqInit ((DevicePtr)k, (DevicePtr)p); +} diff --git a/unix/xc/programs/Xserver/vnc/module/Imakefile b/unix/xc/programs/Xserver/vnc/module/Imakefile new file mode 100644 index 00000000..f279649c --- /dev/null +++ b/unix/xc/programs/Xserver/vnc/module/Imakefile @@ -0,0 +1,60 @@ + + VNCTOP = $(TOP)/.. + VNCLIBS = VncExtLibs + VNCINCLUDE = -I$(VNCTOP) -I$(VNCTOP)/vncconfig_unix + +#define CplusplusSource + +#define IHaveModules +#include <Server.tmpl> + + SRCS = vncExtInit.cc vncHooks.cc xf86vncModule.cc XserverDesktop.cc + OBJS = vncExtInit.o vncHooks.o xf86vncModule.o XserverDesktop.o +INCLUDES = -I.. -I../../include -I$(EXTINCSRC) -I$(XINCLUDESRC) \ + -I$(FONTINCSRC) -I$(XF86COMSRC) \ + -I../../render $(VNCINCLUDE) + DEFINES = $(STD_DEFINES) -DGC_HAS_COMPOSITE_CLIP -DXFree86LOADER + +LinkSourceFile(vncExtInit.cc,..) +LinkSourceFile(vncHooks.cc,..) +LinkSourceFile(xf86vncModule.cc,..) +LinkSourceFile(XserverDesktop.cc,..) + +ModuleObjectRule() +/* + LibraryModuleTarget(vnc,$(OBJS) $(VNCLIBS)) + InstallLibraryModule(vnc,$(MODULEDIR),extensions) +*/ + +/* *** The imake rules don't define a ModuleCplusplusObjectRule so + for now we just assume that NormalCplusplusObjectRule will + do the job. + NB: If we don't do this then make will assume CC is the C++ compiler! +*/ +NormalCplusplusObjectRule() + +/* + * CplusplusDynamicModuleTarget - build a module to be dynamically loaded + */ +#ifndef CplusplusDynamicModuleTarget +#define CplusplusDynamicModuleTarget(module,modlist) @@\ +AllTarget(module) @@\ + @@\ +module: modlist @@\ + RemoveFile($@) @@\ + $(CXX) -o $@ $(SHLIBLDFLAGS) modlist @@\ + @@\ +clean:: @@\ + RemoveFile(module) +#endif /* CplusplusDynamicModuleTarget */ + + + +CplusplusDynamicModuleTarget(vnc.so,$(OBJS) $(VNCLIBS)) +InstallDynamicModule(vnc.so,$(MODULEDIR),extensions) + +DependTarget() + +/* + InstallDriverSDKLibraryModule(vnc,$(DRIVERSDKMODULEDIR),extensions) +*/ diff --git a/unix/xc/programs/Xserver/vnc/vncExtInit.cc b/unix/xc/programs/Xserver/vnc/vncExtInit.cc new file mode 100644 index 00000000..9cf9d21b --- /dev/null +++ b/unix/xc/programs/Xserver/vnc/vncExtInit.cc @@ -0,0 +1,866 @@ +/* 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. + */ + +#include <stdio.h> + +extern "C" { +#define class c_class +#define NEED_EVENTS +#include "X.h" +#include "Xproto.h" +#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" +#undef class +#undef xalloc +} + +#include <rfb/Configuration.h> +#include <rfb/Logger_stdio.h> +#include <rfb/LogWriter.h> +#include <rfb/util.h> +#include <rfb/ServerCore.h> +#include <rfb/SSecurityFactoryStandard.h> +#include <rdr/HexOutStream.h> +#include <rfb/LogWriter.h> +#undef max +#undef min +#include <network/TcpSocket.h> + +#include "XserverDesktop.h" +#include "vncHooks.h" +#include "vncExtInit.h" + +extern "C" { + + extern void vncExtensionInit(); + static void vncResetProc(ExtensionEntry* extEntry); + static void vncBlockHandler(pointer data, OSTimePtr t, pointer readmask); + static void vncWakeupHandler(pointer data, int nfds, pointer readmask); + static void vncClientStateChange(CallbackListPtr*, pointer, pointer); + static void SendSelectionChangeEvent(Atom selection); + static int ProcVncExtDispatch(ClientPtr client); + static int SProcVncExtDispatch(ClientPtr client); + + extern char *display; + + extern Selection *CurrentSelections; + extern int NumCurrentSelections; +} + +using namespace rfb; + +static rfb::LogWriter vlog("vncext"); + +static unsigned long vncExtGeneration = 0; +static bool initialised = false; +static XserverDesktop* desktop[MAXSCREENS] = { 0, }; +void* vncFbptr[MAXSCREENS] = { 0, }; + +static char* clientCutText = 0; +static int clientCutTextLen = 0; + +static XserverDesktop* queryConnectDesktop = 0; +static void* queryConnectId = 0; +static int queryConnectTimeout = 0; +static OsTimerPtr queryConnectTimer = 0; + +static struct VncInputSelect* vncInputSelectHead = 0; +struct VncInputSelect { + VncInputSelect(ClientPtr c, Window w, int m) : client(c), window(w), mask(m) + { + next = vncInputSelectHead; + vncInputSelectHead = this; + } + ClientPtr client; + Window window; + int mask; + VncInputSelect* next; +}; + +static int nPrevSelections = 0; +static TimeStamp* prevSelectionTimes = 0; + +static int vncErrorBase = 0; +static int vncEventBase = 0; +static char* vncPasswdFile = 0; +int vncInetdSock = -1; + +rfb::AliasParameter rfbauth("rfbauth", "Alias for PasswordFile", + &SSecurityFactoryStandard::vncAuthPasswdFile); +rfb::StringParameter httpDir("httpd", + "Directory containing files to serve via HTTP", + ""); +rfb::IntParameter httpPort("httpPort", "TCP port to listen for HTTP",0); +rfb::AliasParameter rfbwait("rfbwait", "Alias for ClientWaitTimeMillis", + &rfb::Server::clientWaitTimeMillis); +rfb::IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",0); +rfb::StringParameter desktopName("desktop", "Name of VNC desktop","x11"); +rfb::BoolParameter localhostOnly("localhost", + "Only allow connections from localhost", + false); + +void vncExtensionInit() +{ + if (vncExtGeneration == serverGeneration) { + vlog.error("vncExtensionInit: called twice in same generation?"); + return; + } + vncExtGeneration = serverGeneration; + + ExtensionEntry* extEntry + = AddExtension(VNCEXTNAME, VncExtNumberEvents, VncExtNumberErrors, + ProcVncExtDispatch, SProcVncExtDispatch, vncResetProc, + StandardMinorOpcode); + if (!extEntry) { + ErrorF("vncExtInit: AddExtension failed\n"); + return; + } + + vncErrorBase = extEntry->errorBase; + vncEventBase = extEntry->eventBase; + + vlog.info("VNC extension running!"); + + if (!AddCallback(&ClientStateCallback, vncClientStateChange, 0)) { + FatalError("AddCallback failed\n"); + } + + try { + if (!initialised) { + rfb::initStdIOLoggers(); + initialised = true; + } + + for (int scr = 0; scr < screenInfo.numScreens; scr++) { + + if (!desktop[scr]) { + network::TcpListener* listener = 0; + network::TcpListener* httpListener = 0; + if (scr == 0 && vncInetdSock != -1) { + if (network::TcpSocket::isSocket(vncInetdSock) && + !network::TcpSocket::isConnected(vncInetdSock)) + { + listener = new network::TcpListener(0, 0, vncInetdSock, true); + vlog.info("inetd wait"); + } + } else { + int port = rfbport; + if (port == 0) port = 5900 + atoi(display); + port += 1000 * scr; + listener = new network::TcpListener(port, localhostOnly); + vlog.info("Listening for VNC connections on port %d",port); + CharArray httpDirStr(httpDir.getData()); + if (httpDirStr.buf[0]) { + port = httpPort; + if (port == 0) port = 5800 + atoi(display); + port += 1000 * scr; + httpListener = new network::TcpListener(port, localhostOnly); + vlog.info("Listening for HTTP connections on port %d",port); + } + } + + CharArray desktopNameStr(desktopName.getData()); + desktop[scr] = new XserverDesktop(screenInfo.screens[scr], listener, + httpListener, + desktopNameStr.buf, + vncFbptr[scr]); + vlog.info("created VNC server for screen %d", scr); + + if (scr == 0 && vncInetdSock != -1 && !listener) { + network::Socket* sock = new network::TcpSocket(vncInetdSock); + desktop[scr]->addClient(sock, false); + vlog.info("added inetd sock"); + } + + } else { + desktop[scr]->serverReset(screenInfo.screens[scr]); + } + + vncHooksInit(screenInfo.screens[scr], desktop[scr]); + } + + RegisterBlockAndWakeupHandlers(vncBlockHandler, vncWakeupHandler, 0); + + } catch (rdr::Exception& e) { + vlog.error("vncExtInit: %s",e.str()); + } +} + +static void vncResetProc(ExtensionEntry* extEntry) +{ +} + +// +// 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(pointer data, OSTimePtr timeout, pointer readmask) +{ + fd_set* fds = (fd_set*)readmask; + + for (int scr = 0; scr < screenInfo.numScreens; scr++) { + if (desktop[scr]) { + desktop[scr]->blockHandler(fds); + } + } + + if (nPrevSelections != NumCurrentSelections) { + prevSelectionTimes + = (TimeStamp*)xnfrealloc(prevSelectionTimes, + NumCurrentSelections * sizeof(TimeStamp)); + for (int i = nPrevSelections; i < NumCurrentSelections; i++) { + prevSelectionTimes[i].months = 0; + prevSelectionTimes[i].milliseconds = 0; + } + nPrevSelections = NumCurrentSelections; + } + for (int i = 0; i < NumCurrentSelections; i++) { + if (CurrentSelections[i].lastTimeChanged.months + != prevSelectionTimes[i].months || + CurrentSelections[i].lastTimeChanged.milliseconds + != prevSelectionTimes[i].milliseconds) + { + SendSelectionChangeEvent(CurrentSelections[i].selection); + prevSelectionTimes[i] = CurrentSelections[i].lastTimeChanged; + } + } +} + +static void vncWakeupHandler(pointer data, int nfds, pointer readmask) +{ + fd_set* fds = (fd_set*)readmask; + + for (int scr = 0; scr < screenInfo.numScreens; scr++) { + if (desktop[scr]) { + desktop[scr]->wakeupHandler(fds, nfds); + } + } +} + +static void vncClientStateChange(CallbackListPtr*, pointer, pointer p) +{ + ClientPtr client = ((NewClientInfoRec*)p)->client; + if (client->clientState == ClientStateGone) { + VncInputSelect** nextPtr = &vncInputSelectHead; + for (VncInputSelect* cur = vncInputSelectHead; cur; cur = *nextPtr) { + if (cur->client == client) { + *nextPtr = cur->next; + delete cur; + continue; + } + nextPtr = &cur->next; + } + } +} + +void vncBell() +{ + for (int scr = 0; scr < screenInfo.numScreens; scr++) { + if (desktop[scr]) { + desktop[scr]->bell(); + } + } +} + +void vncClientGone(int fd) +{ + if (fd == vncInetdSock) { + fprintf(stderr,"inetdSock client gone\n"); + GiveUp(0); + } +} + +void vncClientCutText(const char* str, int len) +{ + delete [] clientCutText; + clientCutText = new char[len]; + memcpy(clientCutText, str, len); + clientCutTextLen = len; + xVncExtClientCutTextNotifyEvent ev; + ev.type = vncEventBase + VncExtClientCutTextNotify; + for (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) { + int n; + swaps(&ev.sequenceNumber, n); + swapl(&ev.window, n); + swapl(&ev.time, n); + } + WriteToClient(cur->client, sizeof(xVncExtClientCutTextNotifyEvent), + (char *)&ev); + } + } +} + + +static CARD32 queryConnectTimerCallback(OsTimerPtr timer, + CARD32 now, pointer arg) +{ + if (queryConnectTimeout) + queryConnectDesktop->approveConnection(queryConnectId, false, "The attempt to prompt the user to accept the connection failed"); + // Re-notify clients, causing them to discover that we're done + vncQueryConnect(queryConnectDesktop, queryConnectId); + return 0; +} + +void vncQueryConnect(XserverDesktop* desktop, void* opaqueId) +{ + // Only one query can be processed at any one time + if (queryConnectTimeout && ((desktop != queryConnectDesktop) || + (opaqueId != queryConnectId))) { + desktop->approveConnection(opaqueId, false, + "Another connection is currently being queried."); + return; + } + + // Get the query timeout. If it's zero, there is no query. + queryConnectTimeout = desktop->getQueryTimeout(opaqueId); + queryConnectId = queryConnectTimeout ? opaqueId : 0; + queryConnectDesktop = queryConnectTimeout ? desktop : 0; + + // Notify clients + bool notified = false; + xVncExtQueryConnectNotifyEvent ev; + ev.type = vncEventBase + VncExtQueryConnectNotify; + for (VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) { + if (cur->mask & VncExtQueryConnectMask) { + ev.sequenceNumber = cur->client->sequence; + ev.window = cur->window; + if (cur->client->swapped) { + int n; + swaps(&ev.sequenceNumber, n); + swapl(&ev.window, n); + } + WriteToClient(cur->client, sizeof(xVncExtQueryConnectNotifyEvent), + (char *)&ev); + notified = true; + } + } + + // If we're being asked to query a connection (rather than to cancel + // a query), and haven't been able to notify clients then reject it. + if (queryConnectTimeout && !notified) { + queryConnectTimeout = 0; + queryConnectId = 0; + queryConnectDesktop = 0; + desktop->approveConnection(opaqueId, false, + "Unable to query the local user to accept the connection."); + return; + } + + // Set a timer so that if no-one ever responds, we will eventually + // reject the connection + // NB: We don't set a timer if sock is null, since that indicates + // that pending queries should be cancelled. + if (queryConnectDesktop) + queryConnectTimer = TimerSet(queryConnectTimer, 0, + queryConnectTimeout*2000, + queryConnectTimerCallback, 0); + else + TimerCancel(queryConnectTimer); +} + +static void SendSelectionChangeEvent(Atom selection) +{ + xVncExtSelectionChangeNotifyEvent ev; + ev.type = vncEventBase + VncExtSelectionChangeNotify; + for (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) { + int n; + swaps(&ev.sequenceNumber, n); + swapl(&ev.window, n); + swapl(&ev.selection, n); + } + WriteToClient(cur->client, sizeof(xVncExtSelectionChangeNotifyEvent), + (char *)&ev); + } + } +} + +static int ProcVncExtSetParam(ClientPtr client) +{ + REQUEST(xVncExtSetParamReq); + REQUEST_FIXED_SIZE(xVncExtSetParamReq, stuff->paramLen); + CharArray param(stuff->paramLen+1); + strncpy(param.buf, (char*)&stuff[1], stuff->paramLen); + param.buf[stuff->paramLen] = 0; + + xVncExtSetParamReply rep; + int n; + rep.type = X_Reply; + rep.length = 0; + rep.sequenceNumber = client->sequence; + rep.success = rfb::Configuration::setParam(param.buf); + if (client->swapped) { + swaps(&rep.sequenceNumber, n); + swapl(&rep.length, n); + } + WriteToClient(client, sizeof(xVncExtSetParamReply), (char *)&rep); + return (client->noClientException); +} + +static int SProcVncExtSetParam(ClientPtr client) +{ + register char n; + REQUEST(xVncExtSetParamReq); + swaps(&stuff->length, n); + REQUEST_AT_LEAST_SIZE(xVncExtSetParamReq); + return ProcVncExtSetParam(client); +} + +static int ProcVncExtGetParam(ClientPtr client) +{ + REQUEST(xVncExtGetParamReq); + REQUEST_FIXED_SIZE(xVncExtGetParamReq, stuff->paramLen); + CharArray param(stuff->paramLen+1); + strncpy(param.buf, (char*)&stuff[1], stuff->paramLen); + param.buf[stuff->paramLen] = 0; + + xVncExtGetParamReply rep; + int n; + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.success = 0; + int len = 0; + char* value = 0; + rfb::VoidParameter* p = rfb::Configuration::getParam(param.buf); + // Hack to avoid exposing password! + if (strcasecmp(param.buf, "Password") == 0) + p = 0; + if (p) { + value = p->getValueStr(); + rep.success = 1; + len = value ? strlen(value) : 0; + } + rep.length = (len + 3) >> 2; + rep.valueLen = len; + if (client->swapped) { + swaps(&rep.sequenceNumber, n); + swapl(&rep.length, n); + swaps(&rep.valueLen, n); + } + WriteToClient(client, sizeof(xVncExtGetParamReply), (char *)&rep); + if (value) + WriteToClient(client, len, value); + delete [] value; + return (client->noClientException); +} + +static int SProcVncExtGetParam(ClientPtr client) +{ + register char n; + REQUEST(xVncExtGetParamReq); + swaps(&stuff->length, n); + REQUEST_AT_LEAST_SIZE(xVncExtGetParamReq); + return ProcVncExtGetParam(client); +} + +static int ProcVncExtGetParamDesc(ClientPtr client) +{ + REQUEST(xVncExtGetParamDescReq); + REQUEST_FIXED_SIZE(xVncExtGetParamDescReq, stuff->paramLen); + CharArray param(stuff->paramLen+1); + strncpy(param.buf, (char*)&stuff[1], stuff->paramLen); + param.buf[stuff->paramLen] = 0; + + xVncExtGetParamDescReply rep; + int n; + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.success = 0; + int len = 0; + const char* desc = 0; + rfb::VoidParameter* p = rfb::Configuration::getParam(param.buf); + if (p) { + desc = p->getDescription(); + rep.success = 1; + len = desc ? strlen(desc) : 0; + } + rep.length = (len + 3) >> 2; + rep.descLen = len; + if (client->swapped) { + swaps(&rep.sequenceNumber, n); + swapl(&rep.length, n); + swaps(&rep.descLen, n); + } + WriteToClient(client, sizeof(xVncExtGetParamDescReply), (char *)&rep); + if (desc) + WriteToClient(client, len, (char*)desc); + return (client->noClientException); +} + +static int SProcVncExtGetParamDesc(ClientPtr client) +{ + register char n; + REQUEST(xVncExtGetParamDescReq); + swaps(&stuff->length, n); + REQUEST_AT_LEAST_SIZE(xVncExtGetParamDescReq); + return ProcVncExtGetParamDesc(client); +} + +static int ProcVncExtListParams(ClientPtr client) +{ + REQUEST(xVncExtListParamsReq); + REQUEST_SIZE_MATCH(xVncExtListParamsReq); + + xVncExtListParamsReply rep; + int n; + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + + int nParams = 0; + int len = 0; + for (ParameterIterator i(Configuration::global()); i.param; i.next()) { + int l = strlen(i.param->getName()); + if (l <= 255) { + nParams++; + len += l + 1; + } + } + rep.length = (len + 3) >> 2; + rep.nParams = nParams; + if (client->swapped) { + swaps(&rep.sequenceNumber, n); + swapl(&rep.length, n); + swaps(&rep.nParams, n); + } + WriteToClient(client, sizeof(xVncExtListParamsReply), (char *)&rep); + rdr::U8* data = new rdr::U8[len]; + rdr::U8* ptr = data; + for (ParameterIterator i(Configuration::global()); i.param; i.next()) { + int l = strlen(i.param->getName()); + if (l <= 255) { + *ptr++ = l; + memcpy(ptr, i.param->getName(), l); + ptr += l; + } + } + WriteToClient(client, len, (char*)data); + delete [] data; + return (client->noClientException); +} + +static int SProcVncExtListParams(ClientPtr client) +{ + register char n; + REQUEST(xVncExtListParamsReq); + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH(xVncExtListParamsReq); + return ProcVncExtListParams(client); +} + +static int ProcVncExtSetServerCutText(ClientPtr client) +{ + REQUEST(xVncExtSetServerCutTextReq); + REQUEST_FIXED_SIZE(xVncExtSetServerCutTextReq, stuff->textLen); + char* str = new char[stuff->textLen+1]; + strncpy(str, (char*)&stuff[1], stuff->textLen); + str[stuff->textLen] = 0; + for (int scr = 0; scr < screenInfo.numScreens; scr++) { + if (desktop[scr]) { + desktop[scr]->serverCutText(str, stuff->textLen); + } + } + delete [] str; + return (client->noClientException); +} + +static int SProcVncExtSetServerCutText(ClientPtr client) +{ + register char n; + REQUEST(xVncExtSetServerCutTextReq); + swaps(&stuff->length, n); + REQUEST_AT_LEAST_SIZE(xVncExtSetServerCutTextReq); + swapl(&stuff->textLen, n); + return ProcVncExtSetServerCutText(client); +} + +static int ProcVncExtGetClientCutText(ClientPtr client) +{ + REQUEST(xVncExtGetClientCutTextReq); + REQUEST_SIZE_MATCH(xVncExtGetClientCutTextReq); + + xVncExtGetClientCutTextReply rep; + int n; + rep.type = X_Reply; + rep.length = (clientCutTextLen + 3) >> 2; + rep.sequenceNumber = client->sequence; + rep.textLen = clientCutTextLen; + if (client->swapped) { + swaps(&rep.sequenceNumber, n); + swapl(&rep.length, n); + swapl(&rep.textLen, n); + } + WriteToClient(client, sizeof(xVncExtGetClientCutTextReply), (char *)&rep); + if (clientCutText) + WriteToClient(client, clientCutTextLen, clientCutText); + return (client->noClientException); +} + +static int SProcVncExtGetClientCutText(ClientPtr client) +{ + register char n; + REQUEST(xVncExtGetClientCutTextReq); + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH(xVncExtGetClientCutTextReq); + return ProcVncExtGetClientCutText(client); +} + +static int ProcVncExtSelectInput(ClientPtr client) +{ + REQUEST(xVncExtSelectInputReq); + REQUEST_SIZE_MATCH(xVncExtSelectInputReq); + VncInputSelect** nextPtr = &vncInputSelectHead; + VncInputSelect* cur; + for (cur = vncInputSelectHead; cur; cur = *nextPtr) { + if (cur->client == client && cur->window == stuff->window) { + cur->mask = stuff->mask; + if (!cur->mask) { + *nextPtr = cur->next; + delete cur; + } + break; + } + nextPtr = &cur->next; + } + if (!cur) { + cur = new VncInputSelect(client, stuff->window, stuff->mask); + } + return (client->noClientException); +} + +static int SProcVncExtSelectInput(ClientPtr client) +{ + register char n; + REQUEST(xVncExtSelectInputReq); + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH(xVncExtSelectInputReq); + swapl(&stuff->window, n); + swapl(&stuff->mask, n); + return ProcVncExtSelectInput(client); +} + +static int ProcVncExtConnect(ClientPtr client) +{ + REQUEST(xVncExtConnectReq); + REQUEST_FIXED_SIZE(xVncExtConnectReq, stuff->strLen); + CharArray str(stuff->strLen+1); + strncpy(str.buf, (char*)&stuff[1], stuff->strLen); + str.buf[stuff->strLen] = 0; + + xVncExtConnectReply rep; + rep.success = 0; + if (desktop[0]) { + if (stuff->strLen == 0) { + try { + desktop[0]->disconnectClients(); + rep.success = 1; + } catch (rdr::Exception& e) { + vlog.error("Disconnecting all clients: %s",e.str()); + } + } else { + int port = 5500; + for (int i = 0; i < stuff->strLen; i++) { + if (str.buf[i] == ':') { + port = atoi(&str.buf[i+1]); + str.buf[i] = 0; + break; + } + } + + try { + network::Socket* sock = new network::TcpSocket(str.buf, port); + desktop[0]->addClient(sock, true); + rep.success = 1; + } catch (rdr::Exception& e) { + vlog.error("Reverse connection: %s",e.str()); + } + } + } + + rep.type = X_Reply; + rep.length = 0; + rep.sequenceNumber = client->sequence; + if (client->swapped) { + int n; + swaps(&rep.sequenceNumber, n); + swapl(&rep.length, n); + } + WriteToClient(client, sizeof(xVncExtConnectReply), (char *)&rep); + return (client->noClientException); +} + +static int SProcVncExtConnect(ClientPtr client) +{ + register char n; + REQUEST(xVncExtConnectReq); + swaps(&stuff->length, n); + REQUEST_AT_LEAST_SIZE(xVncExtConnectReq); + return ProcVncExtConnect(client); +} + + +static int ProcVncExtGetQueryConnect(ClientPtr client) +{ + REQUEST(xVncExtGetQueryConnectReq); + REQUEST_SIZE_MATCH(xVncExtGetQueryConnectReq); + + const char *qcAddress=0, *qcUsername=0; + int qcTimeout; + if (queryConnectDesktop) + qcTimeout = queryConnectDesktop->getQueryTimeout(queryConnectId, + &qcAddress, &qcUsername); + else + qcTimeout = 0; + + xVncExtGetQueryConnectReply rep; + int n; + 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)queryConnectId; + rep.length = (rep.userLen + rep.addrLen + 3) >> 2; + if (client->swapped) { + swaps(&rep.sequenceNumber, n); + swapl(&rep.userLen, n); + swapl(&rep.addrLen, n); + swapl(&rep.timeout, n); + swapl(&rep.opaqueId, n); + } + 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) +{ + register char n; + REQUEST(xVncExtGetQueryConnectReq); + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH(xVncExtGetQueryConnectReq); + return ProcVncExtGetQueryConnect(client); +} + + +static int ProcVncExtApproveConnect(ClientPtr client) +{ + REQUEST(xVncExtApproveConnectReq); + REQUEST_SIZE_MATCH(xVncExtApproveConnectReq); + if (queryConnectId == (void*)stuff->opaqueId) { + for (int scr = 0; scr < screenInfo.numScreens; scr++) { + if (desktop[scr]) { + desktop[scr]->approveConnection(queryConnectId, stuff->approve, + "Connection rejected by local user"); + } + } + // Inform other clients of the event and tidy up + vncQueryConnect(queryConnectDesktop, queryConnectId); + } + return (client->noClientException); +} + +static int SProcVncExtApproveConnect(ClientPtr client) +{ + register char n; + REQUEST(xVncExtApproveConnectReq); + swaps(&stuff->length, n); + swapl(&stuff->opaqueId, n); + 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; + } +} + diff --git a/unix/xc/programs/Xserver/vnc/vncExtInit.h b/unix/xc/programs/Xserver/vnc/vncExtInit.h new file mode 100644 index 00000000..45453e1f --- /dev/null +++ b/unix/xc/programs/Xserver/vnc/vncExtInit.h @@ -0,0 +1,32 @@ +/* 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 __VNCEXTINIT_H__ +#define __VNCEXTINIT_H__ + +#include <rfb/Configuration.h> +#include "XserverDesktop.h" + +extern void vncClientCutText(const char* str, int len); +extern void vncQueryConnect(XserverDesktop* desktop, void* opaqueId); +extern void vncClientGone(int fd); +extern void vncBell(); +extern void* vncFbptr[]; +extern int vncInetdSock; +extern rfb::StringParameter httpDir; + +#endif diff --git a/unix/xc/programs/Xserver/vnc/vncHooks.cc b/unix/xc/programs/Xserver/vnc/vncHooks.cc new file mode 100644 index 00000000..ce8e7f02 --- /dev/null +++ b/unix/xc/programs/Xserver/vnc/vncHooks.cc @@ -0,0 +1,1533 @@ +/* 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. + */ + +#include <stdio.h> +#include "XserverDesktop.h" +#include "vncHooks.h" + +extern "C" { +#define class c_class +#define private c_private +#include "scrnintstr.h" +#include "windowstr.h" +#include "gcstruct.h" +#include "regionstr.h" +#include "dixfontstr.h" +#include "colormapst.h" +#ifdef RENDER +#include "picturestr.h" +#endif + +#ifdef GC_HAS_COMPOSITE_CLIP +#define COMPOSITE_CLIP(gc) ((gc)->pCompositeClip) +#else +#include "mfb.h" +#define COMPOSITE_CLIP(gc) \ + (((mfbPrivGCPtr)((gc)->devPrivates[mfbGCPrivateIndex].ptr))->pCompositeClip) +#endif + +#undef class +#undef private +} + +#include "RegionHelper.h" + +#define DBGPRINT(x) //(fprintf x) + +// MAX_RECTS_PER_OP is the maximum number of rectangles we generate from +// operations like Polylines and PolySegment. If the operation is more complex +// than this, we simply use the bounding box. Ideally it would be a +// command-line option, but that would involve an extra malloc each time, so we +// fix it here. +#define MAX_RECTS_PER_OP 5 + +static unsigned long vncHooksGeneration = 0; + +// vncHooksScreenRec and vncHooksGCRec contain pointers to the original +// functions which we "wrap" in order to hook the screen changes. The screen +// functions are each wrapped individually, while the GC "funcs" and "ops" are +// wrapped as a unit. + +typedef struct { + XserverDesktop* desktop; + + CloseScreenProcPtr CloseScreen; + CreateGCProcPtr CreateGC; + PaintWindowBackgroundProcPtr PaintWindowBackground; + PaintWindowBorderProcPtr PaintWindowBorder; + CopyWindowProcPtr CopyWindow; + ClearToBackgroundProcPtr ClearToBackground; + RestoreAreasProcPtr RestoreAreas; + InstallColormapProcPtr InstallColormap; + StoreColorsProcPtr StoreColors; + DisplayCursorProcPtr DisplayCursor; + ScreenBlockHandlerProcPtr BlockHandler; +#ifdef RENDER + CompositeProcPtr Composite; +#endif +} vncHooksScreenRec, *vncHooksScreenPtr; + +typedef struct { + GCFuncs *wrappedFuncs; + GCOps *wrappedOps; +} vncHooksGCRec, *vncHooksGCPtr; + +static int vncHooksScreenIndex; +static int vncHooksGCIndex; + + +// screen functions + +static Bool vncHooksCloseScreen(int i, ScreenPtr pScreen); +static Bool vncHooksCreateGC(GCPtr pGC); +static void vncHooksPaintWindowBackground(WindowPtr pWin, RegionPtr pRegion, + int what); +static void vncHooksPaintWindowBorder(WindowPtr pWin, RegionPtr pRegion, + int what); +static void vncHooksCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, + RegionPtr pOldRegion); +static void vncHooksClearToBackground(WindowPtr pWin, int x, int y, int w, + int h, Bool generateExposures); +static RegionPtr vncHooksRestoreAreas(WindowPtr pWin, RegionPtr prgnExposed); +static void vncHooksInstallColormap(ColormapPtr pColormap); +static void vncHooksStoreColors(ColormapPtr pColormap, int ndef, + xColorItem* pdef); +static Bool vncHooksDisplayCursor(ScreenPtr pScreen, CursorPtr cursor); +static void vncHooksBlockHandler(int i, pointer blockData, pointer pTimeout, + pointer pReadmask); +#ifdef RENDER +static void vncHooksComposite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, + PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, + INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height); +#endif + +// GC "funcs" + +static void vncHooksValidateGC(GCPtr pGC, unsigned long changes, + DrawablePtr pDrawable); +static void vncHooksChangeGC(GCPtr pGC, unsigned long mask); +static void vncHooksCopyGC(GCPtr src, unsigned long mask, GCPtr dst); +static void vncHooksDestroyGC(GCPtr pGC); +static void vncHooksChangeClip(GCPtr pGC, int type, pointer pValue,int nrects); +static void vncHooksDestroyClip(GCPtr pGC); +static void vncHooksCopyClip(GCPtr dst, GCPtr src); + +static GCFuncs vncHooksGCFuncs = { + vncHooksValidateGC, vncHooksChangeGC, vncHooksCopyGC, vncHooksDestroyGC, + vncHooksChangeClip, vncHooksDestroyClip, vncHooksCopyClip, +}; + +// GC "ops" + +static void vncHooksFillSpans(DrawablePtr pDrawable, GCPtr pGC, int nInit, + DDXPointPtr pptInit, int *pwidthInit, + int fSorted); +static void vncHooksSetSpans(DrawablePtr pDrawable, GCPtr pGC, char *psrc, + DDXPointPtr ppt, int *pwidth, int nspans, + int fSorted); +static void vncHooksPutImage(DrawablePtr pDrawable, GCPtr pGC, int depth, + int x, int y, int w, int h, int leftPad, + int format, char *pBits); +static RegionPtr vncHooksCopyArea(DrawablePtr pSrc, DrawablePtr pDst, + GCPtr pGC, int srcx, int srcy, int w, int h, + int dstx, int dsty); +static RegionPtr vncHooksCopyPlane(DrawablePtr pSrc, DrawablePtr pDst, + GCPtr pGC, int srcx, int srcy, int w, int h, + int dstx, int dsty, unsigned long plane); +static void vncHooksPolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode, + int npt, xPoint *pts); +static void vncHooksPolylines(DrawablePtr pDrawable, GCPtr pGC, int mode, + int npt, DDXPointPtr ppts); +static void vncHooksPolySegment(DrawablePtr pDrawable, GCPtr pGC, int nseg, + xSegment *segs); +static void vncHooksPolyRectangle(DrawablePtr pDrawable, GCPtr pGC, int nrects, + xRectangle *rects); +static void vncHooksPolyArc(DrawablePtr pDrawable, GCPtr pGC, int narcs, + xArc *arcs); +static void vncHooksFillPolygon(DrawablePtr pDrawable, GCPtr pGC, int shape, + int mode, int count, DDXPointPtr pts); +static void vncHooksPolyFillRect(DrawablePtr pDrawable, GCPtr pGC, int nrects, + xRectangle *rects); +static void vncHooksPolyFillArc(DrawablePtr pDrawable, GCPtr pGC, int narcs, + xArc *arcs); +static int vncHooksPolyText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y, + int count, char *chars); +static int vncHooksPolyText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y, + int count, unsigned short *chars); +static void vncHooksImageText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y, + int count, char *chars); +static void vncHooksImageText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y, + int count, unsigned short *chars); +static void vncHooksImageGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x, + int y, unsigned int nglyph, + CharInfoPtr *ppci, pointer pglyphBase); +static void vncHooksPolyGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x, + int y, unsigned int nglyph, + CharInfoPtr *ppci, pointer pglyphBase); +static void vncHooksPushPixels(GCPtr pGC, PixmapPtr pBitMap, + DrawablePtr pDrawable, int w, int h, int x, + int y); + +static GCOps vncHooksGCOps = { + vncHooksFillSpans, vncHooksSetSpans, vncHooksPutImage, vncHooksCopyArea, + vncHooksCopyPlane, vncHooksPolyPoint, vncHooksPolylines, vncHooksPolySegment, + vncHooksPolyRectangle, vncHooksPolyArc, vncHooksFillPolygon, + vncHooksPolyFillRect, vncHooksPolyFillArc, vncHooksPolyText8, + vncHooksPolyText16, vncHooksImageText8, vncHooksImageText16, + vncHooksImageGlyphBlt, vncHooksPolyGlyphBlt, vncHooksPushPixels +}; + + + +///////////////////////////////////////////////////////////////////////////// +// vncHooksInit() is called at initialisation time and every time the server +// resets. It is called once for each screen, but the indexes are only +// allocated once for each server generation. + +Bool vncHooksInit(ScreenPtr pScreen, XserverDesktop* desktop) +{ + vncHooksScreenPtr vncHooksScreen; + + if (vncHooksGeneration != serverGeneration) { + vncHooksGeneration = serverGeneration; + + vncHooksScreenIndex = AllocateScreenPrivateIndex(); + if (vncHooksScreenIndex < 0) { + ErrorF("vncHooksInit: AllocateScreenPrivateIndex failed\n"); + return FALSE; + } + + vncHooksGCIndex = AllocateGCPrivateIndex(); + if (vncHooksGCIndex < 0) { + ErrorF("vncHooksInit: AllocateGCPrivateIndex failed\n"); + return FALSE; + } + } + + if (!AllocateGCPrivate(pScreen, vncHooksGCIndex, sizeof(vncHooksGCRec))) { + ErrorF("vncHooksInit: AllocateGCPrivate failed\n"); + return FALSE; + } + + vncHooksScreen = (vncHooksScreenPtr)xnfalloc(sizeof(vncHooksScreenRec)); + pScreen->devPrivates[vncHooksScreenIndex].ptr = (pointer)vncHooksScreen; + + vncHooksScreen->desktop = desktop; + + vncHooksScreen->CloseScreen = pScreen->CloseScreen; + vncHooksScreen->CreateGC = pScreen->CreateGC; + vncHooksScreen->PaintWindowBackground = pScreen->PaintWindowBackground; + vncHooksScreen->PaintWindowBorder = pScreen->PaintWindowBorder; + vncHooksScreen->CopyWindow = pScreen->CopyWindow; + vncHooksScreen->ClearToBackground = pScreen->ClearToBackground; + vncHooksScreen->RestoreAreas = pScreen->RestoreAreas; + vncHooksScreen->InstallColormap = pScreen->InstallColormap; + vncHooksScreen->StoreColors = pScreen->StoreColors; + vncHooksScreen->DisplayCursor = pScreen->DisplayCursor; + vncHooksScreen->BlockHandler = pScreen->BlockHandler; +#ifdef RENDER + PictureScreenPtr ps; + ps = GetPictureScreenIfSet(pScreen); + if (ps) { + vncHooksScreen->Composite = ps->Composite; + } +#endif + + pScreen->CloseScreen = vncHooksCloseScreen; + pScreen->CreateGC = vncHooksCreateGC; + pScreen->PaintWindowBackground = vncHooksPaintWindowBackground; + pScreen->PaintWindowBorder = vncHooksPaintWindowBorder; + pScreen->CopyWindow = vncHooksCopyWindow; + pScreen->ClearToBackground = vncHooksClearToBackground; + pScreen->RestoreAreas = vncHooksRestoreAreas; + pScreen->InstallColormap = vncHooksInstallColormap; + pScreen->StoreColors = vncHooksStoreColors; + pScreen->DisplayCursor = vncHooksDisplayCursor; + pScreen->BlockHandler = vncHooksBlockHandler; +#ifdef RENDER + if (ps) { + ps->Composite = vncHooksComposite; + } +#endif + + return TRUE; +} + + + +///////////////////////////////////////////////////////////////////////////// +// +// screen functions +// + +// SCREEN_UNWRAP and SCREEN_REWRAP unwrap and rewrap the given screen function. +// It would be nice to do this with a C++ class, but each function is of a +// distinct type, so it would have to use templates, and it's not worth that +// much pain. + +#define SCREEN_UNWRAP(scrn,field) \ + ScreenPtr pScreen = scrn; \ + vncHooksScreenPtr vncHooksScreen \ + = ((vncHooksScreenPtr)pScreen->devPrivates[vncHooksScreenIndex].ptr); \ + pScreen->field = vncHooksScreen->field; \ + DBGPRINT((stderr,"vncHooks" #field " called\n")); + +#define SCREEN_REWRAP(field) pScreen->field = vncHooks##field; + + +// CloseScreen - unwrap the screen functions and call the original CloseScreen +// function + +static Bool vncHooksCloseScreen(int i, ScreenPtr pScreen_) +{ + SCREEN_UNWRAP(pScreen_, CloseScreen); + + pScreen->CreateGC = vncHooksScreen->CreateGC; + pScreen->PaintWindowBackground = vncHooksScreen->PaintWindowBackground; + pScreen->PaintWindowBorder = vncHooksScreen->PaintWindowBorder; + pScreen->CopyWindow = vncHooksScreen->CopyWindow; + pScreen->ClearToBackground = vncHooksScreen->ClearToBackground; + pScreen->RestoreAreas = vncHooksScreen->RestoreAreas; + pScreen->InstallColormap = vncHooksScreen->InstallColormap; + pScreen->StoreColors = vncHooksScreen->StoreColors; + pScreen->DisplayCursor = vncHooksScreen->DisplayCursor; + pScreen->BlockHandler = vncHooksScreen->BlockHandler; + + xfree((pointer)vncHooksScreen); + + DBGPRINT((stderr,"vncHooksCloseScreen: unwrapped screen functions\n")); + + return (*pScreen->CloseScreen)(i, pScreen); +} + +// CreateGC - wrap the "GC funcs" + +static Bool vncHooksCreateGC(GCPtr pGC) +{ + SCREEN_UNWRAP(pGC->pScreen, CreateGC); + + vncHooksGCPtr vncHooksGC + = (vncHooksGCPtr)pGC->devPrivates[vncHooksGCIndex].ptr; + + Bool ret = (*pScreen->CreateGC) (pGC); + + vncHooksGC->wrappedOps = 0; + vncHooksGC->wrappedFuncs = pGC->funcs; + pGC->funcs = &vncHooksGCFuncs; + + SCREEN_REWRAP(CreateGC); + + return ret; +} + +// PaintWindowBackground - changed region is the given region + +static void vncHooksPaintWindowBackground(WindowPtr pWin, RegionPtr pRegion, + int what) +{ + SCREEN_UNWRAP(pWin->drawable.pScreen, PaintWindowBackground); + + RegionHelper changed(pScreen, pRegion); + + (*pScreen->PaintWindowBackground) (pWin, pRegion, what); + + vncHooksScreen->desktop->add_changed(changed.reg); + + SCREEN_REWRAP(PaintWindowBackground); +} + +// PaintWindowBorder - changed region is the given region + +static void vncHooksPaintWindowBorder(WindowPtr pWin, RegionPtr pRegion, + int what) +{ + SCREEN_UNWRAP(pWin->drawable.pScreen, PaintWindowBorder); + + RegionHelper changed(pScreen, pRegion); + + (*pScreen->PaintWindowBorder) (pWin, pRegion, what); + + vncHooksScreen->desktop->add_changed(changed.reg); + + SCREEN_REWRAP(PaintWindowBorder); +} + +// CopyWindow - destination of the copy is the old region, clipped by +// borderClip, translated by the delta. This call only does the copy - it +// doesn't affect any other bits. + +static void vncHooksCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, + RegionPtr pOldRegion) +{ + SCREEN_UNWRAP(pWin->drawable.pScreen, CopyWindow); + + RegionHelper copied(pScreen, pOldRegion); + int dx = pWin->drawable.x - ptOldOrg.x; + int dy = pWin->drawable.y - ptOldOrg.y; + REGION_TRANSLATE(pScreen, copied.reg, dx, dy); + REGION_INTERSECT(pWin->drawable.pScreen, copied.reg, copied.reg, + &pWin->borderClip); + + (*pScreen->CopyWindow) (pWin, ptOldOrg, pOldRegion); + + vncHooksScreen->desktop->add_copied(copied.reg, dx, dy); + + SCREEN_REWRAP(CopyWindow); +} + +// ClearToBackground - changed region is the given rectangle, clipped by +// clipList, but only if generateExposures is false. + +static void vncHooksClearToBackground(WindowPtr pWin, int x, int y, int w, + int h, Bool generateExposures) +{ + SCREEN_UNWRAP(pWin->drawable.pScreen, ClearToBackground); + + BoxRec box; + box.x1 = x + pWin->drawable.x; + box.y1 = y + pWin->drawable.y; + box.x2 = w ? (box.x1 + w) : (pWin->drawable.x + pWin->drawable.width); + box.y2 = h ? (box.y1 + h) : (pWin->drawable.y + pWin->drawable.height); + + RegionHelper changed(pScreen, &box, 0); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, &pWin->clipList); + + (*pScreen->ClearToBackground) (pWin, x, y, w, h, generateExposures); + + if (!generateExposures) { + vncHooksScreen->desktop->add_changed(changed.reg); + } + + SCREEN_REWRAP(ClearToBackground); +} + +// RestoreAreas - changed region is the given region + +static RegionPtr vncHooksRestoreAreas(WindowPtr pWin, RegionPtr pRegion) +{ + SCREEN_UNWRAP(pWin->drawable.pScreen, RestoreAreas); + + RegionHelper changed(pScreen, pRegion); + + RegionPtr result = (*pScreen->RestoreAreas) (pWin, pRegion); + + vncHooksScreen->desktop->add_changed(changed.reg); + + SCREEN_REWRAP(RestoreAreas); + + return result; +} + +// InstallColormap - get the new colormap + +static void vncHooksInstallColormap(ColormapPtr pColormap) +{ + SCREEN_UNWRAP(pColormap->pScreen, InstallColormap); + + (*pScreen->InstallColormap) (pColormap); + + vncHooksScreen->desktop->setColormap(pColormap); + + SCREEN_REWRAP(InstallColormap); +} + +// StoreColors - get the colormap changes + +static void vncHooksStoreColors(ColormapPtr pColormap, int ndef, + xColorItem* pdef) +{ + SCREEN_UNWRAP(pColormap->pScreen, StoreColors); + + (*pScreen->StoreColors) (pColormap, ndef, pdef); + + vncHooksScreen->desktop->setColourMapEntries(pColormap, ndef, pdef); + + SCREEN_REWRAP(StoreColors); +} + +// DisplayCursor - get the cursor shape + +static Bool vncHooksDisplayCursor(ScreenPtr pScreen_, CursorPtr cursor) +{ + SCREEN_UNWRAP(pScreen_, DisplayCursor); + + Bool ret = (*pScreen->DisplayCursor) (pScreen, cursor); + + vncHooksScreen->desktop->setCursor(cursor); + + SCREEN_REWRAP(DisplayCursor); + + return ret; +} + +// BlockHandler - ignore any changes during the block handler - it's likely +// these are just drawing the cursor. + +static void vncHooksBlockHandler(int i, pointer blockData, pointer pTimeout, + pointer pReadmask) +{ + SCREEN_UNWRAP(screenInfo.screens[i], BlockHandler); + + vncHooksScreen->desktop->ignoreHooks(true); + + (*pScreen->BlockHandler) (i, blockData, pTimeout, pReadmask); + + vncHooksScreen->desktop->ignoreHooks(false); + + SCREEN_REWRAP(BlockHandler); +} + +// Composite - needed for RENDER + +#ifdef RENDER +void vncHooksComposite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, + PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, + INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) +{ + ScreenPtr pScreen = pDst->pDrawable->pScreen; + vncHooksScreenPtr vncHooksScreen = ((vncHooksScreenPtr)pScreen->devPrivates[vncHooksScreenIndex].ptr); + BoxRec box; + PictureScreenPtr ps = GetPictureScreen(pScreen); + rfb::Rect rect1, rect2; + + rect1.setXYWH(pDst->pDrawable->x + xDst, + pDst->pDrawable->y + yDst, + width, + height); + + rect2 = rect1.intersect(vncHooksScreen->desktop->getRect()); + if (!rect2.is_empty()) { + box.x1 = rect2.tl.x; + box.y1 = rect2.tl.y; + box.x2 = rect2.br.x; + box.y2 = rect2.br.y; + RegionHelper changed(pScreen, &box, 0); + vncHooksScreen->desktop->add_changed(changed.reg); + } + + ps->Composite = vncHooksScreen->Composite; + (*ps->Composite)(op, pSrc, pMask, pDst, xSrc, ySrc, + xMask, yMask, xDst, yDst, width, height); + ps->Composite = vncHooksComposite; +} + +#endif /* RENDER */ + + +///////////////////////////////////////////////////////////////////////////// +// +// GC "funcs" +// + +// GCFuncUnwrapper is a helper class which unwraps the GC funcs and ops in its +// constructor and rewraps them in its destructor. + +class GCFuncUnwrapper { +public: + GCFuncUnwrapper(GCPtr pGC_) : pGC(pGC_) { + vncHooksGC = (vncHooksGCPtr)pGC->devPrivates[vncHooksGCIndex].ptr; + pGC->funcs = vncHooksGC->wrappedFuncs; + if (vncHooksGC->wrappedOps) + pGC->ops = vncHooksGC->wrappedOps; + } + ~GCFuncUnwrapper() { + vncHooksGC->wrappedFuncs = pGC->funcs; + pGC->funcs = &vncHooksGCFuncs; + if (vncHooksGC->wrappedOps) { + vncHooksGC->wrappedOps = pGC->ops; + pGC->ops = &vncHooksGCOps; + } + } + GCPtr pGC; + vncHooksGCPtr vncHooksGC; +}; + + +// ValidateGC - wrap the "ops" if a viewable window + +static void vncHooksValidateGC(GCPtr pGC, unsigned long changes, + DrawablePtr pDrawable) +{ + GCFuncUnwrapper u(pGC); + + DBGPRINT((stderr,"vncHooksValidateGC called\n")); + + (*pGC->funcs->ValidateGC) (pGC, changes, pDrawable); + + u.vncHooksGC->wrappedOps = 0; + if (pDrawable->type == DRAWABLE_WINDOW && ((WindowPtr)pDrawable)->viewable) { + WindowPtr pWin = (WindowPtr)pDrawable; + RegionPtr pRegion = &pWin->clipList; + + if (pGC->subWindowMode == IncludeInferiors) + pRegion = &pWin->borderClip; + if (REGION_NOTEMPTY(pDrawable->pScreen, pRegion)) { + u.vncHooksGC->wrappedOps = pGC->ops; + DBGPRINT((stderr,"vncHooksValidateGC: wrapped GC ops\n")); + } + } +} + +// Other GC funcs - just unwrap and call on + +static void vncHooksChangeGC(GCPtr pGC, unsigned long mask) { + GCFuncUnwrapper u(pGC); + (*pGC->funcs->ChangeGC) (pGC, mask); +} +static void vncHooksCopyGC(GCPtr src, unsigned long mask, GCPtr dst) { + GCFuncUnwrapper u(dst); + (*dst->funcs->CopyGC) (src, mask, dst); +} +static void vncHooksDestroyGC(GCPtr pGC) { + GCFuncUnwrapper u(pGC); + (*pGC->funcs->DestroyGC) (pGC); +} +static void vncHooksChangeClip(GCPtr pGC, int type, pointer pValue, int nrects) +{ + GCFuncUnwrapper u(pGC); + (*pGC->funcs->ChangeClip) (pGC, type, pValue, nrects); +} +static void vncHooksDestroyClip(GCPtr pGC) { + GCFuncUnwrapper u(pGC); + (*pGC->funcs->DestroyClip) (pGC); +} +static void vncHooksCopyClip(GCPtr dst, GCPtr src) { + GCFuncUnwrapper u(dst); + (*dst->funcs->CopyClip) (dst, src); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// GC "ops" +// + +// GCOpUnwrapper is a helper class which unwraps the GC funcs and ops in its +// constructor and rewraps them in its destructor. + +class GCOpUnwrapper { +public: + GCOpUnwrapper(DrawablePtr pDrawable, GCPtr pGC_) + : pGC(pGC_), pScreen(pDrawable->pScreen) + { + vncHooksGC = (vncHooksGCPtr)pGC->devPrivates[vncHooksGCIndex].ptr; + oldFuncs = pGC->funcs; + pGC->funcs = vncHooksGC->wrappedFuncs; + pGC->ops = vncHooksGC->wrappedOps; + } + ~GCOpUnwrapper() { + vncHooksGC->wrappedOps = pGC->ops; + pGC->funcs = oldFuncs; + pGC->ops = &vncHooksGCOps; + } + GCPtr pGC; + vncHooksGCPtr vncHooksGC; + GCFuncs* oldFuncs; + ScreenPtr pScreen; +}; + +#define GC_OP_UNWRAPPER(pDrawable, pGC, name) \ + GCOpUnwrapper u(pDrawable, pGC); \ + ScreenPtr pScreen = (pDrawable)->pScreen; \ + vncHooksScreenPtr vncHooksScreen \ + = ((vncHooksScreenPtr)pScreen->devPrivates[vncHooksScreenIndex].ptr); \ + DBGPRINT((stderr,"vncHooks" #name " called\n")); + + +// FillSpans - changed region is the whole of borderClip. This is pessimistic, +// but I believe this function is rarely used so it doesn't matter. + +static void vncHooksFillSpans(DrawablePtr pDrawable, GCPtr pGC, int nInit, + DDXPointPtr pptInit, int *pwidthInit, + int fSorted) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, FillSpans); + + RegionHelper changed(pScreen, &((WindowPtr)pDrawable)->borderClip); + + (*pGC->ops->FillSpans) (pDrawable, pGC, nInit, pptInit, pwidthInit, fSorted); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + +// SetSpans - changed region is the whole of borderClip. This is pessimistic, +// but I believe this function is rarely used so it doesn't matter. + +static void vncHooksSetSpans(DrawablePtr pDrawable, GCPtr pGC, char *psrc, + DDXPointPtr ppt, int *pwidth, int nspans, + int fSorted) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, SetSpans); + + RegionHelper changed(pScreen, &((WindowPtr)pDrawable)->borderClip); + + (*pGC->ops->SetSpans) (pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + +// PutImage - changed region is the given rectangle, clipped by pCompositeClip + +static void vncHooksPutImage(DrawablePtr pDrawable, GCPtr pGC, int depth, + int x, int y, int w, int h, int leftPad, + int format, char *pBits) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, PutImage); + + BoxRec box; + box.x1 = x + pDrawable->x; + box.y1 = y + pDrawable->y; + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + + RegionHelper changed(pScreen, &box, 0); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + (*pGC->ops->PutImage) (pDrawable, pGC, depth, x, y, w, h, leftPad, format, + pBits); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + +// CopyArea - destination of the copy is the dest rectangle, clipped by +// pCompositeClip. Any parts of the destination which cannot be copied from +// the source (could be all of it) go into the changed region. + +static RegionPtr vncHooksCopyArea(DrawablePtr pSrc, DrawablePtr pDst, + GCPtr pGC, int srcx, int srcy, int w, int h, + int dstx, int dsty) +{ + GC_OP_UNWRAPPER(pDst, pGC, CopyArea); + + BoxRec box; + box.x1 = dstx + pDst->x; + box.y1 = dsty + pDst->y; + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + + RegionHelper dst(pScreen, &box, 0); + REGION_INTERSECT(pScreen, dst.reg, dst.reg, COMPOSITE_CLIP(pGC)); + + RegionHelper src(pScreen); + + if ((pSrc->type == DRAWABLE_WINDOW) && (pSrc->pScreen == pScreen)) { + box.x1 = srcx + pSrc->x; + box.y1 = srcy + pSrc->y; + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + + src.init(&box, 0); + REGION_INTERSECT(pScreen, src.reg, src.reg, &((WindowPtr)pSrc)->clipList); + REGION_TRANSLATE(pScreen, src.reg, + dstx + pDst->x - srcx - pSrc->x, + dsty + pDst->y - srcy - pSrc->y); + } else { + src.init(NullBox, 0); + } + + RegionHelper changed(pScreen, NullBox, 0); + REGION_SUBTRACT(pScreen, changed.reg, dst.reg, src.reg); + REGION_INTERSECT(pScreen, dst.reg, dst.reg, src.reg); + + RegionPtr rgn = (*pGC->ops->CopyArea) (pSrc, pDst, pGC, srcx, srcy, w, h, + dstx, dsty); + + if (REGION_NOTEMPTY(pScreen, dst.reg)) + vncHooksScreen->desktop->add_copied(dst.reg, + dstx + pDst->x - srcx - pSrc->x, + dsty + pDst->y - srcy - pSrc->y); + + if (REGION_NOTEMPTY(pScreen, changed.reg)) + vncHooksScreen->desktop->add_changed(changed.reg); + + return rgn; +} + + +// CopyPlane - changed region is the destination rectangle, clipped by +// pCompositeClip + +static RegionPtr vncHooksCopyPlane(DrawablePtr pSrc, DrawablePtr pDst, + GCPtr pGC, int srcx, int srcy, int w, int h, + int dstx, int dsty, unsigned long plane) +{ + GC_OP_UNWRAPPER(pDst, pGC, CopyPlane); + + BoxRec box; + box.x1 = dstx + pDst->x; + box.y1 = dsty + pDst->y; + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + + RegionHelper changed(pScreen, &box, 0); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + RegionPtr rgn = (*pGC->ops->CopyPlane) (pSrc, pDst, pGC, srcx, srcy, w, h, + dstx, dsty, plane); + vncHooksScreen->desktop->add_changed(changed.reg); + + return rgn; +} + +// PolyPoint - changed region is the bounding rect, clipped by pCompositeClip + +static void vncHooksPolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode, + int npt, xPoint *pts) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, PolyPoint); + + if (npt == 0) { + (*pGC->ops->PolyPoint) (pDrawable, pGC, mode, npt, pts); + return; + } + + int minX = pts[0].x; + int maxX = pts[0].x; + int minY = pts[0].y; + int maxY = pts[0].y; + + if (mode == CoordModePrevious) { + int x = pts[0].x; + int y = pts[0].y; + + for (int i = 1; i < npt; i++) { + x += pts[i].x; + y += pts[i].y; + if (x < minX) minX = x; + if (x > maxX) maxX = x; + if (y < minY) minY = y; + if (y > maxY) maxY = y; + } + } else { + for (int i = 1; i < npt; i++) { + if (pts[i].x < minX) minX = pts[i].x; + if (pts[i].x > maxX) maxX = pts[i].x; + if (pts[i].y < minY) minY = pts[i].y; + if (pts[i].y > maxY) maxY = pts[i].y; + } + } + + BoxRec box; + box.x1 = minX + pDrawable->x; + box.y1 = minY + pDrawable->y; + box.x2 = maxX + 1 + pDrawable->x; + box.y2 = maxY + 1 + pDrawable->y; + + RegionHelper changed(pScreen, &box, 0); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + (*pGC->ops->PolyPoint) (pDrawable, pGC, mode, npt, pts); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + +// Polylines - changed region is the union of the bounding rects of each line, +// clipped by pCompositeClip. If there are more than MAX_RECTS_PER_OP lines, +// just use the bounding rect of all the lines. + +static void vncHooksPolylines(DrawablePtr pDrawable, GCPtr pGC, int mode, + int npt, DDXPointPtr ppts) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, Polylines); + + if (npt == 0) { + (*pGC->ops->Polylines) (pDrawable, pGC, mode, npt, ppts); + return; + } + + int nRegRects = npt - 1; + xRectangle regRects[MAX_RECTS_PER_OP]; + + int lw = pGC->lineWidth; + if (lw == 0) lw = 1; + + if (npt == 1) + { + // a single point + nRegRects = 1; + regRects[0].x = pDrawable->x + ppts[0].x - lw; + regRects[0].y = pDrawable->y + ppts[0].y - lw; + regRects[0].width = 2*lw; + regRects[0].height = 2*lw; + } + else + { + /* + * mitered joins can project quite a way from + * the line end; the 11 degree miter limit limits + * this extension to lw / (2 * tan(11/2)), rounded up + * and converted to int yields 6 * lw + */ + + int extra = lw / 2; + if (pGC->joinStyle == JoinMiter) { + extra = 6 * lw; + } + + int prevX, prevY, curX, curY; + int rectX1, rectY1, rectX2, rectY2; + int minX, minY, maxX, maxY; + + prevX = ppts[0].x + pDrawable->x; + prevY = ppts[0].y + pDrawable->y; + minX = maxX = prevX; + minY = maxY = prevY; + + for (int i = 0; i < nRegRects; i++) { + if (mode == CoordModeOrigin) { + curX = pDrawable->x + ppts[i+1].x; + curY = pDrawable->y + ppts[i+1].y; + } else { + curX = prevX + ppts[i+1].x; + curY = prevY + ppts[i+1].y; + } + + if (prevX > curX) { + rectX1 = curX - extra; + rectX2 = prevX + extra + 1; + } else { + rectX1 = prevX - extra; + rectX2 = curX + extra + 1; + } + + if (prevY > curY) { + rectY1 = curY - extra; + rectY2 = prevY + extra + 1; + } else { + rectY1 = prevY - extra; + rectY2 = curY + extra + 1; + } + + if (nRegRects <= MAX_RECTS_PER_OP) { + regRects[i].x = rectX1; + regRects[i].y = rectY1; + regRects[i].width = rectX2 - rectX1; + regRects[i].height = rectY2 - rectY1; + } else { + if (rectX1 < minX) minX = rectX1; + if (rectY1 < minY) minY = rectY1; + if (rectX2 > maxX) maxX = rectX2; + if (rectY2 > maxY) maxY = rectY2; + } + + prevX = curX; + prevY = curY; + } + + if (nRegRects > MAX_RECTS_PER_OP) { + regRects[0].x = minX; + regRects[0].y = minY; + regRects[0].width = maxX - minX; + regRects[0].height = maxY - minY; + nRegRects = 1; + } + } + + RegionHelper changed(pScreen, nRegRects, regRects); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + (*pGC->ops->Polylines) (pDrawable, pGC, mode, npt, ppts); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + +// PolySegment - changed region is the union of the bounding rects of each +// segment, clipped by pCompositeClip. If there are more than MAX_RECTS_PER_OP +// segments, just use the bounding rect of all the segments. + +static void vncHooksPolySegment(DrawablePtr pDrawable, GCPtr pGC, int nseg, + xSegment *segs) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, PolySegment); + + if (nseg == 0) { + (*pGC->ops->PolySegment) (pDrawable, pGC, nseg, segs); + return; + } + + xRectangle regRects[MAX_RECTS_PER_OP]; + int nRegRects = nseg; + + int lw = pGC->lineWidth; + int extra = lw / 2; + + int rectX1, rectY1, rectX2, rectY2; + int minX, minY, maxX, maxY; + + minX = maxX = segs[0].x1; + minY = maxY = segs[0].y1; + + for (int i = 0; i < nseg; i++) { + if (segs[i].x1 > segs[i].x2) { + rectX1 = pDrawable->x + segs[i].x2 - extra; + rectX2 = pDrawable->x + segs[i].x1 + extra + 1; + } else { + rectX1 = pDrawable->x + segs[i].x1 - extra; + rectX2 = pDrawable->x + segs[i].x2 + extra + 1; + } + + if (segs[i].y1 > segs[i].y2) { + rectY1 = pDrawable->y + segs[i].y2 - extra; + rectY2 = pDrawable->y + segs[i].y1 + extra + 1; + } else { + rectY1 = pDrawable->y + segs[i].y1 - extra; + rectY2 = pDrawable->y + segs[i].y2 + extra + 1; + } + + if (nseg <= MAX_RECTS_PER_OP) { + regRects[i].x = rectX1; + regRects[i].y = rectY1; + regRects[i].width = rectX2 - rectX1; + regRects[i].height = rectY2 - rectY1; + } else { + if (rectX1 < minX) minX = rectX1; + if (rectY1 < minY) minY = rectY1; + if (rectX2 > maxX) maxX = rectX2; + if (rectY2 > maxY) maxY = rectY2; + } + } + + if (nseg > MAX_RECTS_PER_OP) { + regRects[0].x = minX; + regRects[0].y = minY; + regRects[0].width = maxX - minX; + regRects[0].height = maxY - minY; + nRegRects = 1; + } + + RegionHelper changed(pScreen, nRegRects, regRects); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + (*pGC->ops->PolySegment) (pDrawable, pGC, nseg, segs); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + +// PolyRectangle - changed region is the union of the bounding rects around +// each side of the outline rectangles, clipped by pCompositeClip. If there +// are more than MAX_RECTS_PER_OP rectangles, just use the bounding rect of all +// the rectangles. + +static void vncHooksPolyRectangle(DrawablePtr pDrawable, GCPtr pGC, int nrects, + xRectangle *rects) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, PolyRectangle); + + if (nrects == 0) { + (*pGC->ops->PolyRectangle) (pDrawable, pGC, nrects, rects); + return; + } + + xRectangle regRects[MAX_RECTS_PER_OP*4]; + int nRegRects = nrects * 4; + + int lw = pGC->lineWidth; + int extra = lw / 2; + + int rectX1, rectY1, rectX2, rectY2; + int minX, minY, maxX, maxY; + + minX = maxX = rects[0].x; + minY = maxY = rects[0].y; + + for (int i = 0; i < nrects; i++) { + if (nrects <= MAX_RECTS_PER_OP) { + regRects[i*4].x = rects[i].x - extra + pDrawable->x; + regRects[i*4].y = rects[i].y - extra + pDrawable->y; + regRects[i*4].width = rects[i].width + 1 + 2 * extra; + regRects[i*4].height = 1 + 2 * extra; + + regRects[i*4+1].x = rects[i].x - extra + pDrawable->x; + regRects[i*4+1].y = rects[i].y - extra + pDrawable->y; + regRects[i*4+1].width = 1 + 2 * extra; + regRects[i*4+1].height = rects[i].height + 1 + 2 * extra; + + regRects[i*4+2].x = rects[i].x + rects[i].width - extra + pDrawable->x; + regRects[i*4+2].y = rects[i].y - extra + pDrawable->y; + regRects[i*4+2].width = 1 + 2 * extra; + regRects[i*4+2].height = rects[i].height + 1 + 2 * extra; + + regRects[i*4+3].x = rects[i].x - extra + pDrawable->x; + regRects[i*4+3].y = rects[i].y + rects[i].height - extra + pDrawable->y; + regRects[i*4+3].width = rects[i].width + 1 + 2 * extra; + regRects[i*4+3].height = 1 + 2 * extra; + } else { + rectX1 = pDrawable->x + rects[i].x - extra; + rectY1 = pDrawable->y + rects[i].y - extra; + rectX2 = pDrawable->x + rects[i].x + rects[i].width + extra+1; + rectY2 = pDrawable->y + rects[i].y + rects[i].height + extra+1; + if (rectX1 < minX) minX = rectX1; + if (rectY1 < minY) minY = rectY1; + if (rectX2 > maxX) maxX = rectX2; + if (rectY2 > maxY) maxY = rectY2; + } + } + + if (nrects > MAX_RECTS_PER_OP) { + regRects[0].x = minX; + regRects[0].y = minY; + regRects[0].width = maxX - minX; + regRects[0].height = maxY - minY; + nRegRects = 1; + } + + RegionHelper changed(pScreen, nRegRects, regRects); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + (*pGC->ops->PolyRectangle) (pDrawable, pGC, nrects, rects); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + +// PolyArc - changed region is the union of bounding rects around each arc, +// clipped by pCompositeClip. If there are more than MAX_RECTS_PER_OP +// arcs, just use the bounding rect of all the arcs. + +static void vncHooksPolyArc(DrawablePtr pDrawable, GCPtr pGC, int narcs, + xArc *arcs) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, PolyArc); + + if (narcs == 0) { + (*pGC->ops->PolyArc) (pDrawable, pGC, narcs, arcs); + return; + } + + xRectangle regRects[MAX_RECTS_PER_OP]; + int nRegRects = narcs; + + int lw = pGC->lineWidth; + if (lw == 0) lw = 1; + int extra = lw / 2; + + int rectX1, rectY1, rectX2, rectY2; + int minX, minY, maxX, maxY; + + minX = maxX = arcs[0].x; + minY = maxY = arcs[0].y; + + for (int i = 0; i < narcs; i++) { + if (narcs <= MAX_RECTS_PER_OP) { + regRects[i].x = arcs[i].x - extra + pDrawable->x; + regRects[i].y = arcs[i].y - extra + pDrawable->y; + regRects[i].width = arcs[i].width + lw; + regRects[i].height = arcs[i].height + lw; + } else { + rectX1 = pDrawable->x + arcs[i].x - extra; + rectY1 = pDrawable->y + arcs[i].y - extra; + rectX2 = pDrawable->x + arcs[i].x + arcs[i].width + lw; + rectY2 = pDrawable->y + arcs[i].y + arcs[i].height + lw; + if (rectX1 < minX) minX = rectX1; + if (rectY1 < minY) minY = rectY1; + if (rectX2 > maxX) maxX = rectX2; + if (rectY2 > maxY) maxY = rectY2; + } + } + + if (narcs > MAX_RECTS_PER_OP) { + regRects[0].x = minX; + regRects[0].y = minY; + regRects[0].width = maxX - minX; + regRects[0].height = maxY - minY; + nRegRects = 1; + } + + RegionHelper changed(pScreen, nRegRects, regRects); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + (*pGC->ops->PolyArc) (pDrawable, pGC, narcs, arcs); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + + +// FillPolygon - changed region is the bounding rect around the polygon, +// clipped by pCompositeClip + +static void vncHooksFillPolygon(DrawablePtr pDrawable, GCPtr pGC, int shape, + int mode, int count, DDXPointPtr pts) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, FillPolygon); + + if (count == 0) { + (*pGC->ops->FillPolygon) (pDrawable, pGC, shape, mode, count, pts); + return; + } + + int minX = pts[0].x; + int maxX = pts[0].x; + int minY = pts[0].y; + int maxY = pts[0].y; + + if (mode == CoordModePrevious) { + int x = pts[0].x; + int y = pts[0].y; + + for (int i = 1; i < count; i++) { + x += pts[i].x; + y += pts[i].y; + if (x < minX) minX = x; + if (x > maxX) maxX = x; + if (y < minY) minY = y; + if (y > maxY) maxY = y; + } + } else { + for (int i = 1; i < count; i++) { + if (pts[i].x < minX) minX = pts[i].x; + if (pts[i].x > maxX) maxX = pts[i].x; + if (pts[i].y < minY) minY = pts[i].y; + if (pts[i].y > maxY) maxY = pts[i].y; + } + } + + BoxRec box; + box.x1 = minX + pDrawable->x; + box.y1 = minY + pDrawable->y; + box.x2 = maxX + 1 + pDrawable->x; + box.y2 = maxY + 1 + pDrawable->y; + + RegionHelper changed(pScreen, &box, 0); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + (*pGC->ops->FillPolygon) (pDrawable, pGC, shape, mode, count, pts); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + +// PolyFillRect - changed region is the union of the rectangles, clipped by +// pCompositeClip. If there are more than MAX_RECTS_PER_OP rectangles, just +// use the bounding rect of all the rectangles. + +static void vncHooksPolyFillRect(DrawablePtr pDrawable, GCPtr pGC, int nrects, + xRectangle *rects) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, PolyFillRect); + + if (nrects == 0) { + (*pGC->ops->PolyFillRect) (pDrawable, pGC, nrects, rects); + return; + } + + xRectangle regRects[MAX_RECTS_PER_OP]; + int nRegRects = nrects; + int rectX1, rectY1, rectX2, rectY2; + int minX, minY, maxX, maxY; + minX = maxX = rects[0].x; + minY = maxY = rects[0].y; + + for (int i = 0; i < nrects; i++) { + if (nrects <= MAX_RECTS_PER_OP) { + regRects[i].x = rects[i].x + pDrawable->x; + regRects[i].y = rects[i].y + pDrawable->y; + regRects[i].width = rects[i].width; + regRects[i].height = rects[i].height; + } else { + rectX1 = pDrawable->x + rects[i].x; + rectY1 = pDrawable->y + rects[i].y; + rectX2 = pDrawable->x + rects[i].x + rects[i].width; + rectY2 = pDrawable->y + rects[i].y + rects[i].height; + if (rectX1 < minX) minX = rectX1; + if (rectY1 < minY) minY = rectY1; + if (rectX2 > maxX) maxX = rectX2; + if (rectY2 > maxY) maxY = rectY2; + } + } + + if (nrects > MAX_RECTS_PER_OP) { + regRects[0].x = minX; + regRects[0].y = minY; + regRects[0].width = maxX - minX; + regRects[0].height = maxY - minY; + nRegRects = 1; + } + + RegionHelper changed(pScreen, nRegRects, regRects); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + (*pGC->ops->PolyFillRect) (pDrawable, pGC, nrects, rects); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + +// PolyFillArc - changed region is the union of bounding rects around each arc, +// clipped by pCompositeClip. If there are more than MAX_RECTS_PER_OP arcs, +// just use the bounding rect of all the arcs. + +static void vncHooksPolyFillArc(DrawablePtr pDrawable, GCPtr pGC, int narcs, + xArc *arcs) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, PolyFillArc); + + if (narcs == 0) { + (*pGC->ops->PolyFillArc) (pDrawable, pGC, narcs, arcs); + return; + } + + xRectangle regRects[MAX_RECTS_PER_OP]; + int nRegRects = narcs; + + int lw = pGC->lineWidth; + if (lw == 0) lw = 1; + int extra = lw / 2; + + int rectX1, rectY1, rectX2, rectY2; + int minX, minY, maxX, maxY; + + minX = maxX = arcs[0].x; + minY = maxY = arcs[0].y; + + for (int i = 0; i < narcs; i++) { + if (narcs <= MAX_RECTS_PER_OP) { + regRects[i].x = arcs[i].x - extra + pDrawable->x; + regRects[i].y = arcs[i].y - extra + pDrawable->y; + regRects[i].width = arcs[i].width + lw; + regRects[i].height = arcs[i].height + lw; + } else { + rectX1 = pDrawable->x + arcs[i].x - extra; + rectY1 = pDrawable->y + arcs[i].y - extra; + rectX2 = pDrawable->x + arcs[i].x + arcs[i].width + lw; + rectY2 = pDrawable->y + arcs[i].y + arcs[i].height + lw; + if (rectX1 < minX) minX = rectX1; + if (rectY1 < minY) minY = rectY1; + if (rectX2 > maxX) maxX = rectX2; + if (rectY2 > maxY) maxY = rectY2; + } + } + + if (narcs > MAX_RECTS_PER_OP) { + regRects[0].x = minX; + regRects[0].y = minY; + regRects[0].width = maxX - minX; + regRects[0].height = maxY - minY; + nRegRects = 1; + } + + RegionHelper changed(pScreen, nRegRects, regRects); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + (*pGC->ops->PolyFillArc) (pDrawable, pGC, narcs, arcs); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + +// GetTextBoundingRect - calculate a bounding rectangle around n chars of a +// font. Not particularly accurate, but good enough. + +static void GetTextBoundingRect(DrawablePtr pDrawable, FontPtr font, int x, + int y, int nchars, BoxPtr box) +{ + int ascent = __rfbmax(FONTASCENT(font), FONTMAXBOUNDS(font, ascent)); + int descent = __rfbmax(FONTDESCENT(font), FONTMAXBOUNDS(font, descent)); + int charWidth = __rfbmax(FONTMAXBOUNDS(font,rightSideBearing), + FONTMAXBOUNDS(font,characterWidth)); + + box->x1 = pDrawable->x + x; + box->y1 = pDrawable->y + y - ascent; + box->x2 = box->x1 + charWidth * nchars; + box->y2 = box->y1 + ascent + descent; + + if (FONTMINBOUNDS(font,leftSideBearing) < 0) + box->x1 += FONTMINBOUNDS(font,leftSideBearing); +} + +// PolyText8 - changed region is bounding rect around count chars, clipped by +// pCompositeClip + +static int vncHooksPolyText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y, + int count, char *chars) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, PolyText8); + + if (count == 0) + return (*pGC->ops->PolyText8) (pDrawable, pGC, x, y, count, chars); + + BoxRec box; + GetTextBoundingRect(pDrawable, pGC->font, x, y, count, &box); + + RegionHelper changed(pScreen, &box, 0); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + int ret = (*pGC->ops->PolyText8) (pDrawable, pGC, x, y, count, chars); + + vncHooksScreen->desktop->add_changed(changed.reg); + + return ret; +} + +// PolyText16 - changed region is bounding rect around count chars, clipped by +// pCompositeClip + +static int vncHooksPolyText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y, + int count, unsigned short *chars) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, PolyText16); + + if (count == 0) + return (*pGC->ops->PolyText16) (pDrawable, pGC, x, y, count, chars); + + BoxRec box; + GetTextBoundingRect(pDrawable, pGC->font, x, y, count, &box); + + RegionHelper changed(pScreen, &box, 0); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + int ret = (*pGC->ops->PolyText16) (pDrawable, pGC, x, y, count, chars); + + vncHooksScreen->desktop->add_changed(changed.reg); + + return ret; +} + +// ImageText8 - changed region is bounding rect around count chars, clipped by +// pCompositeClip + +static void vncHooksImageText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y, + int count, char *chars) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, ImageText8); + + if (count == 0) { + (*pGC->ops->ImageText8) (pDrawable, pGC, x, y, count, chars); + return; + } + + BoxRec box; + GetTextBoundingRect(pDrawable, pGC->font, x, y, count, &box); + + RegionHelper changed(pScreen, &box, 0); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + (*pGC->ops->ImageText8) (pDrawable, pGC, x, y, count, chars); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + +// ImageText16 - changed region is bounding rect around count chars, clipped by +// pCompositeClip + +static void vncHooksImageText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y, + int count, unsigned short *chars) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, ImageText16); + + if (count == 0) { + (*pGC->ops->ImageText16) (pDrawable, pGC, x, y, count, chars); + return; + } + + BoxRec box; + GetTextBoundingRect(pDrawable, pGC->font, x, y, count, &box); + + RegionHelper changed(pScreen, &box, 0); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + (*pGC->ops->ImageText16) (pDrawable, pGC, x, y, count, chars); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + +// ImageGlyphBlt - changed region is bounding rect around nglyph chars, clipped +// by pCompositeClip + +static void vncHooksImageGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x, + int y, unsigned int nglyph, + CharInfoPtr *ppci, pointer pglyphBase) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, ImageGlyphBlt); + + if (nglyph == 0) { + (*pGC->ops->ImageGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci,pglyphBase); + return; + } + + BoxRec box; + GetTextBoundingRect(pDrawable, pGC->font, x, y, nglyph, &box); + + RegionHelper changed(pScreen, &box, 0); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + (*pGC->ops->ImageGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + +// PolyGlyphBlt - changed region is bounding rect around nglyph chars, clipped +// by pCompositeClip + +static void vncHooksPolyGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x, + int y, unsigned int nglyph, + CharInfoPtr *ppci, pointer pglyphBase) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, PolyGlyphBlt); + + if (nglyph == 0) { + (*pGC->ops->PolyGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci,pglyphBase); + return; + } + + BoxRec box; + GetTextBoundingRect(pDrawable, pGC->font, x, y, nglyph, &box); + + RegionHelper changed(pScreen, &box, 0); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + (*pGC->ops->PolyGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); + + vncHooksScreen->desktop->add_changed(changed.reg); +} + +// PushPixels - changed region is the given rectangle, clipped by +// pCompositeClip + +static void vncHooksPushPixels(GCPtr pGC, PixmapPtr pBitMap, + DrawablePtr pDrawable, int w, int h, int x, + int y) +{ + GC_OP_UNWRAPPER(pDrawable, pGC, PushPixels); + + BoxRec box; + box.x1 = x + pDrawable->x; + box.y1 = y + pDrawable->y; + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + + RegionHelper changed(pScreen, &box, 0); + + REGION_INTERSECT(pScreen, changed.reg, changed.reg, COMPOSITE_CLIP(pGC)); + + (*pGC->ops->PushPixels) (pGC, pBitMap, pDrawable, w, h, x, y); + + vncHooksScreen->desktop->add_changed(changed.reg); +} diff --git a/unix/xc/programs/Xserver/vnc/vncHooks.h b/unix/xc/programs/Xserver/vnc/vncHooks.h new file mode 100644 index 00000000..c556ef3a --- /dev/null +++ b/unix/xc/programs/Xserver/vnc/vncHooks.h @@ -0,0 +1,26 @@ +/* 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 __VNCHOOKS_H__ +#define __VNCHOOKS_H__ + +extern "C" { +#include <screenint.h> + extern Bool vncHooksInit(ScreenPtr pScreen, XserverDesktop* desktop); +} + +#endif diff --git a/unix/xc/programs/Xserver/vnc/xf86vncModule.cc b/unix/xc/programs/Xserver/vnc/xf86vncModule.cc new file mode 100644 index 00000000..ef8ea506 --- /dev/null +++ b/unix/xc/programs/Xserver/vnc/xf86vncModule.cc @@ -0,0 +1,97 @@ +/* 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. + */ +/* This is the xf86 module code for the vnc extension. + */ + +#include <rfb/Configuration.h> +#include <rfb/Logger_stdio.h> +#include <rfb/LogWriter.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" +#undef class +#undef private +#undef bool +#undef new + +using namespace rfb; + +extern void vncExtensionInit(); +static void vncExtensionInitWithParams(INITARGS); + +#ifdef XFree86LOADER + +static MODULESETUPPROTO(vncSetup); + +ExtensionModule vncExt = +{ + vncExtensionInitWithParams, + "VNC", + NULL, + NULL, + NULL +}; + +static XF86ModuleVersionInfo vncVersRec = +{ + "vnc", + "Constantin Kaplinsky", + MODINFOSTRING1, + MODINFOSTRING2, + XF86_VERSION_CURRENT, + 1, 0, 0, + ABI_CLASS_EXTENSION, /* needs the server extension ABI */ + ABI_EXTENSION_VERSION, + MOD_CLASS_EXTENSION, + {0,0,0,0} +}; + +XF86ModuleData vncModuleData = { &vncVersRec, vncSetup, NULL }; + +static pointer +vncSetup(pointer module, pointer opts, int *errmaj, int *errmin) { + LoadExtension(&vncExt, FALSE); + /* Need a non-NULL return value to indicate success */ + return (pointer)1; +} + +static void vncExtensionInitWithParams(INITARGS) +{ + rfb::initStdIOLoggers(); + rfb::LogWriter::setLogParams("*:stderr:30"); + + for (int scr = 0; scr < screenInfo.numScreens; scr++) { + ScrnInfoPtr pScrn = xf86Screens[scr]; + + for (ParameterIterator i(Configuration::global()); i.param; i.next()) { + char* val = xf86FindOptionValue(pScrn->options, i.param->getName()); + if (val) + i.param->setParam(val); + } + } + + vncExtensionInit(); +} + +#endif /* XFree86LOADER */ +} |