diff options
author | runge <runge> | 2004-12-17 03:55:35 +0000 |
---|---|---|
committer | runge <runge> | 2004-12-17 03:55:35 +0000 |
commit | 3a84b0ccc87a023be3ac84fba82895fafade30c1 (patch) | |
tree | 054ec71b3ad716623433ed8b8e39da417afb71fe /x11vnc | |
parent | 7e13b8a594805e25e928b8bcac394eef0a1ee5c8 (diff) | |
download | libvncserver-3a84b0ccc87a023be3ac84fba82895fafade30c1.zip libvncserver-3a84b0ccc87a023be3ac84fba82895fafade30c1.tar.gz |
x11vnc: XFIXES cursorshape, XRANDR resize, remote control, gui
Diffstat (limited to 'x11vnc')
-rw-r--r-- | x11vnc/ChangeLog | 17 | ||||
-rw-r--r-- | x11vnc/README | 1526 | ||||
-rwxr-xr-x | x11vnc/tkx11vnc | 2187 | ||||
-rw-r--r-- | x11vnc/tkx11vnc.h | 2194 | ||||
-rw-r--r-- | x11vnc/x11vnc.1 | 812 | ||||
-rw-r--r-- | x11vnc/x11vnc.c | 6876 |
6 files changed, 11934 insertions, 1678 deletions
diff --git a/x11vnc/ChangeLog b/x11vnc/ChangeLog index 12fb239..71a4007 100644 --- a/x11vnc/ChangeLog +++ b/x11vnc/ChangeLog @@ -1,3 +1,19 @@ +2004-12-16 Karl Runge <runge@karlrunge.com> + * support for XFIXES extension to show the exact cursor shape, + working on Linux/Xorg and Solaris 10. disable with -noxfixes + * remote control mania - nearly everything can be changed dynamically! + see the -remote/-query (aka -R/-Q) options. e.g. -R scale:5/6 + * simple gui tkx11vnc based on the remote control mechanism, see -gui + * support for XRANDR extension, if the X screen changes size (see + xrandr(1)), x11vnc will resize the fb. Pays to have NewFBSize viewer + * -overlay support on IRIX with XReadDisplay (not tested). + * RFB_MODE is set to "accept" or "gone" in environment + * "-id pick" will let you pick the window (calls xwininfo(1)...) + * "-pointer_mode n" replaces -old_pointer (n=1) and -old_pointer2 (n=2) + a new mode n=3 is added (similary to nodragging, but dynamic). + * "-sb n" screen blank timeout option is now documented. + * renamed NON_CVS to OLD_TREE + 2004-08-31 Karl Runge <runge@karlrunge.com> * new check_user_input() pointer input algorithm, it tries to avoid extra-draws. still needs tuning, get previous one with -old_pointer2 @@ -20,7 +36,6 @@ * fix misc bugs: missing var types, hardwired blackouts sizes, subwin desktop name crash. - 2004-08-03 Karl Runge <runge@karlrunge.com> * add man page x11vnc.1 autogenerated from x11vnc -help; tweak help output a little bit. Adjust autoconf to pick up manpage. diff --git a/x11vnc/README b/x11vnc/README index f3ed566..efe43d5 100644 --- a/x11vnc/README +++ b/x11vnc/README @@ -1,5 +1,5 @@ -x11vnc README file Date: Tue Aug 31 22:39:00 EDT 2004 +x11vnc README file Date: Thu Dec 16 23:32:54 EST 2004 The following information is taken from these URLs: @@ -26,18 +26,21 @@ x11vnc: a VNC server for real X displays (to [1]FAQ) (to [2]downloads) problems centered around esoteric C++ toolkits. x11vnc is written in plain C and uses only standard libraries. I also added a few enhancements to improve the interactive response, add esoteric - features, etc. + features, etc. The [4]FAQ contains a lot of information and solutions + to problems, but please feel free to [5]contact me if you have + problems or questions. Background: - VNC is a very useful network graphics protocol in the spirit of X, - however, unlike X, the viewing-end is very simple and maintains no - state. It is a remote framebuffer (RFB) protocol . + VNC (Virtual Network Computing) is a very useful network graphics + protocol in the spirit of X, however, unlike X, the viewing-end is + very simple and maintains no state. It is a remote framebuffer (RFB) + protocol Some VNC links: - * [4]http://www.uk.research.att.com/vnc/ - * [5]http://www.realvnc.com - * [6]http://www.tightvnc.com + * [6]http://www.uk.research.att.com/vnc/ + * [7]http://www.realvnc.com + * [8]http://www.tightvnc.com For Unix, the VNC implementation includes a virtual X11 server Xvnc (usually launched via the vncserver command) that is not associated @@ -71,12 +74,13 @@ x11vnc: a VNC server for real X displays (to [1]FAQ) (to [2]downloads) How to use x11vnc: In this example let's assume the remote machine with the X display you - wish to view is far-away.east:0 and the workstation you are presently - working at is sitting-here.west. + wish to view is "far-away.east:0" and the workstation you are + presently working at is "sitting-here.west". - Step 0. Download x11vnc ([7]see below) and have it available to run - (e.g. via PATH) on far-away.east. Similarly, have a VNC viewer (e.g. - vncviewer) ready to run on sitting-here.west. + Step 0. Download x11vnc ([9]see below) and have it available to run + (e.g. via $PATH) on far-away.east. Similarly, have a VNC viewer (e.g. + vncviewer) ready to run on sitting-here.west. We recommend + [10]TightVNC Viewers. Step 1. By some means log in to far-away.east and get a command shell running there. You can use ssh, rlogin, telnet, or any other method to @@ -85,14 +89,20 @@ x11vnc: a VNC server for real X displays (to [1]FAQ) (to [2]downloads) the X11 framebuffer). Step 2. In that far-away.east shell (with command prompt "far-away>" - in this example) run x11vnc directed at the far-away.east X session: + in this example) run x11vnc directed at the far-away.east X session + display: far-away> x11vnc -display :0 - You could have also set the environment variable DISPLAY=:0 to achieve - the same thing. This step attaches x11vnc to the far-away.east:0 X + You could have also set the environment variable DISPLAY=:0 instead of + using -display. This step attaches x11vnc to the far-away.east:0 X display (no viewer clients yet). + To get X11 permissions right, you may also need to set the XAUTHORITY + environment variable (or use the -auth option) to point to the correct + MIT-MAGIC-COOKIE file (e.g. /home/joe/.Xauthority). More on this + [11]below. + There will then be much chatter printed out from x11vnc, until it finally says something like: . @@ -108,9 +118,9 @@ x11vnc: a VNC server for real X displays (to [1]FAQ) (to [2]downloads) example) you now want to run a VNC viewer program. There are VNC viewers for Unix, Windows, MacOS, Java-enabled web browsers, and even for PDA's like the Palm Pilot! You can use any of them to connect to - x11vnc (see the above VNC links on how to obtain a viewer for your - platform. For Solaris, vncviewer is available in the [8]Companion CD - package SFWvnc ). + x11vnc (see the above VNC links under "Background:" on how to obtain a + viewer for your platform or [12]this FAQ. For Solaris, vncviewer is + available in the [13]Companion CD package SFWvnc ). In this example we'll use the Unix vncviewer program on sitting-here by typing the following command in a second terminal window: @@ -120,15 +130,15 @@ x11vnc: a VNC server for real X displays (to [1]FAQ) (to [2]downloads) That should pop up a viewer window on sitting-here.west showing and allowing interaction with the far-away.east:0 X11 desktop. Pretty nifty! When finished, exit the viewer: the remote x11vnc process will - shutdown automatically (or you can use the -forever [9]option to have + shutdown automatically (or you can use the -forever [14]option to have it wait for additional viewer connections). Desktop Sharing: The above more or less assumed nobody was sitting at - the workstation display far-away.east:0. This is often the case: a + the workstation display "far-away.east:0". This is often the case: a user wants to access her workstation remotely. Another usage pattern - has the user sitting at far-away.east:0 and invites one or more other - people to view and interact with his desktop. Perhaps the user gives a - demo or presentation this way (using the telephone for vocal + has the user sitting at "far-away.east:0" and invites one or more + other people to view and interact with his desktop. Perhaps the user + gives a demo or presentation this way (using the telephone for vocal communication). A "Remote Help Desk" mode would be similar: a technician remotely connects to the user's desktop to interactively solve a problem the user is having. @@ -137,7 +147,7 @@ x11vnc: a VNC server for real X displays (to [1]FAQ) (to [2]downloads) will work, but more easily the user sitting at far-away.east:0 simply starts up x11vnc from a terminal window, after which the guests would start their VNC viewers. For this usage mode the -accept popup option - discussed in the [10]FAQ below may be of use to allow the user at + discussed in the [15]FAQ below may be of use to allow the user at far-away.east:0 to accept or reject incoming connections. _________________________________________________________________ @@ -152,7 +162,7 @@ x11vnc: a VNC server for real X displays (to [1]FAQ) (to [2]downloads) (you will likely have to provide passwords/passphrases for the ssh login) and then in another terminal window on sitting-here run the command: - sitting-here> vncviewer -encodings "copyrect tight hextile" localhost:0 + sitting-here> vncviewer -encodings "copyrect tight zrle hextile" localhost:0 The -encodings option is very important: vncviewer will default to "raw" encoding if it thinks the connection is to the local machine, @@ -164,15 +174,16 @@ x11vnc: a VNC server for real X displays (to [1]FAQ) (to [2]downloads) you wish to view (e.g. your company provides incoming SSH access to a gateway machine), then you need to change the above to, e.g.: -L 5900:otherhost:5900. Once logged in, you'll need to do a second login - (ssh or rsh) to the workstation machine 'otherhost' and then start up - x11vnc on it. - - As discussed below, there may be some problems with port 5900 being - available. If that happens, the above port and display numbers may - change a bit. However, if you "know" port 5900 will be free on the - local and remote machines, you can automate the above two steps by - using the x11vnc option -bg (forks into background after connection to - the display is set up) or using the -f option of ssh. A simple example + (ssh, rsh, etc.) to the workstation machine 'otherhost' and then start + up x11vnc on it. + + Scripts to automate tunneling: As discussed below, there may be some + problems with port 5900 being available. If that happens, the above + port and display numbers may change a bit (e.g. -> 5901 and :1). + However, if you "know" port 5900 will be free on the local and remote + machines, you can easily automate the above two steps by using the + x11vnc option -bg (forks into background after connection to the + display is set up) or using the -f option of ssh. A simple example script, assuming no problems with port 5900 being taken on the local or remote sides, looks like: #!/bin/sh @@ -183,8 +194,8 @@ host=`echo $1 | awk -F: '{print $1}'` disp=`echo $1 | awk -F: '{print $2}'` if [ "x$disp" = "x" ]; then disp=0; fi -cmd="x11vnc -display :$disp -rfbauth .vnc/passwd" -enc="copyrect tight hextile zlib corre rre raw" +cmd="x11vnc -display :$disp -localhost -rfbauth .vnc/passwd" +enc="copyrect tight zrle hextile zlib corre rre raw" ssh -f -L 5900:localhost:5900 $host "$cmd" @@ -197,7 +208,7 @@ done See also rx11vnc.pl below. Another method is to start the VNC viewer in listen mode "vncviewer - -listen" and have x11vnc initiate the reverse connection using the + -listen" and have x11vnc initiate a reverse connection using the -connect option: #!/bin/sh # usage: x11vnc_ssh <host>:<xdisplay> @@ -207,8 +218,9 @@ host=`echo $1 | awk -F: '{print $1}'` disp=`echo $1 | awk -F: '{print $2}'` if [ "x$disp" = "x" ]; then disp=0; fi -cmd="x11vnc -display :$disp -connect localhost" # <-- new option -enc="copyrect tight hextile zlib corre rre raw" +cmd="x11vnc -display :$disp -localhost -connect localhost" # <-- note new opt +ion +enc="copyrect tight zrle hextile zlib corre rre raw" vncviewer -encodings "$enc" -listen & pid=$! @@ -227,8 +239,8 @@ host=`echo $1 | awk -F: '{print $1}'` disp=`echo $1 | awk -F: '{print $2}'` if [ "x$disp" = "x" ]; then disp=0; fi -VNC_VIA_CMD="ssh -f -L %L:%H:%R %G x11vnc -rfbport 5900 -display :$disp; sleep -5" +VNC_VIA_CMD="ssh -f -L %L:%H:%R %G x11vnc -localhost -rfbport 5900 -display :$d +isp; sleep 5" export VNC_VIA_CMD vncviewer -via $host localhost:0 # must be TightVNC vncviewer. @@ -236,36 +248,47 @@ vncviewer -via $host localhost:0 # must be TightVNC vncviewer. Of course if you already have the x11vnc running waiting for connections (or have it started out of inetd(1)), you can simply use the TightVNC vncviewer -via gateway host:port in its default mode to - provide secure ssh tunneling. + provide secure ssh tunnelling. VNC password file: Also note in the first example script that the option "-rfbauth .vnc/passwd" provides additional protection by requiring a VNC password for every VNC viewer that connects. The - vncpasswd or storepasswd programs, or the x11vnc [11]-storepasswd + vncpasswd or storepasswd programs, or the x11vnc [16]-storepasswd option can be used to create the password file. x11vnc also has the - slightly less secure [12]-passwdfile and -passwd XXXXX options. - - It is up to you to tell x11vnc to use password protection, it will not - do it for you automatically. The same goes for encrypting the channel - between the viewer and x11vnc: it is up to you to use ssh, etc. + slightly less secure [17]-passwdfile and "-passwd XXXXX" options. + + Important: It is up to you to tell x11vnc to use password protection, + it will not do it for you automatically. The same goes for encrypting + the channel between the viewer and x11vnc: it is up to you to use ssh, + stunnel, VPN, etc. Also look into the -allow and -localhost + [18]options and building x11vnc with [19]tcp_wrappers support to limit + host access. _________________________________________________________________ Downloading x11vnc: - x11vnc is a contributed program to the [13]libvncserver project at + x11vnc is a contributed program to the [20]libvncserver project at SourceForge.net. I use libvncserver for all of the VNC aspects; I couldn't have done without it. The full source code may be found and downloaded (either file-release tarball or CVS tree) from the above - link. As of Aug 2004, the [14]x11vnc 0.6.2 source package is released - (recommended download) . The x11vnc package is the subset of the - libvncserver package needed to build the x11vnc program. Also, you can - get a copy of my latest, bleeding edge [15]x11vnc.c file to replace - the one in the above packages or the one in the CVS tree and then - rebuild. - - See the [16]FAQ below for information about where you might obtain a + link. As of Aug 2004, the [21]x11vnc-0.6.2.tar.gz source package is + released (recommended download) . The x11vnc package is the subset of + the libvncserver package needed to build the x11vnc program. Please do + not use the LibVNCServer-0.6 tarball: it contains an older, more buggy + version of x11vnc (Oct 2003) that you likely want to avoid. Also, you + can get a copy of my latest, bleeding edge [22]x11vnc.c file to + replace the one in the above packages or the one in the CVS tree and + then rebuild. + + See the [23]FAQ below for information about where you might obtain a precompiled x11vnc binary from 3rd parties. + To obtain VNC viewers for the viewing side (Windows, Mac OS, or Unix) + try here: + * [24]http://www.tightvnc.com/download.html + * [25]http://www.realvnc.com/download-free.html + * [26]http://sourceforge.net/projects/cotvnc/ + More tools: Here is a rsh/ssh wrapper script rx11vnc that attempts to automatically do the above Steps 1-3 for you (provided you have rsh/ssh login permission on the machine x11vnc is to be run on). The @@ -274,8 +297,8 @@ vncviewer -via $host localhost:0 # must be TightVNC vncviewer. that attempts to tunnel the vnc traffic through an ssh port redirection (and does not assume port 5900 is free). Have a look at them to see what they do and customize as needed: - * [17]rx11vnc wrapper script - * [18]rx11vnc.pl wrapper script to tunnel traffic thru ssh + * [27]rx11vnc wrapper script + * [28]rx11vnc.pl wrapper script to tunnel traffic thru ssh _________________________________________________________________ Building x11vnc: @@ -309,17 +332,17 @@ vncviewer -via $host localhost:0 # must be TightVNC vncviewer. Unix OS), the jpeg and/or zlib libraries may be in non-standard places (e.g. /usr/local, /usr/sfw, /opt/sfw, etc). - Note: If configure cannot find these two libraries then TightVNC - support will be disabled, and you don't want that! (the TightVNC - encoding gives very good compression and performance, even makes a - difference over a LAN) + Note: If configure cannot find these two libraries then TightVNC and + ZRLE encoding support will be disabled, and you don't want that! (the + TightVNC encoding gives very good compression and performance, it even + makes a noticable difference over a fast LAN) libjpeg is included in Solaris 9 and later (/usr/sfw/include and /usr/sfw/lib), and zlib in Solaris 8 and later (/usr/include and /usr/lib). To get the source for these libraries: libjpeg is available - at [19]ftp://ftp.uu.net/graphics/jpeg/ and zlib at - [20]http://www.gzip.org/zlib/. See also - [21]http://www.sunfreeware.com/ for Solaris binary packages of these + at [29]ftp://ftp.uu.net/graphics/jpeg/ and zlib at + [30]http://www.gzip.org/zlib/. See also + [31]http://www.sunfreeware.com/ for Solaris binary packages of these libraries. Here is a build script that indicates one way to pass the library @@ -331,8 +354,8 @@ vncviewer -via $host localhost:0 # must be TightVNC vncviewer. PATH=/path/to/gcc/bin:/usr/ccs/bin:$PATH # set to get gcc -JPEG=/path/to/jpeg # maybe "/usr/local" or "/opt/sfw" -ZLIB=/path/to/zlib # maybe "/usr/local" or "/opt/sfw" +JPEG=/path/to/jpeg # maybe "/usr/local", "/usr/sfw", or "/opt/sfw" +ZLIB=/path/to/zlib # maybe "/usr/local", "/usr/sfw", or "/opt/sfw" # Below we assume headers in $JPEG/include and $ZLIB/include and the # shared libraries are in $JPEG/lib and $ZLIB/lib. If your situation @@ -378,6 +401,24 @@ ls -l ./x11vnc/x11vnc LIBVNCSERVER_HAVE_XKEYBOARD after the rfb/rfb.h include in the x11vnc/x11vnc.c file. (This problem has been fixed as of x11vnc 0.6.2 (Aug/2004)) + + If you need to build on Solaris 2.5.1 or earlier, see [32]this + workaround FAQ. + + Building on HP-UX: For jpeg and zlib you will need to do the same + sort of thing as described above for Solaris. You set CPPFLAGS and + LDFLAGS to find them. You do not need to do any of the above + /usr/openwin stuff. Also, HP-UX does not seem to support -R, so get + rid of the -R items in LDFLAGS. Because of this, at runtime you may + need to set LD_LIBRARY_PATH to indicate the directory paths so the + libraries can be found. + + Finally, there seems to be a bug with gcc on HP-UX 11.xx: something + fails (in the gcc private header files?) and it thinks it cannot find + gettimeofday(). As a workaround add this to CPPFLAGS: + -DLIBVNCSERVER_HAVE_GETTIMEOFDAY. You may get some warnings but we + have verified that this generates working x11vnc binaries on HP-UX + hppa and ia64. _________________________________________________________________ Some Notes: @@ -386,11 +427,13 @@ ls -l ./x11vnc/x11vnc always a good idea to have a solid background color instead of a pretty background image. Each and every re-exposure of the background must be resent over the network: better to have that background be a - solid color that compresses very well compared to a photo image. I - suggest using xsetroot, dtstyle or similar utility to set a solid - background while using x11vnc. + solid color that compresses very well compared to a photo image. (This + is one place where the X protocol has an advantage over the VNC + protocol.) I suggest using xsetroot, dtstyle or similar utility to set + a solid background while using x11vnc. You can turn the pretty + background image back on when you are using the display directly. - I also find the [22]tightvnc encoding gives the best response for my + I also find the [33]tightvnc encoding gives the best response for my usage (Unix <-> Unix over cable modem). One needs a tightvnc-aware vncviewer to take advantage of this encoding. @@ -402,14 +445,14 @@ ls -l ./x11vnc/x11vnc is X11's default listening port). Had port 5900 been taken by some other application, x11vnc would have next tried 5901. That would mean the viewer command above should be changed to vncviewer - far-away.east:1. You can force the issue with the -rfbport NNNN - option. + far-away.east:1. You can force the issue with the "-rfbport NNNN" + option where NNNN is the desired port number. Options: x11vnc has (far too) many features that may be activated - via its [23]command line options. Useful options are -nap to use fewer + via its [34]command line options. Useful options are -nap to use fewer resources (it sleeps more between polls when activity is low) and -rfbauth passwd-file to use VNC password protection (the vncpasswd or - storepasswd programs, or the x11vnc [24]-storepasswd option can be + storepasswd programs, or the x11vnc [35]-storepasswd option can be used to create the password file). Algorithm: How does x11vnc do it? Rather brute-forcedly: it @@ -432,14 +475,14 @@ ls -l ./x11vnc/x11vnc first testing out the programs. You get an interesting "feedback" effect where vncviewer images keep popping up each one contained in the previous one and slightly shifted a bit by the window manager - decorations. There will be an even more interesting effect if -scale - is used. Also, if the XKEYBOARD is supported and the XBell "beeps" - once, you get an infinite loop of beeps going off. Although all of - this is mildly exciting it is not much use: you will normally run and - display the viewer on a different machine! + decorations. There will be an [36]even more interesting effect if + -scale is used. Also, if the XKEYBOARD is supported and the XBell + "beeps" once, you get an infinite loop of beeps going off. Although + all of this is mildly exciting it is not much use: you will normally + run and display the viewer on a different machine! SunRay notes: You can run x11vnc on your (connected or disconnected) - [25]SunRay session (Please remember to use -nap and maybe -wait 200 to + [37]SunRay session (Please remember to use -nap and maybe -wait 200 to avoid being a resource hog! It also helps a bit to have a solid background color). You have to know the name of the machine your SunRay session X server is running on. You also need to know the X11 @@ -451,7 +494,7 @@ ls -l ./x11vnc/x11vnc server machine has your session, you could login to all possible ones looking at the who output for your username...). - SunRay Gotcha: Note that even though your SunRay X11 DISPLAY is + SunRay Gotcha #1: Note that even though your SunRay X11 DISPLAY is something like :137, x11vnc still tries for port 5900 as its listening if it can get it, in which case the VNC display (i.e. the information you supply to the VNC viewer) is something like sunray-server:0 @@ -490,7 +533,8 @@ ls -l ./x11vnc/x11vnc of the /etc/X11/XF86Config file. Note that this disables 2D acceleration at the physical display and so likely defeats the purpose. Nevertheless this could be handy in some circumstances, - e.g. if the speed at the physical display was tolerable. + e.g. if the slower speed at the physical display was acceptable + (this seems to be true for most video cards these days). Unfortunately it does not seem shadowfb can be turned on and off dynamically... * Somewhat surprisingly, the X11 mouse (cursor) shape is write-only @@ -510,191 +554,202 @@ ls -l ./x11vnc/x11vnc Evidently a timing related bug and difficult to reproduce... * Using -threads can expose some bugs in libvncserver. - Please feel free to [26]contact me if you have any questions, + Please feel free to [38]contact me if you have any questions, problems, or comments about x11vnc, etc. _________________________________________________________________ x11vnc FAQ: - [27]Q-1: I can't get x11vnc to start up. It says "XOpenDisplay failed + [39]Q-1: I can't get x11vnc to start up. It says "XOpenDisplay failed ((null))" or "Xlib: connection to ":0.0" refused by server". What do I need to do? - [28]Q-2: I can't get x11vnc and/or libvncserver to compile. + [40]Q-2: I can't get x11vnc and/or libvncserver to compile. + + [41]Q-3: Help, I need to run x11vnc on Solaris 2.5.1 and it doesn't + compile! If I try to run a binary built on Solaris 2.6 I get: + relocation error: file x11vnc: symbol XConvertCase: referenced symbol + not found - [29]Q-3: Where can I get a precompiled x11vnc binary for my Operating + [42]Q-4: Where can I get a precompiled x11vnc binary for my Operating System? - [30]Q-4: How can I see all of x11vnc's command line options and + [43]Q-5: Where can I get a VNC Viewer binary (or source code) for the + Operating System I will be viewing from? + + [44]Q-6: How can I see all of x11vnc's command line options and documentation on how to use them? - [31]Q-5: I don't like typing arcane command line options every time I + [45]Q-7: I don't like typing arcane command line options every time I start x11vnc. What can I do? Is there a config file? - [32]Q-6: Why does x11vnc exit as soon as the VNC viewer disconnects? + [46]Q-8: Why does x11vnc exit as soon as the VNC viewer disconnects? And why doesn't it allow more than one VNC viewer to connect at the same time? - [33]Q-7: I have two separate machine displays in front of me, one + [47]Q-9: I have two separate machine displays in front of me, one Windows the other X11: can I use x11vnc in combination with Win2VNC in dual-screen mode to pass the keystrokes and mouse motions to the X11 display? - [34]Q-8: I am running Win2VNC on my windows machine and trying to - create a dual-screen mode with my second display by running x11vnc - -nofb. Whenever I initiate the connection Win2VNC quickly disconnects + [48]Q-10: I am running Win2VNC on my windows machine and trying to + create a dual-screen mode with my second display by running "x11vnc + -nofb". Whenever I initiate the connection Win2VNC quickly disconnects and x11vnc says something like: rfbProcessClientNormalMessage: read: Connection reset by peer - [35]Q-9: The X display I run x11vnc on is only 8 bits per pixel (bpp) + [49]Q-11: The X display I run x11vnc on is only 8 bits per pixel (bpp) PseudoColor (i.e. only 256 distinct colors). The x11vnc colors may start out OK, but after a while the colors are incorrect in certain windows. - [36]Q-10: Color problems: Why are the colors for some windows messed + [50]Q-12: Color problems: Why are the colors for some windows messed up in x11vnc? BTW, I have an X display that has nice overlay/multi-depth visuals of different color depths: e.g. there are both depth 8 and 24 visuals available at the same time. - [37]Q-11: How do I figure out the window id to supply to the -id + [51]Q-13: How do I figure out the window id to supply to the -id windowid option? - [38]Q-12: Why don't menus or other transient windows come up when I am + [52]Q-14: Why don't menus or other transient windows come up when I am using the -id windowid option to view a single application window? - [39]Q-13: Can I use x11vnc to view and interact with an Xterminal + [53]Q-15: Can I use x11vnc to view and interact with an Xterminal (e.g. NCD) that is not running UNIX and so x11vnc cannot be run on it directly? - [40]Q-14: Can I make x11vnc more quiet and also go into the background + [54]Q-16: Can I make x11vnc more quiet and also go into the background after starting up? - [41]Q-15: Can I limit which machines incoming VNC clients can connect + [55]Q-17: How do I stop x11vnc once it is running in the background? + + [56]Q-18: Can I limit which machines incoming VNC clients can connect from? - [42]Q-16: How do I build x11vnc/libvncserver with libwrap + [57]Q-19: How do I build x11vnc/libvncserver with libwrap (tcp_wrappers) support? - [43]Q-17: Can I prompt the user at the local X display whether the + [58]Q-20: Can I prompt the user at the local X display whether the incoming VNC client should be accepted or not? Can I decide to make some clients view-only? How about running an arbitrary program to make the decisions? - [44]Q-18: How do I create a VNC password for use with x11vnc? + [59]Q-21: How do I create a VNC password for use with x11vnc? - [45]Q-19: How can I tunnel my connection to x11vnc via an encrypted + [60]Q-22: How can I tunnel my connection to x11vnc via an encrypted SSH channel between two Unix machines? - [46]Q-20: How can I tunnel my connection to x11vnc via an encrypted + [61]Q-23: How can I tunnel my connection to x11vnc via an encrypted SSH channel from Windows using an SSH client like Putty? - [47]Q-21: Does x11vnc support Unix usernames and passwords? Can I + [62]Q-24: Does x11vnc support Unix usernames and passwords? Can I further limit the set of Unix usernames who can connect to the VNC desktop? - [48]Q-22: Can I have two passwords for VNC viewers, one for full + [63]Q-25: Can I have two passwords for VNC viewers, one for full access and the other for view-only access to the display? - [49]Q-23: I use a screen-lock when I leave my workstation (e.g. + [64]Q-26: I use a screen-lock when I leave my workstation (e.g. xscreensaver or xlock). When I remotely access my workstation desktop via x11vnc I can unlock the desktop fine, but I am worried people will see my activities on the physical monitor. What can I do to prevent this, or at least make it more difficult? - [50]Q-24: Can I have x11vnc automatically lock the screen when I + [65]Q-27: Can I have x11vnc automatically lock the screen when I disconnect the VNC viewer? - [51]Q-25: Are reverse connections (i.e. the VNC server connecting to + [66]Q-28: Are reverse connections (i.e. the VNC server connecting to the VNC viewer) using "vncviewer -listen" and vncconnect(1) supported? - [52]Q-26: Sometimes when a VNC viewer dies abruptly, x11vnc also dies + [67]Q-29: Sometimes when a VNC viewer dies abruptly, x11vnc also dies with the error message like: "Broken pipe". I'm using the -forever mode and I want x11vnc to keep running. - [53]Q-27: How can I use x11vnc to connect to an X login screen like - xdm, GNOME gdm, KDE kdm, or CDE dtlogin? + [68]Q-30: How can I use x11vnc to connect to an X login screen like + xdm, GNOME gdm, KDE kdm, or CDE dtlogin? (i.e. nobody is logged into + an X session yet). - [54]Q-28: Can I run x11vnc out of inetd(1)? + [69]Q-31: Can I run x11vnc out of inetd(1)? - [55]Q-29: How do I make x11vnc work with the Java VNC viewer applet in + [70]Q-32: How do I make x11vnc work with the Java VNC viewer applet in a web browser? - [56]Q-30: Why isn't the mouse cursor shape (the little icon shape + [71]Q-33: Why isn't the mouse cursor shape (the little icon shape where the mouse pointer is) correct as I move from window to window? - [57]Q-31: Why does the mouse arrow just stay in one corner in my + [72]Q-34: Why does the mouse arrow just stay in one corner in my vncviewer, whereas my cursor (that does move) is just a dot? - [58]Q-32: Can I take advantage of the TightVNC extension to the VNC + [73]Q-35: Can I take advantage of the TightVNC extension to the VNC protocol where Cursor Positions Updates are sent back to all connected clients (i.e. passive viewers can see the mouse cursor being moved around by another viewer)? - [59]Q-33: Is it possible to swap the mouse buttons (e.g. left-handed + [74]Q-36: Is it possible to swap the mouse buttons (e.g. left-handed operation), or arbitrarily remap them? How about mapping button clicks to keystrokes, e.g. to partially emulate Mouse wheel scrolling? - [60]Q-34: When I drag windows around with the mouse or scroll up and + [75]Q-37: When I drag windows around with the mouse or scroll up and down things really bog down (unless I do the drag in a single, quick motion). Is there anything to do to improve things? - [61]Q-35: I have lots of memory, but why does x11vnc fail with + [76]Q-38: I have lots of memory, but why does x11vnc fail with shmget: No space left on device or Minor opcode of failed request: 1 (X_ShmAttach)? - [62]Q-36: How can I make x11vnc use less system resources? + [77]Q-39: How can I make x11vnc use less system resources? - [63]Q-37: How can I make x11vnc use MORE system resources? + [78]Q-40: How can I make x11vnc use MORE system resources? - [64]Q-38: I use x11vnc over a slow link with high latency (e.g. dialup + [79]Q-41: I use x11vnc over a slow link with high latency (e.g. dialup modem), is there anything I can do to speed things up? - [65]Q-39: How can I get my AltGr and Shift modifiers to work between + [80]Q-42: How can I get my AltGr and Shift modifiers to work between keyboards for different languages? - [66]Q-40: When I try to type a "<" (i.e. less than) instead I get ">" + [81]Q-43: When I try to type a "<" (i.e. less than) instead I get ">" (i.e. greater than)! Strangely, typing ">" works OK!! - [67]Q-41: I'm using an "international" keyboard (e.g. German "de", or + [82]Q-44: I'm using an "international" keyboard (e.g. German "de", or Danish "dk") and the -modtweak mode works well if the VNC viewer is run on a Unix/Linux machine with a similar keyboard. But if I run the VNC viewer on Unix/Linux with a different keyboard (e.g. "us") or Windows with any keyboard, I can't type some keys like: "@", "$", "<", ">", etc. How can I fix this? - [68]Q-42: When typing I sometimes get double, triple, or more of my + [83]Q-45: When typing I sometimes get double, triple, or more of my keystrokes repeated. I'm sure I only typed them once, what can I do? - [69]Q-43: The machine where I run x11vnc has an AltGr key, but the + [84]Q-46: The machine where I run x11vnc has an AltGr key, but the local machine where I run the VNC viewer does not. Is there a way I can map a local unused key to send an AltGr? How about a Compose key as well? - [70]Q-44: I have a Sun machine I run x11vnc on. Its Sun keyboard has + [85]Q-47: I have a Sun machine I run x11vnc on. Its Sun keyboard has just one Alt key labelled "Alt" and two Meta keys labelled with little diamonds. The machine where I run the VNC viewer only has Alt keys. How can I send a Meta keypress? (e.g. emacs needs this) - [71]Q-45: Can I map a keystroke to a mouse button click on the remote + [86]Q-48: Can I map a keystroke to a mouse button click on the remote machine? - [72]Q-46: The remote display is larger (in number of pixels) than the + [87]Q-49: The remote display is larger (in number of pixels) than the local display I am running the vncviewer on. I don't like the vncviewer scrollbars, what I can do? - [73]Q-47: Does x11vnc support server-side framebuffer scaling? (E.g. + [88]Q-50: Does x11vnc support server-side framebuffer scaling? (E.g. to make the desktop smaller). - [74]Q-48: Does x11vnc work with Xinerama? (i.e. multiple monitors + [89]Q-51: Does x11vnc work with Xinerama? (i.e. multiple monitors joined together to form one big, single screen). - [75]Q-49: Can I use x11vnc on a multi-headed display that is not + [90]Q-52: Can I use x11vnc on a multi-headed display that is not Xinerama (i.e. separate screens :0.0, :0.1, ... for each monitor)? - [76]Q-50: Why is the view in my VNC viewer completely black? Or why is + [91]Q-53: Why is the view in my VNC viewer completely black? Or why is everything flashing around randomly? - [77]Q-51: I use Linux Virtual Consoles (VC's) to implement 'Fast User + [92]Q-54: I use Linux Virtual Consoles (VC's) to implement 'Fast User Switching' between users' sessions (e.g. Betty is on Ctrl-Alt-F7, Bobby is on Ctrl-Alt-F8, and Sid is on Ctrl-Alt-F1: they use those keystrokes to switch between their sessions). How come the view in a @@ -702,13 +757,13 @@ ls -l ./x11vnc/x11vnc otherwise all messed up unless the X session x11vnc is attached to is in the active VC? - [78]Q-52: Does the Clipboard/Selection get transferred between the + [93]Q-55: Does the Clipboard/Selection get transferred between the vncviewer and the X display? - [79]Q-53: Why don't I hear the "Beeps" in my X session (e.g. when + [94]Q-56: Why don't I hear the "Beeps" in my X session (e.g. when typing tput bel in an xterm)? - [80]Q-54: I am using x11vnc where my local machine has "popup/hidden + [95]Q-57: I am using x11vnc where my local machine has "popup/hidden taskbars" (e.g. GNOME or MacOS X) and the remote display where x11vnc runs also has "popup/hidden taskbars" (e.g. GNOME). When I move the mouse to the edge of the screen where the popups happen, the taskbars @@ -724,10 +779,11 @@ ls -l ./x11vnc/x11vnc your DISPLAY environment variable or use the -display option to specify it. Nearly always the correct value will be ":0" - For the latter error, you need to set up the X11 permissions. See the - xauth(1), Xsecurity(7), and xhost(1) man pages. For example, you may - need to set your XAUTHORITY environment variable to point to the - correct cookie file (e.g. /home/joe/.Xauthority or + For the latter error, you need to set up the X11 permissions + correctly. See the xauth(1), Xsecurity(7), and xhost(1) man pages for + much info. For example, you may need to set your XAUTHORITY + environment variable (or use the -auth option) to point to the correct + cookie file (e.g. /home/joe/.Xauthority or /var/lib/xdm/authdir/authfiles/A:0-nRySEi), or simply be sure you run x11vnc as the correct user (i.e. the user who owns the X session you wish to view). Less safe, but if the owner of the X session runs xhost @@ -749,12 +805,13 @@ ls -l ./x11vnc/x11vnc See xauth(1) manpage for full details on how to transfer an MIT-MAGIC-COOKIE between machines and displays. - To test out your X11 permissions, set DISPLAY and type xclock in the - same place you will be typing (or otherwise running) x11vnc. If a - little clock comes up on the display, that means the X11 permissions - are OK. To test your X11 permissions when logged into the display - machine remotely, use xdpyinfo (if you see the informational output - about the display you know it connected to the X server successfully). + To test out your X11 permissions, set DISPLAY (and possibly + XAUTHORITY) and type xclock in the same place you will be typing (or + otherwise running) x11vnc. If a little clock comes up on the display, + that means the X11 permissions are OK. To test your X11 permissions + when logged into the display machine remotely, use xdpyinfo (if you + see the informational output about the display you know it connected + to the X server successfully). Important: if you cannot get your X11 permissions so that the xclock or xdpyinfo tests work, x11vnc also will not work (all of these X clients must be able to connect to the X server to function). @@ -796,27 +853,94 @@ ls -l ./x11vnc/x11vnc XFree86-devel zlib-devel - Q-3: Where can I get a precompiled x11vnc binary for my Operating + Q-3: Help, I need to run x11vnc on Solaris 2.5.1 and it doesn't + compile! If I try to run a binary built on Solaris 2.6 I get: + relocation error: file x11vnc: symbol XConvertCase: referenced symbol + not found + + We apologize that x11vnc does not build cleanly on older versions of + Solaris, Linux, etc.: very few users are on these old releases. Here + is a workaround for Solaris 2.5.1 (and perhaps earlier): + + First use the environment settings (CPPFLAGS, LDFLAGS, etc.) in the + above [96]Solaris build script to run the configure command. That + should succeed without failure. Then, you have to hand edit the + autogenerated rfb/rfbconfig.h file in the source tree, and just before + the last #endif at the bottom of that file insert these workaround + lines: +#ifndef usleep +struct timeval _tmp_usleep_tv; +#define usleep(x) \ + _tmp_usleep_tv.tv_sec = (x) / 1000000; \ + _tmp_usleep_tv.tv_usec = (x) % 1000000; \ + select(0, NULL, NULL, NULL, &_tmp_usleep_tv); +#endif +int gethostname(char *name, int namelen); +long random(); +int srandom(unsigned int seed); +#undef LIBVNCSERVER_HAVE_LIBPTHREAD +#define SHUT_RDWR 2 +typedef unsigned int in_addr_t; +#define XConvertCase(sym, lower, upper) \ +*(lower) = sym; \ +*(upper) = sym; \ +if (sym >> 8 == 0) { \ + 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); \ +} + + Then run make with the Solaris build script environment, everything + should compile without problems, and the resulting x11vnc binary + should work OK (but note the above XConvertCase only covers Latin 1). + Similar sorts of kludges can be done on other older OS (Solaris, + Linux, ...) releases. + + Please let us know if you had to use the above workaround (and whether + it worked or not). If there is enough demand we will try to push clean + compilations back to earlier Solaris, Linux, etc, releases. + + Q-4: Where can I get a precompiled x11vnc binary for my Operating System? - Hopefully the [81]build steps above and [82]FAQ provide enough info + Hopefully the [97]build steps above and [98]FAQ provide enough info for a painless compile for most environments. Please report problems with the x11vnc configure, make, etc. on your system (if your system is known to compile other GNU packages successfully). There are precompiled x11vnc binaries made by other groups available at the following locations: - Debian: (.deb) [83]http://packages.debian.org/x11vnc - - Slackware: (.tgz) [84]http://www.linuxpackages.net/ Redhat/Fedora: - (.rpm) [85]http://dag.wieers.com/packages/x11vnc/ wwexptools: (.tgz) - [86]http://www.bell-labs.com/project/wwexptools/packages.html The last - one, wwexptools, provides a variety of Unix binaries (Linux, Solaris, - HP-UX, Irix, ...) with the intent of being compatible on a wide range - of OS releases. Find x11vnc at that link and select 'download info'. + Debian: (.deb) [99]http://packages.debian.org/x11vnc + + Slackware: (.tgz) [100]http://www.linuxpackages.net/ Redhat/Fedora: + (.rpm) [101]http://dag.wieers.com/packages/x11vnc/ wwexptools: (.tgz) + [102]http://www.bell-labs.com/project/wwexptools/packages.html The + last one, wwexptools, provides a variety of Unix binaries (Linux, + Solaris, HP-UX, Irix, ...) with the intent of being compatible on a + wide range of OS releases. Find x11vnc near the bottom of that page + and select 'download info'. + + Note: it appears some of the wwexptools x11vnc binaries are not + compiled with libz and libjpeg and so the fast compression encodings: + "Tight", "ZRLE", etc are not supported by those binaries. You can see + this by looking at the x11vnc output and if it says the encoding for a + client is "hextile" then likely the fast compression encodings are + missing. If you want optimal performance on your OS, you should see + the [103]build notes above for where to download libz and libjpeg, and + then build everything with gcc. + If any of the above binaries don't work and building x11vnc on your OS - fails, and all else fails, you can contact me as I occasionally have a - test binary I could give you. + fails (and all else fails!) you can contact me as I often have a test + binary I could give you. As a general note, the x11vnc program is simple enough you don't really need to install a package: the binary will in most cases work @@ -830,12 +954,21 @@ ls -l ./x11vnc/x11vnc resulting data.tar.gz tar file. Also, rpm2cpio(1) is useful in extracting files from rpm packages. - Q-4: How can I see all of x11vnc's command line options and + Q-5: Where can I get a VNC Viewer binary (or source code) for the + Operating System I will be viewing from? + + To obtain VNC viewers for the viewing side (Windows, Mac OS, or Unix) + try here: + * [104]http://www.tightvnc.com/download.html + * [105]http://www.realvnc.com/download-free.html + * [106]http://sourceforge.net/projects/cotvnc/ + + Q-6: How can I see all of x11vnc's command line options and documentation on how to use them? - Run: x11vnc -help The output is listed [87]here as well. + Run: x11vnc -help The output is listed [107]here as well. - Q-5: I don't like typing arcane command line options every time I + Q-7: I don't like typing arcane command line options every time I start x11vnc. What can I do? Is there a config file? You could create a shell script that calls x11vnc with your options: @@ -861,7 +994,7 @@ display :0 At some point this config file support will be expanded. - Q-6: Why does x11vnc exit as soon as the VNC viewer disconnects? And + Q-8: Why does x11vnc exit as soon as the VNC viewer disconnects? And why doesn't it allow more than one VNC viewer to connect at the same time? @@ -872,12 +1005,12 @@ display :0 -shared option to have x11vnc allow multiple clients to connect simultaneously. - Recommended additional safety measures include using ssh (see above) - or a VPN to authenticate and encrypt the viewer connections or to at - least use the -rfbauth passwd-file [88]option to use VNC password - protection. + Recommended additional safety measures include using ssh ([108]see + above), stunnel, or a VPN to authenticate and encrypt the viewer + connections or to at least use the -rfbauth passwd-file [109]option to + use VNC password protection (or [110]-passwdfile) - Q-7: I have two separate machine displays in front of me, one Windows + Q-9: I have two separate machine displays in front of me, one Windows the other X11: can I use x11vnc in combination with Win2VNC in dual-screen mode to pass the keystrokes and mouse motions to the X11 display? @@ -887,21 +1020,21 @@ display :0 secondary display (X11). Then start up Win2VNC on the primary display (Windows) referring it to the secondary display. - This will also work X11 to X11 using [89]x2vnc, however you would + This will also work X11 to X11 using [111]x2vnc, however you would probably just want to avoid VNC and use x2x for that. For reference, here are some links to Win2VNC-like programs for multiple monitor setups: - * [90]Original Win2VNC - * [91]Enhanced Win2VNC and [92]sourceforge link - * [93]x2vnc - * [94]x2x also [95]here - * [96]zvnc (MorphOS) + * [112]Original Win2VNC + * [113]Enhanced Win2VNC and [114]sourceforge link + * [115]x2vnc + * [116]x2x also [117]here + * [118]zvnc (MorphOS) All of them (except x2x) will work with x11vnc. - Q-8: I am running Win2VNC on my windows machine and trying to create a - dual-screen mode with my second display by running x11vnc -nofb. + Q-10: I am running Win2VNC on my windows machine and trying to create + a dual-screen mode with my second display by running "x11vnc -nofb". Whenever I initiate the connection Win2VNC quickly disconnects and x11vnc says something like: rfbProcessClientNormalMessage: read: Connection reset by peer @@ -926,7 +1059,7 @@ display :0 this. Since Win2VNC does not use the framebuffer data there should be no problems in doing this. - Q-9: The X display I run x11vnc on is only 8 bits per pixel (bpp) + Q-11: The X display I run x11vnc on is only 8 bits per pixel (bpp) PseudoColor (i.e. only 256 distinct colors). The x11vnc colors may start out OK, but after a while the colors are incorrect in certain windows. @@ -945,12 +1078,12 @@ display :0 non-zero in 8bpp PseudoColor on an obscure setup, and this option corrected the problems. - Q-10: Color problems: Why are the colors for some windows messed up in + Q-12: Color problems: Why are the colors for some windows messed up in x11vnc? BTW, I have an X display that has nice overlay/multi-depth visuals of different color depths: e.g. there are both depth 8 and 24 visuals available at the same time. - You may want to review the [97]previous question regarding 8 bpp + You may want to review the [119]previous question regarding 8 bpp PseudoColor. If that isn't the problem, run xdpyinfo(1) to see what the default @@ -1014,20 +1147,20 @@ TrueColor defdepth 24 option works for Solaris machines with overlay visuals where most of this problem occurs. - Q-11: How do I figure out the window id to supply to the -id windowid + Q-13: How do I figure out the window id to supply to the -id windowid option? Run the xwininfo program in a terminal. It will ask you to click on the desired application window. After clicking, it will print out much information, including the window id. Also, the visual and depth of the window printed out is often useful in debugging x11vnc - [98]problems. + [120]problems. When using -id windowid, note that some VNC viewers will have problems rendering screens that have a width that is not a multiple of 4. Try to manually adjust the window width before starting x11vnc -id .... - Q-12: Why don't menus or other transient windows come up when I am + Q-14: Why don't menus or other transient windows come up when I am using the -id windowid option to view a single application window? This is related to the behavior of the XGetImage(3X11) and @@ -1042,7 +1175,7 @@ TrueColor defdepth 24 x11vnc is known to crash under both -id and -sid, so both modes are still experimental. - Q-13: Can I use x11vnc to view and interact with an Xterminal (e.g. + Q-15: Can I use x11vnc to view and interact with an Xterminal (e.g. NCD) that is not running UNIX and so x11vnc cannot be run on it directly? @@ -1060,7 +1193,7 @@ TrueColor defdepth 24 -flipbyteorder if the colors get messed up due to endian byte order differences. - Q-14: Can I make x11vnc more quiet and also go into the background + Q-16: Can I make x11vnc more quiet and also go into the background after starting up? Use the -q and -bg options, respectively. (also: -quiet is an alias @@ -1069,7 +1202,26 @@ TrueColor defdepth 24 Note that under -bg the stderr messages will be lost unless you use the "-o logfile" option. - Q-15: Can I limit which machines incoming VNC clients can connect + Q-17: How do I stop x11vnc once it is running in the background? + + If the -forever option has not been supplied, x11vnc will + automatically exit after the first client disconnects. In general you + will have to kill the x11vnc process. This can be done via: "kill + NNNNN" (where NNNNN is the x11vnc process id number found from ps(1)), + or "pkill x11vnc", or "killall x11vnc" (Linux only, IIRC). + + If you have not put x11vnc in the background via the -bg option or + shell & operator, then simply press Ctrl-C in the shell where x11vnc + is running to stop it. If somehow your Keypress of Ctrl-C went through + x11vnc to the Xserver that then delivered it to x11vnc it is possible + one or both of the Ctrl or C keys will be left stuck in the pressed + down state in the Xserver. Tapping the stuck key (either via x11vnc or + at the physical console) will release it from the stuck state. If the + keyboard seems to be acting strangely it is often fixed by tapping + Ctrl, Shift, and Alt. Alternatively, the -clear_mods and -clear_keys + options can be used to release pressed keys at startup and exit. + + Q-18: Can I limit which machines incoming VNC clients can connect from? Yes, look at the -allow and -localhost options to limit connections by @@ -1084,25 +1236,26 @@ TrueColor defdepth 24 For more control, build libvncserver with libwrap support (tcp_wrappers) and then use /etc/hosts.allow. - Q-16: How do I build x11vnc/libvncserver with libwrap (tcp_wrappers) + Q-19: How do I build x11vnc/libvncserver with libwrap (tcp_wrappers) support? Here is one way to pass this information to the configure script: env CPPFLAGS=-DUSE_LIBWRAP LDFLAGS=-lwrap ./configure then run make as usual. This requires libwrap and its development - package (tcpd.h) to be installed on the build machine. + package (tcpd.h) to be installed on the build machine. If additional + CPPFLAGS or LDFLAGS options are needed supply them as well. The resulting x11vnc then uses libwrap/tcp_wrappers for connections. The service name you will use in /etc/hosts.allow and /etc/hosts.deny is "vnc", e.g.: vnc: 192.168.100.3 .example.com - Note that if you run x11vnc out of [99]inetd you do not need to build + Note that if you run x11vnc out of [121]inetd you do not need to build x11vnc with libwrap support because the /usr/sbin/tcpd reference in /etc/inetd.conf handles the tcp_wrappers stuff. - Q-17: Can I prompt the user at the local X display whether the + Q-20: Can I prompt the user at the local X display whether the incoming VNC client should be accepted or not? Can I decide to make some clients view-only? How about running an arbitrary program to make the decisions? @@ -1142,7 +1295,7 @@ TrueColor defdepth 24 program to prompt the user whether the client should be accepted or not. This requires that you have xmessage installed and available via PATH. In case it is not already on your system, the xmessage program - is available at [100]ftp://ftp.x.org/ + is available at [122]ftp://ftp.x.org/ To include view-only decisions for the external commands, prefix the command something like this: "yes:0,no:*,view:3 mycommand ..." This @@ -1181,7 +1334,7 @@ elif [ $rc = 4 ]; then fi exit 1 - Stefan Radman has written a nice dtksh script [101]dtVncPopup for use + Stefan Radman has written a nice dtksh script [123]dtVncPopup for use in CDE environments to do the same sort of thing. Information on how to use it is found at the top of the file. He encourages you to provide feedback to him to help improve the script. @@ -1196,7 +1349,7 @@ exit 1 the command is not interpreted by x11vnc. The same environment variables are set as in "-accept command" - Q-18: How do I create a VNC password for use with x11vnc? + Q-21: How do I create a VNC password for use with x11vnc? You may already have one in $HOME/.vnc/passwd if you have used, say, the vncserver program from the regular RealVNC or TightVNC packages @@ -1216,24 +1369,26 @@ exit 1 out for the command winding up in your shell's history file (history -c is often a way to clear it). - x11vnc also has the [102]-passwdfile and -passwd/-viewpasswd plain + x11vnc also has the [124]-passwdfile and -passwd/-viewpasswd plain text (i.e. not obscured like the -rfbauth VNC passwords) password options. - Q-19: How can I tunnel my connection to x11vnc via an encrypted SSH + Q-22: How can I tunnel my connection to x11vnc via an encrypted SSH channel between two Unix machines? - See the description earlier on this page on [103]how to tunnel VNC via + See the description earlier on this page on [125]how to tunnel VNC via SSH from Unix to Unix. A number of ways are described along with some issues you may encounter. - Other secure encrypted methods exists, e.g. stunnel. + Other secure encrypted methods exists, e.g. stunnel, IPSEC, various + VPNs, etc. - Q-20: How can I tunnel my connection to x11vnc via an encrypted SSH + Q-23: How can I tunnel my connection to x11vnc via an encrypted SSH channel from Windows using an SSH client like Putty? - [104]Above we described how to tunnel VNC via SSH from Unix to Unix. - To do this from Windows using Putty it would go something like this: + [126]Above we described how to tunnel VNC via SSH from Unix to Unix, + you may want to review it. To do this from Windows using Putty it + would go something like this: * In the Putty dialog window under 'Session' enter the hostname or IP number of the Unix machine with display to be viewed. * Make sure the SSH protocol is selected and the server port is @@ -1264,14 +1419,14 @@ exit 1 you'll need to do a second login (ssh or rsh) to the workstation machine 'otherhost' and then start up x11vnc on it. - As discussed [105]above another option is to first start the VNC + As discussed [127]above another option is to first start the VNC viewer in "listen" mode, and then launch x11vnc with the "-connection localhost" option to establish the reverse connection. In this case a Remote port redirection (not Local) is needed for port 5500 instead of 5900 (i.e. 'Source port: 5500' and 'Destination: localhost:5500' for a Remote connection). - Q-21: Does x11vnc support Unix usernames and passwords? Can I further + Q-24: Does x11vnc support Unix usernames and passwords? Can I further limit the set of Unix usernames who can connect to the VNC desktop? Until the VNC protocol and libvncserver support this things will be @@ -1285,7 +1440,7 @@ exit 1 connection to make it appear to emanate from the local machine. As discussed above, ssh is useful for this: ssh -l username -L 5900:localhost:5900 hostname ... See the ssh wrapper scripts mentioned - [106]elsewhere on this page. Of course a malicious user could allow + [128]elsewhere on this page. Of course a malicious user could allow other users to get in through his channel, but that is a problem with every method. Another thing to watch out for is a malicious user on the viewer side (where ssh is running) trying to sneak in through the @@ -1322,7 +1477,7 @@ exit 1 # reject it For this to work with ssh port redirection, the ssh option UsePrivilegeSeparation must be enabled. - Q-22: Can I have two passwords for VNC viewers, one for full access + Q-25: Can I have two passwords for VNC viewers, one for full access and the other for view-only access to the display? Yes, as of May/2004 in the libvncserver CVS there is the -viewpasswd @@ -1348,7 +1503,7 @@ exit 1 # reject it plain text passwords from $HOME/.vnc/passwd since it is very straight-forward to work out what to do from the VNC source code. - Q-23: I use a screen-lock when I leave my workstation (e.g. + Q-26: I use a screen-lock when I leave my workstation (e.g. xscreensaver or xlock). When I remotely access my workstation desktop via x11vnc I can unlock the desktop fine, but I am worried people will see my activities on the physical monitor. What can I do to prevent @@ -1361,7 +1516,7 @@ exit 1 # reject it In any event, as of Jun/2004 there is an experimental utility to make it more difficult for nosey people to see your x11vnc activities. The - source for it is [107]blockdpy.c The idea behind it is simple (but + source for it is [129]blockdpy.c The idea behind it is simple (but obviously not bulletproof): when a VNC client attaches to x11vnc put the display monitor in the DPMS "off" state, if the DPMS state ever changes immediately start up the screen-lock program. The x11vnc user @@ -1382,7 +1537,7 @@ exit 1 # reject it re-lock the screen before disconnecting!). Instructions can be found in the source code for the utility at the above link. - Q-24: Can I have x11vnc automatically lock the screen when I + Q-27: Can I have x11vnc automatically lock the screen when I disconnect the VNC viewer? Yes, a user mentions he uses the -gone option under CDE to run a @@ -1394,7 +1549,7 @@ exit 1 # reject it x11vnc -display :0.0 -forever -gone 'kdesktop_lock' x11vnc -display :0.0 -forever -gone 'xlock &' - Q-25: Are reverse connections (i.e. the VNC server connecting to the + Q-28: Are reverse connections (i.e. the VNC server connecting to the VNC viewer) using "vncviewer -listen" and vncconnect(1) supported? As of Mar/2004 in the libvncserver CVS x11vnc supports reverse @@ -1420,7 +1575,7 @@ exit 1 # reject it # xprop -root -f VNC_CONNECT 8s -set VNC_CONNECT "$1" - Q-26: Sometimes when a VNC viewer dies abruptly, x11vnc also dies with + Q-29: Sometimes when a VNC viewer dies abruptly, x11vnc also dies with the error message like: "Broken pipe". I'm using the -forever mode and I want x11vnc to keep running. @@ -1430,11 +1585,12 @@ xprop -root -f VNC_CONNECT 8s -set VNC_CONNECT "$1" Currently (Apr/2004) the above fix only works for BSD signal systems (Linux, FreeBSD, ...) For SYSV systems there is a workaround in my - [108]x11vnc.c file. It also has an option -sigpipe exit to have x11vnc + [130]x11vnc.c file. It also has an option -sigpipe exit to have x11vnc clean up and exit upon receiving SIGPIPE. - Q-27: How can I use x11vnc to connect to an X login screen like xdm, - GNOME gdm, KDE kdm, or CDE dtlogin? + Q-30: How can I use x11vnc to connect to an X login screen like xdm, + GNOME gdm, KDE kdm, or CDE dtlogin? (i.e. nobody is logged into an X + session yet). One time only. If the X login screen is running and you just want to connect to it once: @@ -1498,7 +1654,7 @@ xprop -root -f VNC_CONNECT 8s -set VNC_CONNECT "$1" If you do not want to deal with the display manager startup scripts, here is a kludgey script that can be run manually or out of a boot - file like rc.local. [109]x11vnc_loop It will need some local + file like rc.local. [131]x11vnc_loop It will need some local customization before running. Because the XAUTHORITY auth file must be guessed by this script, use of the display manager script above is preferred. @@ -1521,7 +1677,7 @@ xprop -root -f VNC_CONNECT 8s -set VNC_CONNECT "$1" See xauth(1) manpage for full details on how to transfer an MIT-MAGIC-COOKIE between machines and displays. - Q-28: Can I run x11vnc out of inetd(1)? + Q-31: Can I run x11vnc out of inetd(1)? Yes, perhaps a line something like this in /etc/inetd.conf will do it for you: @@ -1567,7 +1723,7 @@ mp/x11vnc_sh.log connecting to it. Always use a VNC password to further protect against unwanted access. - Q-29: How do I make x11vnc work with the Java VNC viewer applet in a + Q-32: How do I make x11vnc work with the Java VNC viewer applet in a web browser? To have x11vnc serve up a Java VNC viewer applet to any web browsers @@ -1594,7 +1750,7 @@ mp/x11vnc_sh.log entirely from the viewer-side by having the jar file there and using either the java or appletviewer commands to run the program. - Q-30: Why isn't the mouse cursor shape (the little icon shape where + Q-33: Why isn't the mouse cursor shape (the little icon shape where the mouse pointer is) correct as I move from window to window? As mentioned above, the X11 mouse cursor shape (i.e. little picture: @@ -1621,10 +1777,10 @@ mp/x11vnc_sh.log problems however, and can be slower). Plans are in the works to use XFIXES for this on XFree86, Xorg, and Xsun. - Q-31: Why does the mouse arrow just stay in one corner in my + Q-34: Why does the mouse arrow just stay in one corner in my vncviewer, whereas my cursor (that does move) is just a dot? - This default takes advantage of a [110]tightvnc extension that allows + This default takes advantage of a [132]tightvnc extension that allows specifying a cursor image for the local VNC viewer. You may disable it with the -nocursor option to x11vnc if your viewer does not have this extension. @@ -1633,7 +1789,7 @@ mp/x11vnc_sh.log default for non-tightvnc viewers will be to draw the moving cursor into the framebuffer. This can also be disabled via -nocursor. - Q-32: Can I take advantage of the TightVNC extension to the VNC + Q-35: Can I take advantage of the TightVNC extension to the VNC protocol where Cursor Positions Updates are sent back to all connected clients (i.e. passive viewers can see the mouse cursor being moved around by another viewer)? @@ -1644,7 +1800,7 @@ mp/x11vnc_sh.log libvncserver CVS -cursorpos is the default. See -nocursorpos and -nocursorshape. - Q-33: Is it possible to swap the mouse buttons (e.g. left-handed + Q-36: Is it possible to swap the mouse buttons (e.g. left-handed operation), or arbitrarily remap them? How about mapping button clicks to keystrokes, e.g. to partially emulate Mouse wheel scrolling? @@ -1693,7 +1849,7 @@ mp/x11vnc_sh.log (yes, this is getting a little silly). - Q-34: When I drag windows around with the mouse or scroll up and down + Q-37: When I drag windows around with the mouse or scroll up and down things really bog down (unless I do the drag in a single, quick motion). Is there anything to do to improve things? @@ -1709,7 +1865,7 @@ mp/x11vnc_sh.log some circumstances when you want to see the visual feedback while dragging (e.g. menu traversal or text selection). - Q-35: I have lots of memory, but why does x11vnc fail with shmget: + Q-38: I have lots of memory, but why does x11vnc fail with shmget: No space left on device or Minor opcode of failed request: 1 (X_ShmAttach)? @@ -1727,7 +1883,7 @@ mp/x11vnc_sh.log 19/03/2004 10:10:58 error creating tile-row shm for len=4 19/03/2004 10:10:58 reverting to single_copytile mode - Here is a shell script [111]shm_clear to list and prompt for removal + Here is a shell script [133]shm_clear to list and prompt for removal of your unattached shm segments (attached ones are skipped). I use it while debugging x11vnc (I use shm_clear -y to assume "yes" for each prompt). If x11vnc is regularly not cleaning up its shm segments, @@ -1765,10 +1921,10 @@ ied) -fs 1.0 knocks it down to 2). If you are having much trouble with shm segments, consider disabling shm completely via the -noshm option. Performance will be somewhat degraded but when done over local machine - sockets it should be acceptable (see an [112]earlier question + sockets it should be acceptable (see an [134]earlier question discussing -noshm). - Q-36: How can I make x11vnc use less system resources? + Q-39: How can I make x11vnc use less system resources? The -nap and "-wait n" (where n is the sleep between polls in milliseconds, the default is 30 or so) option are good places to @@ -1777,7 +1933,7 @@ ied) -onetile option will use less memory and use fewer shared memory slots (add -fs 1.0 for one less slot). - Q-37: How can I make x11vnc use MORE system resources? + Q-40: How can I make x11vnc use MORE system resources? You can try -threads and dial down the wait time (e.g. -wait 1) and possibly dial down -defer as well. Note that if you try to increase @@ -1789,7 +1945,7 @@ ied) the x11vnc -id option) can be streamed over a LAN or wireless at a reasonable frame rate. - Q-38: I use x11vnc over a slow link with high latency (e.g. dialup + Q-41: I use x11vnc over a slow link with high latency (e.g. dialup modem), is there anything I can do to speed things up? Some things you might want to experiment with (most of which will help @@ -1813,7 +1969,7 @@ ied) worth it, but could be of use in some situations. VNC viewer parameters: - * Use a [113]TightVNC enabled viewer! + * Use a [135]TightVNC enabled viewer! * Make sure the tight encoding is being used (look at vncviewer and x11vnc outputs) * Request 8 bits per pixel using -bgr233 (up to 4X speedup over @@ -1849,7 +2005,7 @@ ied) * Use -nocursor (repainting the remote cursor position and shape takes resources and round trips) - Q-39: How can I get my AltGr and Shift modifiers to work between + Q-42: How can I get my AltGr and Shift modifiers to work between keyboards for different languages? The option -modtweak should be of some use for this. It is a mode that @@ -1861,12 +2017,12 @@ ied) default (use -nomodtweak to get the old behavior). This was done because it was noticed on newer XFree86 setups even on bland "us" keyboards like "pc104 us" XFree86 included a "ghost" key with both "<" - and ">" it. This key does not exist on the keyboard (see [114]this FAQ + and ">" it. This key does not exist on the keyboard (see [136]this FAQ for more info). Without -modtweak there was then an ambiguity in the reverse map keysym => keycode, making it so the "<" symbol could not be typed. - Also see the [115]FAQ about the -xkb option for a more powerful method + Also see the [137]FAQ about the -xkb option for a more powerful method of modifier tweaking for use on X servers with the XKEYBOARD extension. @@ -1874,7 +2030,7 @@ ied) -debug_keyboard option prints out much info for every keystroke and so can be useful debugging things. - Q-40: When I try to type a "<" (i.e. less than) instead I get ">" + Q-43: When I try to type a "<" (i.e. less than) instead I get ">" (i.e. greater than)! Strangely, typing ">" works OK!! Does your keyboard have a single key with both "<" and ">" on it? Even @@ -1927,14 +2083,14 @@ ied) the keysym comma when it comes in from a client (so when Shift is down the comma press will yield "<"). - See also the [116]FAQ about the -xkb option as a possible workaround + See also the [138]FAQ about the -xkb option as a possible workaround using the XKEYBOARD extension. Note that of Jul/2004 in the libvncserver CVS the -modtweak option is now that default. Note that the -debug_keyboard option prints out much info for every keystroke to aid debugging keyboard problems. - Q-41: I'm using an "international" keyboard (e.g. German "de", or + Q-44: I'm using an "international" keyboard (e.g. German "de", or Danish "dk") and the -modtweak mode works well if the VNC viewer is run on a Unix/Linux machine with a similar keyboard. But if I run the VNC viewer on Unix/Linux with a different keyboard (e.g. "us") or @@ -2022,7 +2178,7 @@ ied) is received from a VNC viewer, and only after that would -add_keysyms, or anything else, come into play. - Q-42: When typing I sometimes get double, triple, or more of my + Q-45: When typing I sometimes get double, triple, or more of my keystrokes repeated. I'm sure I only typed them once, what can I do? This may be due to an interplay between your X server's key autorepeat @@ -2060,7 +2216,7 @@ ied) someone is also working at the physical display and misses his autorepeating. - Q-43: The machine where I run x11vnc has an AltGr key, but the local + Q-46: The machine where I run x11vnc has an AltGr key, but the local machine where I run the VNC viewer does not. Is there a way I can map a local unused key to send an AltGr? How about a Compose key as well? @@ -2074,7 +2230,7 @@ ied) Super_R-Mode_switch,Menu-Multi_key" or use "-remap filename" to specify remappings from a file. - Q-44: I have a Sun machine I run x11vnc on. Its Sun keyboard has just + Q-47: I have a Sun machine I run x11vnc on. Its Sun keyboard has just one Alt key labelled "Alt" and two Meta keys labelled with little diamonds. The machine where I run the VNC viewer only has Alt keys. How can I send a Meta keypress? (e.g. emacs needs this) @@ -2091,7 +2247,7 @@ ied) send Alt_L in this case, maybe -remap Super_L-Meta_L would be a better choice. - Q-45: Can I map a keystroke to a mouse button click on the remote + Q-48: Can I map a keystroke to a mouse button click on the remote machine? This can be done directly in some X servers using AccessX and @@ -2113,7 +2269,7 @@ ied) are generated immediately on the x11vnc side. When the key is released (i.e. goes up) no events are generated. - Q-46: The remote display is larger (in number of pixels) than the + Q-49: The remote display is larger (in number of pixels) than the local display I am running the vncviewer on. I don't like the vncviewer scrollbars, what I can do? @@ -2131,10 +2287,10 @@ ied) There may also be scaling viewers out there (e.g. TightVNC on Windows) that automatically shrink or expand the remote framebuffer to fit the - local display. Especially for hand-held devices. See also [117]this + local display. Especially for hand-held devices. See also [139]this FAQ - Q-47: Does x11vnc support server-side framebuffer scaling? (E.g. to + Q-50: Does x11vnc support server-side framebuffer scaling? (E.g. to make the desktop smaller). As of Jun/2004 in the libvncserver CVS x11vnc provides basic @@ -2181,7 +2337,7 @@ ied) different scalings listening on separate ports (-rfbport option, etc.). - Q-48: Does x11vnc work with Xinerama? (i.e. multiple monitors joined + Q-51: Does x11vnc work with Xinerama? (i.e. multiple monitors joined together to form one big, single screen). Yes, it should generally work because it simply polls the big @@ -2208,7 +2364,7 @@ ied) function. (This may be due to a bug in the X server for XTEST when Xinerama is enabled). - Q-49: Can I use x11vnc on a multi-headed display that is not Xinerama + Q-52: Can I use x11vnc on a multi-headed display that is not Xinerama (i.e. separate screens :0.0, :0.1, ... for each monitor)? You can, but it is a little bit awkward: you must start separate @@ -2226,17 +2382,17 @@ ied) Note: if you are running on Solaris 8 or earlier you can easily hit up against the maximum of 6 shm segments per process (for Xsun in this case) from running multiple x11vnc processes. You should modify - /etc/system as mentioned in another [118]FAQ to increase the limit. It + /etc/system as mentioned in another [140]FAQ to increase the limit. It is probably also a good idea to run with the -onetile option in this case (to limit each x11vnc to 3 shm segments), or even -noshm to use no shm segments. - Q-50: Why is the view in my VNC viewer completely black? Or why is + Q-53: Why is the view in my VNC viewer completely black? Or why is everything flashing around randomly? See the next FAQ for a possible explanation. - Q-51: I use Linux Virtual Consoles (VC's) to implement 'Fast User + Q-54: I use Linux Virtual Consoles (VC's) to implement 'Fast User Switching' between users' sessions (e.g. Betty is on Ctrl-Alt-F7, Bobby is on Ctrl-Alt-F8, and Sid is on Ctrl-Alt-F1: they use those keystrokes to switch between their sessions). How come the view in a @@ -2263,7 +2419,7 @@ ied) x11vnc can poll it), one can use the switchto(1) command, e.g. "switchto 7" for VC #7. - Q-52: Does the Clipboard/Selection get transferred between the + Q-55: Does the Clipboard/Selection get transferred between the vncviewer and the X display? As of Jan/2004 in the libvncserver CVS x11vnc supports the "CutText" @@ -2273,7 +2429,7 @@ ied) want the PRIMARY selection to be polled for changes use the -noprimary option. - Q-53: Why don't I hear the "Beeps" in my X session (e.g. when typing + Q-56: Why don't I hear the "Beeps" in my X session (e.g. when typing tput bel in an xterm)? As of Dec/2003 in the libvncserver CVS "Beep" XBell events are tracked @@ -2285,135 +2441,160 @@ ied) want to hear the audio from the remote applications, consider trying a redirector such as esd. - Q-54: I am using x11vnc where my local machine has "popup/hidden + Q-57: I am using x11vnc where my local machine has "popup/hidden taskbars" (e.g. GNOME or MacOS X) and the remote display where x11vnc runs also has "popup/hidden taskbars" (e.g. GNOME). When I move the mouse to the edge of the screen where the popups happen, the taskbars interfere and fight with each other in strange ways. What can I do? - No useful answer so far. Is there a way to temporarily disable one or - both of these magic desktop taskbars? + Is there a way to temporarily disable one or both of these magic + desktop taskbars? One x11vnc user suggests: it should be + straightforward to right mouse click on the task bar panel, and + uncheck "enable auto-hide" from the panel properties dialog box. This + will make the panel always visible. References 1. http://www.karlrunge.com/x11vnc/index.html#faq 2. http://www.karlrunge.com/x11vnc/index.html#downloading 3. http://www.karlrunge.com/x11vnc/index.html#building - 4. http://www.uk.research.att.com/vnc/ - 5. http://www.realvnc.com/ - 6. http://www.tightvnc.com/ - 7. http://www.karlrunge.com/x11vnc/index.html#downloading - 8. http://www.sun.com/software/solaris/freeware/ - 9. http://www.karlrunge.com/x11vnc/x11vnc_opts.html - 10. http://www.karlrunge.com/x11vnc/index.html#accept - 11. http://www.karlrunge.com/x11vnc/index.html#passwd - 12. http://www.karlrunge.com/x11vnc/index.html#passwdfile - 13. http://sourceforge.net/projects/libvncserver/ - 14. http://sourceforge.net/project/showfiles.php?group_id=32584&package_id=119006&release_id=257442 - 15. http://www.karlrunge.com/x11vnc/x11vnc.c - 16. http://www.karlrunge.com/x11vnc/index.html#binaries - 17. http://www.karlrunge.com/x11vnc/rx11vnc - 18. http://www.karlrunge.com/x11vnc/rx11vnc.pl - 19. ftp://ftp.uu.net/graphics/jpeg/ - 20. http://www.gzip.org/zlib/ - 21. http://www.sunfreeware.com/ - 22. http://www.tightvnc.com/ - 23. http://www.karlrunge.com/x11vnc/x11vnc_opts.html - 24. http://www.karlrunge.com/x11vnc/index.html#passwd - 25. http://wwws.sun.com/sunray/index.html - 26. mailto:xvml@karlrunge.com - 27. http://www.karlrunge.com/x11vnc/index.html#FAQ-1 - 28. http://www.karlrunge.com/x11vnc/index.html#FAQ-2 - 29. http://www.karlrunge.com/x11vnc/index.html#FAQ-3 - 30. http://www.karlrunge.com/x11vnc/index.html#FAQ-4 - 31. http://www.karlrunge.com/x11vnc/index.html#FAQ-5 - 32. http://www.karlrunge.com/x11vnc/index.html#FAQ-6 - 33. http://www.karlrunge.com/x11vnc/index.html#FAQ-7 - 34. http://www.karlrunge.com/x11vnc/index.html#FAQ-8 - 35. http://www.karlrunge.com/x11vnc/index.html#FAQ-9 - 36. http://www.karlrunge.com/x11vnc/index.html#FAQ-10 - 37. http://www.karlrunge.com/x11vnc/index.html#FAQ-11 - 38. http://www.karlrunge.com/x11vnc/index.html#FAQ-12 - 39. http://www.karlrunge.com/x11vnc/index.html#FAQ-13 - 40. http://www.karlrunge.com/x11vnc/index.html#FAQ-14 - 41. http://www.karlrunge.com/x11vnc/index.html#FAQ-15 - 42. http://www.karlrunge.com/x11vnc/index.html#FAQ-16 - 43. http://www.karlrunge.com/x11vnc/index.html#FAQ-17 - 44. http://www.karlrunge.com/x11vnc/index.html#FAQ-18 - 45. http://www.karlrunge.com/x11vnc/index.html#FAQ-19 - 46. http://www.karlrunge.com/x11vnc/index.html#FAQ-20 - 47. http://www.karlrunge.com/x11vnc/index.html#FAQ-21 - 48. http://www.karlrunge.com/x11vnc/index.html#FAQ-22 - 49. http://www.karlrunge.com/x11vnc/index.html#FAQ-23 - 50. http://www.karlrunge.com/x11vnc/index.html#FAQ-24 - 51. http://www.karlrunge.com/x11vnc/index.html#FAQ-25 - 52. http://www.karlrunge.com/x11vnc/index.html#FAQ-26 - 53. http://www.karlrunge.com/x11vnc/index.html#FAQ-27 - 54. http://www.karlrunge.com/x11vnc/index.html#FAQ-28 - 55. http://www.karlrunge.com/x11vnc/index.html#FAQ-29 - 56. http://www.karlrunge.com/x11vnc/index.html#FAQ-30 - 57. http://www.karlrunge.com/x11vnc/index.html#FAQ-31 - 58. http://www.karlrunge.com/x11vnc/index.html#FAQ-32 - 59. http://www.karlrunge.com/x11vnc/index.html#FAQ-33 - 60. http://www.karlrunge.com/x11vnc/index.html#FAQ-34 - 61. http://www.karlrunge.com/x11vnc/index.html#FAQ-35 - 62. http://www.karlrunge.com/x11vnc/index.html#FAQ-36 - 63. http://www.karlrunge.com/x11vnc/index.html#FAQ-37 - 64. http://www.karlrunge.com/x11vnc/index.html#FAQ-38 - 65. http://www.karlrunge.com/x11vnc/index.html#FAQ-39 - 66. http://www.karlrunge.com/x11vnc/index.html#FAQ-40 - 67. http://www.karlrunge.com/x11vnc/index.html#FAQ-41 - 68. http://www.karlrunge.com/x11vnc/index.html#FAQ-42 - 69. http://www.karlrunge.com/x11vnc/index.html#FAQ-43 - 70. http://www.karlrunge.com/x11vnc/index.html#FAQ-44 - 71. http://www.karlrunge.com/x11vnc/index.html#FAQ-45 - 72. http://www.karlrunge.com/x11vnc/index.html#FAQ-46 - 73. http://www.karlrunge.com/x11vnc/index.html#FAQ-47 - 74. http://www.karlrunge.com/x11vnc/index.html#FAQ-48 - 75. http://www.karlrunge.com/x11vnc/index.html#FAQ-49 - 76. http://www.karlrunge.com/x11vnc/index.html#FAQ-50 - 77. http://www.karlrunge.com/x11vnc/index.html#FAQ-51 - 78. http://www.karlrunge.com/x11vnc/index.html#FAQ-52 - 79. http://www.karlrunge.com/x11vnc/index.html#FAQ-53 - 80. http://www.karlrunge.com/x11vnc/index.html#FAQ-54 - 81. http://www.karlrunge.com/x11vnc/index.html#building - 82. http://www.karlrunge.com/x11vnc/index.html#buildfaq - 83. http://packages.debian.org/x11vnc - 84. http://www.linuxpackages.net/search_view.php?by=name&name=x11vnc - 85. http://dag.wieers.com/packages/x11vnc/ - 86. http://www.bell-labs.com/project/wwexptools/packages.html - 87. http://www.karlrunge.com/x11vnc/x11vnc_opts.html - 88. http://www.karlrunge.com/x11vnc/index.html#passwd - 89. http://fredrik.hubbe.net/x2vnc.html - 90. http://www.hubbe.net/~hubbe/win2vnc.html - 91. http://www.deboer.gmxhome.de/ - 92. http://sourceforge.net/projects/win2vnc/ - 93. http://fredrik.hubbe.net/x2vnc.html - 94. http://freshmeat.net/projects/x2x/ - 95. http://ftp.digital.com/pub/Digital/SRC/x2x/ - 96. http://zapek.com/software/zvnc/ - 97. http://www.karlrunge.com/x11vnc/index.html#8bpp - 98. http://www.karlrunge.com/x11vnc/overlays - 99. http://www.karlrunge.com/x11vnc/index.html#inetd - 100. ftp://ftp.x.org/ - 101. http://www.karlrunge.com/x11vnc/dtVncPopup - 102. http://www.karlrunge.com/x11vnc/index.html#passwdfile - 103. http://www.karlrunge.com/x11vnc/index.html#tunnelling - 104. http://www.karlrunge.com/x11vnc/index.html#tunnelling - 105. http://www.karlrunge.com/x11vnc/index.html#tunnelling - 106. http://www.karlrunge.com/x11vnc/index.html#tunnelling - 107. http://www.karlrunge.com/x11vnc/blockdpy.c - 108. http://www.karlrunge.com/x11vnc/x11vnc.c - 109. http://www.karlrunge.com/x11vnc/x11vnc_loop - 110. http://www.tightvnc.com/ - 111. http://www.karlrunge.com/x11vnc/shm_clear - 112. http://www.karlrunge.com/x11vnc/index.html#noshm - 113. http://www.tightvnc.com/ - 114. http://www.karlrunge.com/x11vnc/index.html#greaterless - 115. http://www.karlrunge.com/x11vnc/index.html#xkbmodtweak - 116. http://www.karlrunge.com/x11vnc/index.html#xkbmodtweak - 117. http://www.karlrunge.com/x11vnc/index.html#scaling - 118. http://www.karlrunge.com/x11vnc/index.html#solshm + 4. http://www.karlrunge.com/x11vnc/index.html#faq + 5. http://www.karlrunge.com/x11vnc/index.html#contact + 6. http://www.uk.research.att.com/vnc/ + 7. http://www.realvnc.com/ + 8. http://www.tightvnc.com/ + 9. http://www.karlrunge.com/x11vnc/index.html#downloading + 10. http://www.tightvnc.com/download.html + 11. http://www.karlrunge.com/x11vnc/index.html#xperms + 12. http://www.karlrunge.com/x11vnc/index.html#viewer-download + 13. http://www.sun.com/software/solaris/freeware/ + 14. http://www.karlrunge.com/x11vnc/x11vnc_opts.html + 15. http://www.karlrunge.com/x11vnc/index.html#accept + 16. http://www.karlrunge.com/x11vnc/index.html#passwd + 17. http://www.karlrunge.com/x11vnc/index.html#passwdfile + 18. http://www.karlrunge.com/x11vnc/index.html#allow_opt + 19. http://www.karlrunge.com/x11vnc/index.html#tcp_wrappers + 20. http://sourceforge.net/projects/libvncserver/ + 21. http://sourceforge.net/project/showfiles.php?group_id=32584&package_id=119006&release_id=257442 + 22. http://www.karlrunge.com/x11vnc/x11vnc.c + 23. http://www.karlrunge.com/x11vnc/index.html#binaries + 24. http://www.tightvnc.com/download.html + 25. http://www.realvnc.com/download-free.html + 26. http://sourceforge.net/projects/cotvnc/ + 27. http://www.karlrunge.com/x11vnc/rx11vnc + 28. http://www.karlrunge.com/x11vnc/rx11vnc.pl + 29. ftp://ftp.uu.net/graphics/jpeg/ + 30. http://www.gzip.org/zlib/ + 31. http://www.sunfreeware.com/ + 32. http://www.karlrunge.com/x11vnc/index.html#solaris251build + 33. http://www.tightvnc.com/ + 34. http://www.karlrunge.com/x11vnc/x11vnc_opts.html + 35. http://www.karlrunge.com/x11vnc/index.html#passwd + 36. http://www.karlrunge.com/x11vnc/recurse_x11vnc.jpg + 37. http://wwws.sun.com/sunray/index.html + 38. mailto:xvml@karlrunge.com + 39. http://www.karlrunge.com/x11vnc/index.html#FAQ-1 + 40. http://www.karlrunge.com/x11vnc/index.html#FAQ-2 + 41. http://www.karlrunge.com/x11vnc/index.html#FAQ-3 + 42. http://www.karlrunge.com/x11vnc/index.html#FAQ-4 + 43. http://www.karlrunge.com/x11vnc/index.html#FAQ-5 + 44. http://www.karlrunge.com/x11vnc/index.html#FAQ-6 + 45. http://www.karlrunge.com/x11vnc/index.html#FAQ-7 + 46. http://www.karlrunge.com/x11vnc/index.html#FAQ-8 + 47. http://www.karlrunge.com/x11vnc/index.html#FAQ-9 + 48. http://www.karlrunge.com/x11vnc/index.html#FAQ-10 + 49. http://www.karlrunge.com/x11vnc/index.html#FAQ-11 + 50. http://www.karlrunge.com/x11vnc/index.html#FAQ-12 + 51. http://www.karlrunge.com/x11vnc/index.html#FAQ-13 + 52. http://www.karlrunge.com/x11vnc/index.html#FAQ-14 + 53. http://www.karlrunge.com/x11vnc/index.html#FAQ-15 + 54. http://www.karlrunge.com/x11vnc/index.html#FAQ-16 + 55. http://www.karlrunge.com/x11vnc/index.html#FAQ-17 + 56. http://www.karlrunge.com/x11vnc/index.html#FAQ-18 + 57. http://www.karlrunge.com/x11vnc/index.html#FAQ-19 + 58. http://www.karlrunge.com/x11vnc/index.html#FAQ-20 + 59. http://www.karlrunge.com/x11vnc/index.html#FAQ-21 + 60. http://www.karlrunge.com/x11vnc/index.html#FAQ-22 + 61. http://www.karlrunge.com/x11vnc/index.html#FAQ-23 + 62. http://www.karlrunge.com/x11vnc/index.html#FAQ-24 + 63. http://www.karlrunge.com/x11vnc/index.html#FAQ-25 + 64. http://www.karlrunge.com/x11vnc/index.html#FAQ-26 + 65. http://www.karlrunge.com/x11vnc/index.html#FAQ-27 + 66. http://www.karlrunge.com/x11vnc/index.html#FAQ-28 + 67. http://www.karlrunge.com/x11vnc/index.html#FAQ-29 + 68. http://www.karlrunge.com/x11vnc/index.html#FAQ-30 + 69. http://www.karlrunge.com/x11vnc/index.html#FAQ-31 + 70. http://www.karlrunge.com/x11vnc/index.html#FAQ-32 + 71. http://www.karlrunge.com/x11vnc/index.html#FAQ-33 + 72. http://www.karlrunge.com/x11vnc/index.html#FAQ-34 + 73. http://www.karlrunge.com/x11vnc/index.html#FAQ-35 + 74. http://www.karlrunge.com/x11vnc/index.html#FAQ-36 + 75. http://www.karlrunge.com/x11vnc/index.html#FAQ-37 + 76. http://www.karlrunge.com/x11vnc/index.html#FAQ-38 + 77. http://www.karlrunge.com/x11vnc/index.html#FAQ-39 + 78. http://www.karlrunge.com/x11vnc/index.html#FAQ-40 + 79. http://www.karlrunge.com/x11vnc/index.html#FAQ-41 + 80. http://www.karlrunge.com/x11vnc/index.html#FAQ-42 + 81. http://www.karlrunge.com/x11vnc/index.html#FAQ-43 + 82. http://www.karlrunge.com/x11vnc/index.html#FAQ-44 + 83. http://www.karlrunge.com/x11vnc/index.html#FAQ-45 + 84. http://www.karlrunge.com/x11vnc/index.html#FAQ-46 + 85. http://www.karlrunge.com/x11vnc/index.html#FAQ-47 + 86. http://www.karlrunge.com/x11vnc/index.html#FAQ-48 + 87. http://www.karlrunge.com/x11vnc/index.html#FAQ-49 + 88. http://www.karlrunge.com/x11vnc/index.html#FAQ-50 + 89. http://www.karlrunge.com/x11vnc/index.html#FAQ-51 + 90. http://www.karlrunge.com/x11vnc/index.html#FAQ-52 + 91. http://www.karlrunge.com/x11vnc/index.html#FAQ-53 + 92. http://www.karlrunge.com/x11vnc/index.html#FAQ-54 + 93. http://www.karlrunge.com/x11vnc/index.html#FAQ-55 + 94. http://www.karlrunge.com/x11vnc/index.html#FAQ-56 + 95. http://www.karlrunge.com/x11vnc/index.html#FAQ-57 + 96. http://www.karlrunge.com/x11vnc/index.html#solarisbuilding + 97. http://www.karlrunge.com/x11vnc/index.html#building + 98. http://www.karlrunge.com/x11vnc/index.html#buildfaq + 99. http://packages.debian.org/x11vnc + 100. http://www.linuxpackages.net/search_view.php?by=name&name=x11vnc + 101. http://dag.wieers.com/packages/x11vnc/ + 102. http://www.bell-labs.com/project/wwexptools/packages.html + 103. http://www.karlrunge.com/x11vnc/index.html#solarisbuilding + 104. http://www.tightvnc.com/download.html + 105. http://www.realvnc.com/download-free.html + 106. http://sourceforge.net/projects/cotvnc/ + 107. http://www.karlrunge.com/x11vnc/x11vnc_opts.html + 108. http://www.karlrunge.com/x11vnc/index.html#tunnelling + 109. http://www.karlrunge.com/x11vnc/index.html#passwd + 110. http://www.karlrunge.com/x11vnc/index.html#passwdfile + 111. http://fredrik.hubbe.net/x2vnc.html + 112. http://www.hubbe.net/~hubbe/win2vnc.html + 113. http://www.deboer.gmxhome.de/ + 114. http://sourceforge.net/projects/win2vnc/ + 115. http://fredrik.hubbe.net/x2vnc.html + 116. http://freshmeat.net/projects/x2x/ + 117. http://ftp.digital.com/pub/Digital/SRC/x2x/ + 118. http://zapek.com/software/zvnc/ + 119. http://www.karlrunge.com/x11vnc/index.html#8bpp + 120. http://www.karlrunge.com/x11vnc/index.html#overlays + 121. http://www.karlrunge.com/x11vnc/index.html#inetd + 122. ftp://ftp.x.org/ + 123. http://www.karlrunge.com/x11vnc/dtVncPopup + 124. http://www.karlrunge.com/x11vnc/index.html#passwdfile + 125. http://www.karlrunge.com/x11vnc/index.html#tunnelling + 126. http://www.karlrunge.com/x11vnc/index.html#tunnelling + 127. http://www.karlrunge.com/x11vnc/index.html#tunnelling + 128. http://www.karlrunge.com/x11vnc/index.html#tunnelling + 129. http://www.karlrunge.com/x11vnc/blockdpy.c + 130. http://www.karlrunge.com/x11vnc/x11vnc.c + 131. http://www.karlrunge.com/x11vnc/x11vnc_loop + 132. http://www.tightvnc.com/ + 133. http://www.karlrunge.com/x11vnc/shm_clear + 134. http://www.karlrunge.com/x11vnc/index.html#noshm + 135. http://www.tightvnc.com/ + 136. http://www.karlrunge.com/x11vnc/index.html#greaterless + 137. http://www.karlrunge.com/x11vnc/index.html#xkbmodtweak + 138. http://www.karlrunge.com/x11vnc/index.html#xkbmodtweak + 139. http://www.karlrunge.com/x11vnc/index.html#scaling + 140. http://www.karlrunge.com/x11vnc/index.html#solshm ======================================================================= @@ -2426,8 +2607,8 @@ x11vnc: a VNC server for real X displays Here are all of x11vnc command line options: % x11vnc -help -x11vnc: allow VNC connections to real X11 displays. 0.6.3pre lastmod: 2004-08-2 -9 +x11vnc: allow VNC connections to real X11 displays. 0.6.3pre lastmod: 2004-12-1 +6 Typical usage is: @@ -2440,15 +2621,17 @@ Typical usage is: vncviewer far-host:0 -Once x11vnc establishes connections with the X11 server and starts -listening as a VNC server it will print out a string: PORT=XXXX where -XXXX is typically 5900 (the default VNC port). One would next run something -like this on the local machine: "vncviewer host:N" where N is XXXX - 5900, -i.e. usually "vncviewer host:0" +Once x11vnc establishes connections with the X11 server and starts listening +as a VNC server it will print out a string: PORT=XXXX where XXXX is typically +5900 (the default VNC server port). One would next run something like +this on the local machine: "vncviewer hostname:N" where "hostname" is +the name of the machine running x11vnc and N is XXXX - 5900, i.e. usually +"vncviewer hostname:0". -By default x11vnc will not allow the screen to be shared and it will -exit as soon as a client disconnects. See -shared and -forever below -to override these protections. +By default x11vnc will not allow the screen to be shared and it will exit +as soon as a client disconnects. See -shared and -forever below to override +these protections. See the FAQ on how to tunnel the VNC connection through +an encrypted channel such as ssh(1). For additional info see: http://www.karlrunge.com/x11vnc/ and http://www.karlrunge.com/x11vnc/#faq @@ -2475,36 +2658,54 @@ Options: -id windowid Show the window corresponding to "windowid" not the entire display. New windows like popup menus, - etc may not be seen, or will be clipped. x11vnc may - crash if the window changes size, is iconified, etc. - Use xwininfo(1) to get the window id. Primarily useful - for exporting very simple applications. + transient toplevels, etc, may not be seen or may be + clipped. Disabling SaveUnders or BackingStore in the + X server may help show them. x11vnc may crash if the + window is initially partially obscured, changes size, + is iconified, etc. Some steps are taken to avoid this + and the -xrandr mechanism is used to track resizes. Use + xwininfo(1) to get the window id, or use "-id pick" + to have x11vnc run xwininfo(1) for you and extract + the id. The -id option is useful for exporting very + simple applications (e.g. the current view on a webcam). -sid windowid As -id, but instead of using the window directly it - shifts a root view to it: this shows saveUnders menus, + shifts a root view to it: this shows SaveUnders menus, etc, although they will be clipped if they extend beyond the window. -flashcmap In 8bpp indexed color, let the installed colormap flash as the pointer moves from window to window (slow). -notruecolor For 8bpp displays, force indexed color (i.e. a colormap) even if it looks like 8bpp TrueColor. (rare problem) +-visual n Experimental option: probably does not do what you + think. It simply *forces* the visual used for the + framebuffer; this may be a bad thing... (e.g. messes + up colors or cause a crash). It is useful for testing + and for some workarounds. n may be a decimal number, + or 0x hex. Run xdpyinfo(1) for the values. One may + also use "TrueColor", etc. see <X11/X.h> for a list. + If the string ends in ":m" for better or for worse + the visual depth is forced to be m. -overlay Handle multiple depth visuals on one screen, e.g. 8+24 and 24+8 overlay visuals (the 32 bits per pixel are packed with 8 for PseudoColor and 24 for TrueColor). - Currently -overlay only works on Solaris (it uses - XReadScreen(3X11)). There is a problem with image - "bleeding" around transient popup menus (but not - for the menu itself): a workaround is to disable - SaveUnders by passing the "-su" argument to Xsun - (in /etc/dt/config/Xservers, say). Also note that, - the mouse cursor shape is exactly correct in this mode. + Currently -overlay only works on Solaris via + XReadScreen(3X11) and IRIX using XReadDisplay(3). + On Solaris there is a problem with image "bleeding" + around transient popup menus (but not for the menu + itself): a workaround is to disable SaveUnders + by passing the "-su" argument to Xsun (in + /etc/dt/config/Xservers). Also note that the mouse + cursor shape is exactly correct in this mode. Use -overlay as a workaround for situations like these: Some legacy applications require the default visual be 8bpp (8+24), or they will use 8bpp PseudoColor even when the default visual is depth 24 TrueColor (24+8). In these cases colors in some windows will be messed - up in x11vnc unless -overlay is used. + up in x11vnc unless -overlay is used. Another use of + -overlay is to enable showing the exact mouse cursor + shape (details below). Under -overlay, performance will be somewhat degraded due to the extra image transformations required. @@ -2514,22 +2715,15 @@ Options: visual (some apps have -use24 or -visual options). -overlay_nocursor Sets -overlay, but does not try to draw the exact mouse cursor shape using the overlay mechanism. --visual n Experimental option: probably does not do what you - think. It simply *forces* the visual used for the - framebuffer; this may be a bad thing... It is useful for - testing and for some workarounds. n may be a decimal - number, or 0x hex. Run xdpyinfo(1) for the values. - One may also use "TrueColor", etc. see <X11/X.h> - for a list. If the string ends in ":m" for better - or for worse the visual depth is forced to be m. - --scale fraction Scale the framebuffer by factor "fraction". - Values less than 1 shrink the fb. Note: image may not - be sharp and response may be slower. Currently the - cursor shape is not scaled. If "fraction" contains - a decimal point "." it is taken as a floating point - number, alternatively the notation "m/n" may be used - to denote fractions exactly, e.g. -scale 2/3. + +-scale fraction Scale the framebuffer by factor "fraction". Values + less than 1 shrink the fb, larger ones expand it. + Note: image may not be sharp and response may be + slower. Currently the cursor shape is not scaled. + If "fraction" contains a decimal point "." it + is taken as a floating point number, alternatively + the notation "m/n" may be used to denote fractions + exactly, e.g. -scale 2/3. Scaling Options: can be added after "fraction" via ":", to supply multiple ":" options use commas. @@ -2548,6 +2742,10 @@ Options: disconnects, opposite of -forever. This is the Default. -forever Keep listening for more connections rather than exiting as soon as the first client(s) disconnect. Same as -many +-inetd Launched by inetd(1): stdio instead of listening socket. + Note: if you are not redirecting stderr to a log file + (via shell 2> or -o option) you must also specify the + -q option, otherwise the stderr goes to the viewer. -connect string For use with "vncviewer -listen" reverse connections. If "string" has the form "host" or "host:port" the connection is made once at startup. Use commas @@ -2559,21 +2757,17 @@ Options: -novncconnect VNC program vncconnect(1). When the property is set to "host" or "host:port" establish a reverse connection. Using xprop(1) instead of vncconnect may - work, see the FAQ. Default: -vncconnect --inetd Launched by inetd(1): stdio instead of listening socket. - Note: if you are not redirecting stderr to a log file - (via shell 2> or -o option) you must also specify the - -q option. - --allow addr1[,addr2..] Only allow client connections from IP addresses matching - the comma separated list of numerical addresses. - Can be a prefix, e.g. "192.168.100." to match a - simple subnet, for more control build libvncserver - with libwrap support. If the list contains a "/" - it instead is a interpreted as a file containing - addresses or prefixes that is re-read each time a new - client connects. Lines can be commented out with the - "#" character in the usual way. + work (see the FAQ). Default: -vncconnect + +-allow host1[,host2..] Only allow client connections from hosts matching + the comma separated list of hostnames or IP addresses. + Can also be a numerical IP prefix, e.g. "192.168.100." + to match a simple subnet, for more control build + libvncserver with libwrap support (See the FAQ). If the + list contains a "/" it instead is a interpreted as a + file containing addresses or prefixes that is re-read + each time a new client connects. Lines can be commented + out with the "#" character in the usual way. -localhost Same as -allow 127.0.0.1 -viewpasswd string Supply a 2nd password for view-only logins. The -passwd (full-access) password must also be supplied. @@ -2582,7 +2776,7 @@ Options: If a second non blank line exists in the file it is taken as a view-only password (i.e. -viewpasswd) Note: this is a simple plaintext passwd, see also -rfbauth - and -storepasswd below. + and -storepasswd below for obfuscated passwords. -storepasswd pass file Store password "pass" as the VNC password in the file "file". Once the password is stored the program exits. Use the password via "-rfbauth file" @@ -2591,10 +2785,10 @@ Options: should be allowed to connect or not. "string" is an external command run via system(3) or some special cases described below. Be sure to quote "string" - if it contains spaces, etc. If the external command - returns 0 the client is accepted, otherwise the client - is rejected. See below for an extension to accept a - client view-only. + if it contains spaces, shell characters, etc. If the + external command returns 0 the client is accepted, + otherwise the client is rejected. See below for an + extension to accept a client view-only. Environment: The RFB_CLIENT_IP environment variable will be set to the incoming client IP number and the port @@ -2604,7 +2798,7 @@ Options: of the tcp virtual circuit. The x11vnc process id will be in RFB_X11VNC_PID, a client id number in RFB_CLIENT_ID, and the number of other connected clients - in RFB_CLIENT_COUNT. + in RFB_CLIENT_COUNT. RFB_MODE will be "accept" If "string" is "popup" then a builtin popup window is used. The popup will time out after 120 seconds, @@ -2612,7 +2806,8 @@ Options: (use 0 for no timeout) If "string" is "xmessage" then an xmessage(1) - invocation is used for the command. + invocation is used for the command. xmessage must be + installed on the machine for this to work. Both "popup" and "xmessage" will present an option for accepting the client "View-Only" (the client @@ -2628,19 +2823,23 @@ Options: the default action (in case the command returns an unexpected value). E.g. "no:*" is a good choice. - Note that x11vnc blocks while the external command or + Note that x11vnc blocks while the external command or popup is running (other clients may see no updates during this period). More -accept tricks: use "popupmouse" to only allow mouse clicks in the builtin popup to be recognized. - Similarly use "popupkey" to only recognize keystroke - responses. All 3 of the popup keywords can be followed + Similarly use "popupkey" to only recognize + keystroke responses. These are to help avoid the + user accidentally accepting a client by typing or + clicking. All 3 of the popup keywords can be followed by +N+M to supply a position for the popup window. The default is to center the popup window. -gone string As -accept, except to run a user supplied command when - a client goes away (disconnects). Unlike -accept, - the command return code is not interpreted by x11vnc. + a client goes away (disconnects). RFB_MODE will be + set to "gone" and the other RFB_* variables are as + in -accept. Unlike -accept, the command return code + is not interpreted by x11vnc. Example: -gone 'xlock &' -noshm Do not use the MIT-SHM extension for the polling. Remote displays can be polled this way: be careful this @@ -2663,8 +2862,44 @@ Options: In general on XINERAMA displays you may need to use the -xwarppointer option if the mouse pointer misbehaves. +-xrandr [mode] If the display supports the XRANDR (X Resize, Rotate + and Reflection) extension, and you expect XRANDR events + to occur to the display while x11vnc is running, this + options indicates x11vnc should try to respond to + them (as opposed to simply crashing by assuming the + old screen size). See the xrandr(1) manpage and run + 'xrandr -q' for more info. [mode] is optional and + described below. + + Since watching for XRANDR events and errors increases + polling overhead, only use this option if XRANDR changes + are expected. For example on a rotatable screen PDA or + laptop, or using a XRANDR-aware Desktop where you resize + often. It is best to be viewing with a vncviewer that + supports the NewFBSize encoding, since it knows how to + react to screen size changes. Otherwise, libvncserver + tries to do so something reasonable for viewers that + cannot do this (portions of the screen may be clipped, + unused, etc). + + "mode" defaults to "resize", which means create a + new, resized, framebuffer and hope all viewers can cope + with the change. "newfbsize" means first disconnect + all viewers that do not support the NewFBSize VNC + encoding, and then resize the framebuffer. "exit" + means disconnect all viewer clients, and then terminate + x11vnc. +-padgeom WxH Whenever a new vncviewer connects, the framebuffer is + replaced with a fake, solid black one of geometry WxH. + Shortly afterwards the framebuffer is replaced with the + real one. This is intended for use with vncviewers + that do not support NewFBSize and one wants to make + sure the initial viewer geometry will be big enough + to handle all subsequent resizes (e.g. under -xrandr, + -remote id:windowid, rescaling, etc. + -o logfile Write stderr messages to file "logfile" instead of - to the terminal. Same as -logfile "file". + to the terminal. Same as "-logfile file". -rc filename Use "filename" instead of $HOME/.x11vncrc for rc file. -norc Do not process any .x11vncrc file for options. -h, -help Print this help text. @@ -2688,16 +2923,18 @@ Options: identical keyboards). Also useful in resolving cases where a Keysym is bound to multiple keys (e.g. "<" + ">" and "," + "<" keys). Default: -modtweak --xkb When in modtweak mode, use the XKEYBOARD extension - (if it exists) to do the modifier tweaking. This is - powerful and should be tried if there are still - keymapping problems when using the simpler -modtweak. --skip_keycodes string Skip keycodes not on your keyboard but your X server - thinks exist. Currently only applies to -xkb mode. - "string" is a comma separated list of decimal - keycodes. Use this option to help x11vnc in the reverse - problem it tries to solve: Keysym -> Keycode(s) when - ambiguities exist. E.g. -skip_keycodes 94,114 +-xkb When in modtweak mode, use the XKEYBOARD extension (if + the X display supports it) to do the modifier tweaking. + This is powerful and should be tried if there are still + keymapping problems when using -modtweak by itself. +-skip_keycodes string Ignore the comma separated list of decimal keycodes. + Perhaps these are keycodes not on your keyboard but + your X server thinks exist. Currently only applies + to -xkb mode. Use this option to help x11vnc in the + reverse problem it tries to solve: Keysym -> Keycode(s) + when ambiguities exist (more than one Keycode per + Keysym). Run 'xmodmap -pk' to see your keymapping. + E.g. "-skip_keycodes 94,114" -add_keysyms If a Keysym is received from a VNC viewer and that Keysym does not exist in the X server, then add the Keysym to the X server's keyboard mapping. @@ -2716,8 +2953,8 @@ Options: form: key1-key2,key3-key4,... See <X11/keysymdef.h> header file for a list of Keysym names, or use xev(1). To map a key to a button click, use the - fake Keysyms "Button1", ..., etc. - E.g. -remap Super_R-Button2 + fake Keysyms "Button1", ..., etc. E.g. "-remap + Super_R-Button2" (useful for pasting on a laptop) -norepeat Option -norepeat disables X server key auto repeat -repeat when VNC clients are connected. This works around a repeating keystrokes bug (triggered by long processing @@ -2725,7 +2962,7 @@ Options: either from large screen changes or high latency). Note: your VNC viewer side will likely do autorepeating, so this is no loss unless someone is simultaneously at - the real X display. Default: -repeat + the real X display. Default: -norepeat -nofb Ignore video framebuffer: only process keyboard and pointer. Intended for use with Win2VNC and x2vnc @@ -2750,26 +2987,35 @@ Options: network traffic by not having to send the cursor image every time the pointer is moved), in which case these extensions are used (see -nocursorshape and -nocursorpos - below). For other viewers the cursor shape is written - directly to the framebuffer every time the pointer is - moved or changed and gets sent along with the other - framebuffer updates. In this case, there will be - some lag between the vnc viewer pointer and the remote - cursor position. + below to disable). For other viewers the cursor shape + is written directly to the framebuffer every time the + pointer is moved or changed and gets sent along with + the other framebuffer updates. In this case, there + will be some lag between the vnc viewer pointer and + the remote cursor position. If the X display supports retrieving the cursor shape - information from the X server, then the default - is to use that mode. On Solaris this requires - the SUN_OVL extension and the -overlay option to be - supplied. (see also the -overlay_nomouse option). (Soon) - on XFree86/Xorg the XFIXES extension is required. - Either can be disabled with -nocursor, and also some - values of the "mode" option below. + information from the X server, then the default is + to use that mode. On Solaris this can be done with + the SUN_OVL extension using -overlay (see also the + -overlay_nomouse option). A similar overlay scheme + is used on IRIX. Xorg (e.g. Linux) and recent Solaris + Xsun servers support the XFIXES extension to retrieve + the exact cursor shape from the X server. If XFIXES + is present it is preferred over Overlay and is used by + default (see -noxfixes below). This can be disabled + with -nocursor, and also some values of the "mode" + option below. The "mode" string can be used to fine-tune the displaying of cursor shapes. It can be used the following ways: + "-cursor arrow" - just show the standard arrow + nothing more or nothing less. + + "-cursor none" - same as "-nocursor" + "-cursor X" - when the cursor appears to be on the root window, draw the familiar X shape. Some desktops such as GNOME cover up the root window completely, @@ -2786,21 +3032,24 @@ Options: more feedback about the cursor shape. "-cursor most" - try to show as many cursors as - possible. Often this will only be the same as "some". - On Solaris if XFIXES is not available, -overlay mode - will be used. + possible. Often this will only be the same as "some" + unless the display has overlay visuals or XFIXES + extensions available. On Solaris and IRIX if XFIXES + is not available, -overlay mode will be attempted. +-noxfixes Do not use the XFIXES extension to draw the exact cursor + shape even if it is available. -nocursorshape Do not use the TightVNC CursorShapeUpdates extension even if clients support it. See -cursor above. -cursorpos Option -cursorpos enables sending the X cursor position -nocursorpos back to all vnc clients that support the TightVNC CursorPosUpdates extension. Other clients will be able to see the pointer motions. Default: -cursorpos --xwarppointer Move the pointer with XWarpPointer(3X) instead of XTEST - extension. Use this as a workaround if the pointer - motion behaves incorrectly, e.g. on touchscreens or - other non-standard setups. Also sometimes needed on - XINERAMA displays. +-xwarppointer Move the pointer with XWarpPointer(3X) instead of + the XTEST extension. Use this as a workaround + if the pointer motion behaves incorrectly, e.g. + on touchscreens or other non-standard setups. + Also sometimes needed on XINERAMA displays. -buttonmap string String to remap mouse buttons. Format: IJK-LMN, this maps buttons I -> L, etc., e.g. -buttonmap 13-31 @@ -2830,12 +3079,24 @@ Options: improves response on slow setups, but you lose all visual feedback for drags, text selection, and some menu traversals. --old_pointer Do not use the new pointer input handling mechanisms. - See check_input() and pointer() in source file for - details. --input_skip n For the old pointer handling when non-threaded: try to +-pointer_mode n Various pointer update schemes. The problem is pointer + motion can cause rapid changes on the screen, e.g. a + window drag. Neither x11vnc nor the bandwidth to the + vncviewers can keep up these rapid screen changes: + everything bogs down when dragging or scrolling. + Note that most video h/w is optimized for writing, not + reading (a 50X rate difference is possible) and x11vnc + is reading all the time. So a scheme has to be used to + "eat" much of that pointer input before re-polling the + screen. n can be 1 to 4. n=1 was the original scheme + used to about Jan 2004. n=2 is an improved scheme. + n=3 is basically a dynamic -nodragging mode: it detects + if the mouse drag motion has paused and refreshes + the display. n=4 is TBD. The default n is 2. +-input_skip n For the pointer handling when non-threaded: try to read n user input events before scanning display. n < 0 means to act as though there is always user input. + Default: 10 -debug_pointer Print debugging output for every pointer event. -debug_keyboard Print debugging output for every keyboard event. @@ -2843,11 +3104,14 @@ Options: times for more output. -defer time Time in ms to wait for updates before sending to client - [rfbDeferUpdateTime] Default: 30 + (deferUpdateTime) Default: 30 -wait time Time in ms to pause between screen polls. Used to cut down on load. Default: 30 -nap Monitor activity and if low take longer naps between polls to really cut down load when idle. Default: off +-sb time Time in seconds after NO activity (e.g. screen blank) + to really throttle down the screen polls (i.e. sleep + for about 1.5 secs). Use 0 to disable. Default: 60 -sigpipe string Broken pipe (SIGPIPE) handling. "string" can be "ignore" or "exit". For "ignore" libvncserver @@ -2867,6 +3131,311 @@ Options: -fuzz n Tolerance in pixels to mark a tiles edges as changed. Default: 2 +-gui [gui-opts] Start up a simple tcl/tk gui based on the the remote + control options -remote/-query described below. + "gui-opts" is not required: the default is to start + up both the gui and x11vnc with the gui showing up on + the X display in the environment variable DISPLAY. + + "gui-opts" can be a comma separated list of items. + Currently there are only two types of items: 1) a gui + mode and 2) the X display the gui should display on. + The gui mode can be "start", "conn", or "wait" + "start" is the default mode above and is not required. + "conn" means do not automatically start up x11vnc, + but instead just try to connect to an existing x11vnc + process. "wait" means just start the gui and nothing + else (you will later instruct the gui to start x11vnc + or connect to one.) + + Note the possible confusion regarding the potentially + two X displays: x11vnc polls one, but you may want the + gui to appear on another. For example, if you ssh in + and x11vnc is not running yet you may want the gui to + come back to you via your ssh redirected X display + (e.g. localhost:10). + + Examples: "x11vnc -gui", "x11vnc -gui localhost:10", + "x11vnc -gui :10", "x11vnc -gui wait,:10", + "x11vnc -gui <x11vnc-opts...>" + + If you do not specify a gui X display in "gui-opts" + then the DISPLAY environment variable and -display + option are tried (in that order). Regarding the x11vnc + X display the gui will try to connect to, it first + tries -display and then DISPLAY. For example, "x11vnc + -display :0 -gui otherhost:0", will remote control an + x11vnc polling :0 and display the gui on otherhost:0 + + If you do not intend to start x11vnc from the gui + (i.e. just remote control an existing one), then the + gui process can run on a different machine from the + x11vnc server as long as X permissions, etc. permit + communication between the two. + +-remote command Remotely control some aspects of an already running + x11vnc server. "-R" and "-r" are aliases for + "-remote". After the remote control command is + sent to the running server the 'x11vnc -remote ...' + command exits. You can often use the -query command + (see below) to see if the x11vnc server processed your + -remote command. + + The default communication channel is that of X + properties (specifically VNC_CONNECT), and so this + command must be run with correct settings for DISPLAY + and possibly XAUTHORITY to connect to the X server + and set the property. Alternatively, use the -display + and -auth options to set them to the correct values. + The running server cannot use the -novncconnect option + because that disables the communication channel. + See below for alternate channels. + + For example: 'x11vnc -remote stop' (which is the same as + 'x11vnc -R stop') will close down the x11vnc server. + 'x11vnc -R shared' will enable shared connections, and + 'x11vnc -R scale:3/4' will rescale the desktop. + + Note: the more drastic the change induced by the -remote + command, the bigger the chance for bugs or crashes. + Please report reproducible bugs. + + The following -remote/-R commands are supported: + + stop terminate the server, same as "quit" + "exit" or "shutdown" + ping see if the x11vnc server responds. + Return is: ans=ping:<xdisplay> + blacken try to push a black fb update to all + clients (due to timings a client + could miss it). Same as "zero", also + "zero:x1,y1,x2,y2" for a rectangle. + refresh send the entire fb to all clients. + reset recreate the fb, polling mem, etc. + id:windowid set -id window to "windowid" + sid:windowid set -sid window to "windowid" + flashcmap enable -flashcmap mode. + noflashcmap disable -flashcmap mode. + notruecolor enable -notruecolor mode. + truecolor disable -notruecolor mode. + overlay enable -overlay mode (if applicable). + nooverlay disable -overlay mode. + overlay_cursor in -overlay mode, enable cursor drawing. + overlay_nocursor disable cursor drawing. same as + nooverlay_cursor. + visual:vis set -visual to "vis" + scale:frac set -scale to "frac" + viewonly enable -viewonly mode. + noviewonly disable -viewonly mode. + shared enable -shared mode. + noshared disable -shared mode. + forever enable -forever mode. + noforever disable -forever mode. + deny deny any new connections, same as "lock" + nodeny allow new connections, same as "unlock" + connect:host do reverse connection to host, "host" + may be a comma separated list of hosts + or host:ports. See -connect. + disconnect:host disconnect any clients from "host" + same as "close:host". Use host + "all" to close all current clients. + If you know the client internal hex ID, + e.g. 0x3 (returned by -query clients and + RFB_CLIENT_ID), you can use that too. + allowonce:host For the next connection only, allow + connection from "host". + allow:hostlist set -allow list to (comma separated) + "hostlist". See -allow and -localhost. + Do not use with -allow /path/to/file + Use "+host" to add a single host, and + use "-host" to delete a single host + localhost enable -localhost mode + nolocalhost disable -localhost mode + accept:cmd set -accept "cmd" (empty to disable). + gone:cmd set -gone "cmd" (empty to disable). + noshm enable -noshm mode. + shm disable -noshm mode (i.e. use shm). + flipbyteorder enable -flipbyteorder mode, you may need + to set noshm for this to do something. + noflipbyteorder disable -flipbyteorder mode. + onetile enable -onetile mode. (you may need to + set shm for this to do something) + noonetile disable -onetile mode. + blackout:str set -blackout "str" (empty to disable). + See -blackout for the form of "str" + (basically: WxH+X+Y,...) + Use "+WxH+X+Y" to append a single + rectangle use "-WxH+X+Y" to delete one + xinerama enable -xinerama mode. (if applicable) + noxinerama disable -xinerama mode. + xrandr enable -xrandr mode. (if applicable) + noxrandr disable -xrandr mode. + xrandr_mode:mode set the -xrandr mode to "mode". + padgeom:WxH set -padgeom to WxH (empty to disable) + If WxH is "force" or "do" the padded + geometry fb is immediately applied. + quiet enable -quiet mode. + noquiet disable -quiet mode. + modtweak enable -modtweak mode. + nomodtweak enable -nomodtweak mode. + xkb enable -xkb modtweak mode. + noxkb disable -xkb modtweak mode. + skip_keycodes:str enable -xkb -skip_keycodes "str". + add_keysyms enable -add_keysyms mode. + noadd_keysyms stop adding keysyms. those added will + still be removed at exit. + clear_mods enable -clear_mods mode and clear them. + noclear_mods disable -clear_mods mode. + clear_keys enable -clear_keys mode and clear them. + noclear_keys disable -clear_keys mode. + remap:str set -remap "str" (empty to disable). + See -remap for the form of "str" + (basically: key1-key2,key3-key4,...) + Use "+key1-key2" to append a single + keymapping, use "-key1-key2" to delete. + norepeat enable -norepeat mode. + repeat disable -norepeat mode. + bell enable bell (if supported). + nobell disable bell. + sel disable -nosel mode. + nosel enable -nosel mode. + primary disable -noprimary mode. + noprimary enable -noprimary mode. + cursor:mode enable -cursor "mode". + show_cursor enable showing a cursor. + noshow_cursor disable showing a cursor. (same as + "nocursor") + xfixes enable xfixes cursor shape mode. + noxfixes disable xfixes cursor shape mode. + cursorshape disable -nocursorshape mode. + nocursorshape enable -nocursorshape mode. + cursorpos disable -nocursorpos mode. + nocursorpos enable -nocursorpos mode. + xwarp enable -xwarppointer mode. + noxwarp disable -xwarppointer mode. + buttonmap:str set -buttonmap "str" empty to disable + dragging disable -nodragging mode. + nodragging enable -nodragging mode. + pointer_mode n set -pointer_mode to n. + input_skip n set -input_skip to n. + debug_pointer enable -debug_pointer, same as "dp" + nodebug_pointer disable -debug_pointer, same as "nodp" + debug_keyboard enable -debug_keyboard, same as "dk" + nodebug_keyboard disable -debug_keyboard, same as "nodk" + defer:n set -defer to n ms,same as deferupdate:n + wait:n set -wait to n ms. + nap enable -nap mode. + nonap disable -nap mode. + sb:n set -sb to n s, same as screen_blank:n + fs:frac set -fs fraction to "frac", e.g. 0.5 + gaps:n set -gaps to n. + grow:n set -grow to n. + fuzz:n set -fuzz to n. + progressive:n set libvncserver -progressive slice + height parameter to n. + file:name run -remote commands from file "name", + one command per line,blank and # skipped + noremote disable the -remote command processing, + it cannot be turned back on. + + The vncconnect(1) command from standard VNC + distributions may also be used if string is prefixed + with "cmd=" E.g. 'vncconnect cmd=stop'. Under some + circumstances xprop(1) can used if it supports -set + (see the FAQ). + + If "-connect /path/to/file" has been supplied to the + running x11vnc server then that file can be used as a + communication channel (this is the only way to remote + control one of many x11vnc's polling the same X display) + Simply run: 'x11vnc -connect /path/to/file -remote ...' + or you can directly write to the file via something + like: "echo cmd=stop > /path/to/file", etc. + +-query variable Like -remote, except just query the value of + "variable". "-Q" is an alias for "-query". + Multiple queries can be done by separating variables + by commas, e.g. -query var1,var2. The results come + back in the form ans=var1:value1,ans=var2:value2,... + to the standard output. If a variable is read-only, + it comes back with prefix "aro=" instead of "ans=". + + Some -remote commands are pure actions that do not make + sense as variables, e.g. "stop" or "disconnect", + in these cases the value returned is "N/A". To direct + a query straight to the VNC_CONNECT property or connect + file use "qry=..." instead of "cmd=..." + + Here is the current list of "variables" that can + be supplied to the -query command. This includes the + "N/A" ones that return no useful info. For variables + names that do not correspond to an x11vnc option or + remote command, we hope the name makes it obvious what + the returned value corresponds to (hint: the ext_* + variables correspond to the presence of X extensions): + + ans= stop quit exit shutdown ping blacken zero refresh + reset close disconnect id sid flashcmap noflashcmap + truecolor notruecolor overlay nooverlay overlay_cursor + overlay_yescursor nooverlay_cursor overlay_nocursor + visual scale viewonly noviewonly shared noshared + forever noforever once deny lock nodeny unlock connect + allowonce allow localhost nolocalhost accept gone shm + noshm flipbyteorder noflipbyteorder onetile noonetile + blackout xinerama noxinerama xrandr noxrandr xrandr_mode + padgeom quiet q noquiet modtweak nomodtweak xkb noxkb + skip_keycodes add_keysyms noadd_keysyms clear_mods + noclear_mods clear_keys noclear_keys remap repeat + norepeat bell nobell sel nosel primary noprimary + cursorshape nocursorshape cursorpos nocursorpos cursor + show_cursor noshow_cursor nocursor xfixes noxfixes xwarp + xwarppointer noxwarp noxwarppointer buttonmap dragging + nodragging pointer_mode input_skip debug_pointer dp + nodebug_pointer nodp debug_keyboard dk nodebug_keyboard + nodk deferupdate defer wait nap nonap sb screen_blank + fs gaps grow fuzz progressive noremote + + aro= display vncdisplay desktopname desktop auth + rootshift scale_str scaled_x scaled_y scale_numer + scale_denom scale_fac scaling_noblend scaling_nomult4 + scaling_pad scaling_interpolate inetd safer unsafe + passwdfile using_shm logfile o rc norc h help V version + lastmod bg nofb sigpipe threads clients client_count + pid ext_xtest ext_xkb ext_xshm ext_xinerama ext_overlay + ext_xfixes ext_xdamage ext_xrandr rootwin num_buttons + button_mask mouse_x mouse_y bpp depth indexed_color + dpy_x dpy_y rfbport rfbwait rfbauth passwd alwaysshared + dontdisconnect httpdir enablehttpproxy + +-noremote Do not process any remote control commands or queries. + + A note about security wrt remote control commands. + If someone can connect to the X display and change the + property VNC_CONNECT, then they can remotely control + x11vnc. Normally access to the X display is protected. + Note that if they can modify VNC_CONNECT, they could + also run their own x11vnc and have complete control + of the desktop. If the "-connect /path/to/file" + channel is being used, obviously anyone who can write + to /path/to/file can remotely control x11vnc. So be + sure to protect the X display and that file's write + permissions. + +-unsafe If x11vnc is running as root (e.g. inetd or Xsetup for + a display manager) a few remote commands are disabled + (currently: id:pick, accept:<cmd>, and gone:<cmd>) + because they are associated with running external + programs. If you specify -unsafe, then these remote + control commands are allowed when running as root. + When running as non-root all commands are allowed. + See -safer below. +-safer Even if not running as root, disable the above unsafe + remote control commands. + +-deny_all For use with -remote nodeny: start out denying all + incoming clients until "-remote nodeny" is used to + let them in. + These options are passed to libvncserver: @@ -2895,9 +3464,6 @@ yms (the -flashcmap only matters on old 8-bit X displays) - [2]Back to main x11vnc page - References 1. mailto:xvml@karlrunge.com - 2. http://www.karlrunge.com/x11vnc/index.html diff --git a/x11vnc/tkx11vnc b/x11vnc/tkx11vnc new file mode 100755 index 0000000..2119d02 --- /dev/null +++ b/x11vnc/tkx11vnc @@ -0,0 +1,2187 @@ +#!/bin/sh +# the next line restarts using wish. \ +exec wish "$0" "$@" +catch {rename send {}} +# +# Copyright (c) 2004 Karl J. Runge <runge@karlrunge.com> +# 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. + +# +# tkx11vnc v0.1 +# This is a simple frontend to x11vnc. It uses the remote control +# and query features (-remote/-query aka -R/-Q) to interact with it. +# It is just a quick-n-dirty hack (it parses -help output, etc), but +# it could be of use playing with or learning about the (way too) many +# parameters x11vnc has. +# +# It can be used to interact with a running x11vnc (see the x11vnc +# -gui option), or to set the parameters and then start up x11vnc. +# + +# +# Below is a simple picture of how the gui should be laid out and how +# the menus should be organized. Most menu items correspond to remote +# control commands. A trailing ":" after the item name means it is a string +# to be set rather than a boolean that can be toggled (e.g. the entry +# box must be used). +# +# Some tweak options may be set in the prefix "=" string. +# A means it is an "Action" (not a true variable) +# R means it is an action only valid in remote mode. +# S means it is an action only valid in startup mode. +# Q means it is an action worth querying after running. +# D means it is a good idea to delay a little before querying +# (i.e. perhaps it causes x11vnc to do a lot of work, new fb) +# P means the string can be +/- appended/deleted (string may not +# be the same after the remote command) +# G means gui internal item +# F means can be set via file browse +# -C:val1,... means it will be a checkbox (radio button) +# the "-" means no other options follow +# 0 means to skip the item. +# -- means add a separator +# +proc set_template {} { + global template + set template " +Row: Actions Clients Permissions Keyboard Pointer Help +Row: Displays Screen Tuning Debugging Misc + +Actions + =SA start + =RA stop + =GA attach + =RA detach + -- + =RA ping + =RA update-all + =GA clear-all + -- + =GA Quit + +Help + =GA gui + =GA all + +Clients + =RQA current: + =F connect: + =RQA disconnect: + -- + accept: + gone: + vncconnect + -- + =F httpdir: + httpport: + enablehttpproxy + +Displays + display: + =F auth: + desktop: + rfbport: + =0 gui: + +Screen + =DRA refresh + =DRA reset + =DRA blacken + -- + =D id: + =D sid: + =D scale: + -- + =D overlay + overlay_nocursor + -- + =D visual: + flashcmap + notruecolor + -- + =DP blackout: + =D xinerama + -- + = xrandr + =-C:resize,newfbsize,exit xrandr_mode: + padgeom: + +Keyboard + norepeat + add_keysyms + modtweak + xkb + skip_keycodes: + -- + =FP remap: + -- + clear_mods + clear_keys + +Pointer + =-C:none,arrow,X,some,most cursor: + noxfixes + -- + cursorpos + nocursorshape + -- + buttonmap: + -- + xwarppointer + +Misc + =F rc: + norc + -- + nofb + -- + nobell + nosel + noprimary + -- + bg + =-C:ignore,exit sigpipe: + =0 inetd + -- + =RA remote-cmd: + =GA all-settings + +Debugging + debug_pointer + debug_keyboard + =F logfile: + quiet + -- + =G debug_gui + +Permissions + =RQA lock + =RQA unlock + =SQA deny_all + -- + =FP allow: + localhost + =RA allowonce: + -- + viewonly + shared + forever + -- + =RA noremote + -- + alwaysshared + nevershared + dontdisconnect + -- + viewpasswd: + =F passwdfile: + =0 storepasswd + =F rfbauth: + passwd: + -- + safer + unsafe + +Tuning + =-C:1,2,3,4 pointer_mode: + input_skip: + nodragging + -- + =D noshm + flipbyteorder + onetile + -- + wait: + defer: + nap + screen_blank: + -- + fs: + gaps: + grow: + fuzz: + -- + threads + rfbwait: + -- + progressive: +" +} + +proc set_internal_help {} { + global helptext helpall + + # set some internal item help here: + set helptext(start) " +Launch x11vnc with the settings you have prescribed in the gui. +The x11vnc process is started in an xterm window so you can see the +output, kill it, etc. +" + + set helptext(debug_gui) " +Set debug_gui to get more output printed in the text area. +" + + set helptext(detach) " +No longer be associated with the x11vnc server. Switch to non-connected +state. +" + + set helptext(attach) " +Attach to the x11vnc server, if possible. Switches to connected state +if successful. To change or set the X display use \"Displays -> display\" +" + + set helptext(ping) " +Check if x11vnc still responds to \"ping\" remote command. +" + + set helptext(update-all) " +Query the x11vnc server for the current values of all variables. +Populate the values into the gui's database. +" + + set helptext(clear-all) " +Forget any variable settings either entered in by you or retrieved +from a running x11vnc server. Basically sets everything to 0 or +the string (unset). +" + + set helptext(all-settings) " +Displays the gui's database of all of the x11vnc server's current +settings. Use \"Actions -> update-all\" or \"Control+R\" to +refresh this list if it ever gets out of sync. +" + + set helptext(remote-cmd) " +Run a remote command (-R) or query (-Q) directly. Only a few +remote commands are not on a menu, but for those few you can +run the command directly this way. Just enter the command into +the Entry box when prompted. Use the prefix \"Q:\" to indicate +a -Q query. Examples: \"zero:20,20,100,100\", \"Q:ext_xfixes\" +" + + set helptext(Quit) " +Terminate the tkx11vnc gui. Any x11vnc servers will be left running. +" + + set helptext(current) " +Shows a menu of currently connected VNC clients on the x11vnc server. + +Allows you to find more information about them or disconnect them. +You will be prompted to confirm any disconnections. +" + + set helptext(xrandr_mode) " +Set the -xrandr mode value. +" + + set helptext(all) $helpall + + set helptext(gui) " +tkx11vnc is a simple frontend to x11vnc. Nothing fancy, it merely +provides an interface to each of the many x11vnc command line options and +remote control commands. See \"Help -> all\" for much info about x11vnc. + +Most menu items have a (?) button one can click on to get more information +about the option or command. In most cases it will be text extracted +from that in \"Help -> all\". + +There are two states tkx11vnc can be in: + + 1) Available to control a running x11vnc process. + 2) Getting ready to start a x11vnc process. + +In state 1) the Menu items available in the menus are those that +correspond to the x11vnc \"remote control\" commands. See the -remote +entry under \"Help -> all\" for a complete list. Also available is +the \"Actions -> stop\" item to shut down the running x11vnc server, +thereby changing to state 2). One could also simply \"Actions -> detach\" +leaving the x11vnc server running. \"Actions -> attach\" would +reestablish the connection. + +In state 2) the Menu items available in the menus (Actions, Clients, +etc.) are those that correspond to command line options used in starting +an x11vnc process, and the \"Actions -> start\" item executes +x11vnc thereby changing to state 1). To see what x11vnc startup command +you have built so far, look at the (?) help for \"Actions -> start\" +and it will show you what the command looks like. + +There is much overlap between the menu items available in state 1) +and state 2), but it is worth keeping in mind it is not 100%. +For example, you cannot set passwords or password files in state 1). + +Also note that there may be *two* separate X displays involved, not just +one: 1) the X display x11vnc will be polling (and making available to +VNC viewers), and 2) the X display this GUI is intended to display on. +For example, one might use ssh to access the remote machine where the +GUI would display on :11 and x11vnc would poll display :0. + + +GUI components: +--- ---------- + +At the top of the gui is a info text label where information will +be posted, e.g. when traversing menu items text indicating how to get +help on the item and its current value will be displayed. + +Below the info label is the area where the menu buttons, Actions, +Clients, etc., are presented. If a menu item has a checkbox, +it corresponds to a boolean on/off variable. Otherwise it is +either a string variable, or an action not associated with a +variable (for the most part). + +Below the menu button area is a text label indicating the current x11vnc +X display being polled and the corresponding VNC display name. Both +will be \"(*none*)\" when there is no connection established. + +Below the x11 and vnc displays text label is a text area there scrolling +information about actions being taken and commands being run is displayed. +To scroll use PageUp/PageDown or the arrow keys. + +At the bottom is an entry area. When one selects a menu item that +requires supplying a string value, the label will be set to the +parameter name and one types in the new value. Then one presses the +\"OK\" button or presses \"Enter\" to set the value. Or you can press +\"Skip\" or \"Escape\" to avoid changing the variable. Some variables +are boolean toggles (for example, \"Permissions -> viewonly\") or Radio +button selections. Selecting these menu items will not activate the +entry area but rather toggle the variable directly. + +Cascades: There is a bug not yet worked around for the cascade menus +where the (?) help button gets in the way. To get the mouse over to +the cascade menu click and release mouse to activate the cascade, then +you can click on its items. Dragging with a mouse button held down +will not work (sorry). + +Key Bindings: + + In the Text Area: Control-/ selects all of the text. + Anywhere: Control-d invokes \"Actions -> detach\" + Anywhere: Control-a invokes \"Actions -> attach\" + Anywhere: Control-p invokes \"Actions -> ping\" + Anywhere: Control-u and Control-r invoke \"Actions -> update-all\" + +" +} + +proc center_win {w} { + wm withdraw $w + set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2]; + set y [expr [winfo screenheight $w]/2 - [winfo reqheight $w]/2]; + wm geom $w +$x+$y + wm deiconify $w + update +} + +proc textwidth {text} { + set min 0; + foreach line [split $text "\n"] { + set n [string length $line] + if {$n > $min} { + set min $n + } + } + return $min +} + +proc textheight {text} { + set count 0; + foreach line [split $text "\n"] { + incr count + } + return $count +} + +proc make_toplevel {w {title ""}} { + catch {destroy $w} + toplevel $w; + bind $w <Escape> "destroy $w" + if {$title != ""} { + wm title $w $title + } +} + +proc textwin {name title text} { + global max_text_height max_text_width + global bfont + + set width [textwidth $text] + incr width + if {$width > $max_text_width} { + set width $max_text_width + } + set height [textheight $text] + if {$height > $max_text_height} { + set height $max_text_height + } + + set w ".text_$name" + make_toplevel $w $title + + frame $w.f -bd 0; + pack $w.f -fill both -expand 1 + text $w.f.t -width $width -height $height -setgrid 1 -bd 2 \ + -yscrollcommand "$w.f.y set" -relief ridge -font fixed; + scrollbar $w.f.y -orient v -relief sunken -command "$w.f.t yview"; + button $w.f.b -text "Dismiss" -command "destroy $w" -font $bfont + + $w.f.t insert 1.0 $text; + + bind $w <Enter> "focus $w.f.t" + + wm withdraw $w + pack $w.f.b -side bottom -fill x + pack $w.f.y -side right -fill y; + pack $w.f.t -side top -fill both -expand 1; + update + + center_win $w +} + +proc active_when_connected {item} { + global helpremote helptext + + if {[opt_match G $item]} { + return 1 + } elseif {[is_action $item]} { + if {[opt_match R $item]} { + return 1 + } else { + return 0 + } + } elseif {[info exists helpremote($item)]} { + return 1 + } else { + return 0 + } +} + +proc active_when_starting {item} { + global helpremote helptext + + if {[opt_match G $item]} { + return 1 + } elseif {[is_action $item]} { + if {[opt_match S $item]} { + return 1 + } else { + return 0 + } + } elseif {[info exists helptext($item)]} { + return 1 + } else { + return 0 + } +} + +proc help_win {item} { + global helptext helpremote + global query_ans query_aro; + + set ok 0 + set text "Help on $item:\n\n" + + if {[is_gui_internal $item]} { + ; + } elseif {[is_action $item]} { + append text " + Is a remote control Action (cannot be set).\n"; + } elseif {[active_when_connected $item]} { + append text " + Can be changed in a running x11vnc.\n"; + } else { + append text " - Cannot be changed in a running x11vnc.\n"; + } + if {[is_gui_internal $item]} { + ; + } elseif {[active_when_starting $item]} { + append text " + Can be set at x11vnc startup.\n"; + } else { + append text " - Cannot be set at x11vnc startup.\n"; + } + append text "\n" + + if {[info exists helptext($item)]} { + append text "\n" + if {[is_gui_internal $item]} { + append text "==== x11vnc help: ====\n"; + } else { + append text "==== x11vnc startup option help: ====\n"; + } + append text "\n" + append text $helptext($item) + append text "\n" + set ok 1 + } + + if {[info exists helpremote($item)]} { + append text "\n" + append text "==== x11vnc remote control help: ====\n"; + append text "\n" + append text $helpremote($item) + set ok 1 + } + + if {$item == "start"} { + set str [get_start_x11vnc_txt] + append text $str + append_text "$str\n" + } + + regsub -all { } $item " " name + + if {$ok} { + textwin $name "x11vnc help: $item" "$text"; + } + return $ok +} + +proc parse_help {} { + global env x11vnc_prog; + global helpall helptext; + + set helppipe [open "| $x11vnc_prog -help" "r"]; + if {$helppipe == ""} { + puts stderr "failed to run $x11vnc_prog -help"; + exit 1; + } + + set sawopts 0; + set curropt ""; + while {[gets $helppipe line] > -1} { + append helpall "$line\n" + + # XXX + if {[regexp {^Options:} $line]} { + set sawopts 1; + continue; + } + # XXX + if {[regexp {^These options} $line]} { + continue; + } + + if {! $sawopts} { + continue; + } + if {[regexp {^-([A-z_][A-z_]*)} $line match name]} { + set allnames($name) 1; + if {"$curropt" != "no$name" && "no$curropt" != "$name"} { + set curropt $name; + set helptext($curropt) "$line\n"; + } else { + append helptext($curropt) "$line\n"; + } + } elseif {$curropt != ""} { + append helptext($curropt) "$line\n"; + } + } + foreach name [array names allnames] { + if {[regexp {^no} $name]} { + regsub {^no} $name "" pair + } else { + set pair "no$name" + } + if {[info exists helptext($name)]} { + if ![info exists helptext($pair)] { + set helptext($pair) $helptext($name); + } + } elseif {[info exists helptext($pair)]} { + if ![info exists helptext($name)] { + set helptext($name) $helptext($pair); + } + } + } + + set_internal_help +} + +proc tweak_both {new old} { + tweak_help $new $old + tweak_remote_help $new $old +} + +proc tweak_remote_help {new old} { + global helpremote + if ![info exists helpremote($new)] { + if {[info exists helpremote($old)]} { + set helpremote($new) $helpremote($old) + } + } +} + +proc tweak_help {new old} { + global helptext + if ![info exists helptext($new)] { + if {[info exists helptext($old)]} { + set helptext($new) $helptext($old) + } + } +} + +proc parse_remote_help {} { + global helpremote helptext help_indent remote_name; + + set sawopts 0; + set curropt ""; + set possopts ""; + set offset [expr $help_indent - 1]; + foreach line [split $helptext(remote) "\n"] { + + set line [string range $line $offset end]; + + # XXX + if {[regexp {^The following -remote/-R commands} $line]} { + set sawopts 1; + continue; + } + # XXX + if {[regexp {^The vncconnect.*command} $line]} { + set sawopts 0; + } + + if {! $sawopts} { + continue; + } + if {[regexp {^([A-z_][A-z_:]*)} $line match name]} { + regsub {:.*$} $name "" popt + lappend possopts $popt + if {"$curropt" != "no$name" && "no$curropt" != "$name"} { + set curropt $name; + regsub {:.*$} $curropt "" curropt + set remote_name($curropt) $name + set helpremote($curropt) "$line\n"; + } else { + append helpremote($curropt) "$line\n"; + } + } elseif {$curropt != ""} { + append helpremote($curropt) "$line\n"; + } + } + + foreach popt $possopts { + if {[info exists helpremote($popt)]} { + continue + } + if {[regexp {^no} $popt]} { + regsub {^no} $popt "" try + } else { + set try "no$popt" + } + if {[info exists helpremote($try)]} { + set helpremote($popt) $helpremote($try) + } + } +} + +proc parse_query_help {} { + global query_ans query_aro query_ans_list query_aro_list helptext; + + set sawans 0; + set sawaro 0; + set ans_str "" + set aro_str "" + + foreach line [split $helptext(query) "\n"] { + + if {! $sawans && [regexp {^ *ans=} $line]} { + set sawans 1 + } + if {! $sawans} { + continue + } + + if {[regexp {^ *aro=} $line]} { + set sawaro 1 + } + if {$sawaro && [regexp {^[ ]*$} $line]} { + set sawans 0 + break + } + + regsub {ans=} $line "" line + regsub {aro=} $line "" line + set line [string trim $line] + + if {$sawaro} { + set aro_str "$aro_str $line" + } else { + set ans_str "$ans_str $line" + } + } + + regsub -all { *} $ans_str " " ans_str + regsub -all { *} $aro_str " " aro_str + + set ans_str [string trim $ans_str] + set aro_str [string trim $aro_str] + set query_ans_list [split $ans_str] + set query_aro_list [split $aro_str] + + foreach item $query_ans_list { + if {[regexp {^[ ]*$} $item]} { + continue + } + set query_ans($item) 1 + } + foreach item $query_aro_list { + if {[regexp {^[ ]*$} $item]} { + continue + } + set query_aro($item) 1 + } +} + +proc in_debug_mode {} { + global menu_var + if {![info exists menu_var(debug_gui)]} { + return 0 + } + return $menu_var(debug_gui) +} + +# Menubar utilities: +proc menus_state {state} { + global menu_b + + foreach case [array names menu_b] { + set menu_button $menu_b($case) + $menu_button configure -state $state + } +} + +proc menus_enable {} { + menus_state "normal" +} + +proc menus_disable {} { + menus_state "disabled" +} + +# Entry box utilities: +proc entry_state {x state} { + global entry_box entry_label entry_ok entry_help entry_skip entry_browse + if {$x == "all"} { + $entry_label configure -state $state + $entry_box configure -state $state + $entry_ok configure -state $state + $entry_skip configure -state $state + $entry_help configure -state $state + $entry_browse configure -state $state + } elseif {$x == "label"} { + $entry_label configure -state $state + } elseif {$x == "box"} { + $entry_box configure -state $state + } elseif {$x == "ok"} { + $entry_ok configure -state $state + } elseif {$x == "skip"} { + $entry_skip configure -state $state + } elseif {$x == "help"} { + $entry_help configure -state $state + } elseif {$x == "browse"} { + $entry_browse configure -state $state + } +} + +proc entry_enable {{x "all"}} { + entry_state $x normal +} + +proc entry_disable {{x "all"}} { + entry_state $x disabled +} + +proc entry_browse_button {{show 1}} { + global entry_browse + if {$show} { + pack $entry_browse -side left + } else { + pack forget $entry_browse + } +} +proc entry_focus {} { + global entry_box + focus $entry_box +} +proc entry_select {} { + global entry_box + $entry_box selection range 0 end +} +proc entry_get {} { + global entry_box + return [$entry_box get] +} +proc entry_insert {str} { + global entry_box + entry_delete + $entry_box insert end $str + $entry_box icursor end +} +proc entry_delete {} { + global entry_box + $entry_box delete 0 end +} + + +# Utilities for remote control and updating vars. + +proc push_new_value {item name new {query 1}} { + global menu_var always_update remote_output query_output + global delay_sleep extra_sleep extra_sleep_split + + set debug [in_debug_mode] + set do_query_all 0 + set getout 0 + + if {$item == "remote-cmd"} { + # kludge for arbitrary remote command: + if {[regexp {^Q:} $new]} { + # extra kludge for Q:var to mean -Q var + regsub {^Q:} $new "" new + set qonly 1 + } else { + set qonly 0 + } + # need to extract item from new: + set qtmp $new + regsub {:.*$} $qtmp "" qtmp + if {! $qonly} { + set rargs [list "-R" "$new"] + set qargs [list "-Q" "$qtmp"] + set getout 1 + } else { + set rargs [list "-Q" "$qtmp"] + set qargs [list "-Q" "$qtmp"] + } + + } elseif {[value_is_string $item]} { + set rargs [list "-R" "$name:$new"] + set qargs [list "-Q" "$name"] + } else { + set rargs [list "-R" "$name"] + set qargs [list "-Q" "$name"] + } + + if {!$debug} { + append_text "x11vnc $rargs ..." + } + set remote_output [run_remote_cmd $rargs] + + if {[lindex $rargs 0] == "-Q"} { + append_text "\t$remote_output" + set getout 1 + } elseif {! $query && ! $always_update} { + set getout 1 + } elseif {$item == "noremote"} { + set getout 1 + } elseif {[is_action $item] && ![opt_match Q $item] && $rargs != ""} { + set getout 1 + } elseif {[regexp {^(sid|id)$} $item] && ![regexp {^0x} $new]} { + set getout 1 + } + + if {$getout} { + append_text "\n" + return + } + + stop_watch on + after $delay_sleep + if {[opt_match D $item]} { + set s [expr $extra_sleep/$extra_sleep_split] + append_text " " + for {set i 0} {$i<$extra_sleep_split} {incr i} { + after $s + append_text "." + update + } + } + stop_watch off + + if {!$debug} { + append_text ", -Q ..." + } + + if {$item == "disconnect"} { + set new "N/A" + set do_query_all 1 + } + + if {$always_update || $do_query_all} { + set query [query_all 1] + } else { + set query [run_remote_cmd $qargs] + } + set query_output $query + + if {![see_if_ok $query $item "$name:$new"]} { + # failed + if {[regexp {^a..=} $query]} { + # but some result came back + if {! $always_update} { + # synchronize everything + set query_output [query_all 1] + } + } else { + # server may be dead + if {$item != "ping" && $item != "attach"} { + try_connect + } + } + } else { + # succeeded + if {! $always_update} { + # synchronize this variable + update_menu_vars $query + } else { + # already done in query_all + } + } +} + +# For updating a string variable. Also used for simple OK/Skip dialogs +# with entry = 0. +proc entry_dialog {item {entry 1}} { + global menu_var entry_str entry_set entry_dialog_item + global unset_str connected_to_x11vnc + + set entry_str "Set $item" + set entry_set 0 + set entry_dialog_item $item + + entry_enable + menus_disable + + if {$entry} { + entry_insert "" + if {[info exists menu_var($item)] && + $menu_var($item) != $unset_str} { + entry_insert $menu_var($item) + entry_select + } + + if {[is_browse $item]} { + entry_browse_button + } + set_info "Set parameter in entry box, " + entry_focus + } else { + entry_disable box + } + + update + + # wait for user reply: + vwait entry_set + + set rc $entry_set + set entry_set 0 + + set value [entry_get] + update + + entry_browse_button 0 + set entry_str "Set... :" + + entry_delete + entry_disable + menus_enable + update + + if {! $entry} { + ; + } elseif {$rc} { + set menu_var($item) $value + } else { + if {[in_debug_mode]} { + append_text "skipped setting $item\n" + } + } + return $rc +} + +proc warning_dialog {msg {item "gui"} } { + append_text $msg + # just reuse the entry widgets for a yes/no dialog + return [entry_dialog $item 0] +} + +# For updating a boolean toggle: +proc check_var {item} { + global menu_var + + set inval $menu_var($item); + + if {$item == "debug_gui"} { + return ""; + } + + set rname $item + if {! $inval} { + if {[regexp {^no} $item]} { + regsub {^no} $rname "" rname + } else { + set rname "no$rname" + } + } + return $rname +} + +proc see_if_ok {query item expected} { + set ok 0 + set found "" + foreach q [split_query $query] { + if {[regexp "^$item:" $q]} { + set found $q + } + if {$q == $expected} { + set ok 1 + } + } + if {$found == ""} { + set msg $query + regsub {^a..=} $msg {} msg + if {[string length $msg] > 60} { + set msg [string range $msg 0 60] + } + } else { + set msg $found + } + if {$ok} { + append_text "\tSet OK ($msg)\n" + return 1 + + } elseif {[opt_match P $item] && [regexp {:(-|\+)} $expected]} { + # e.g. blackout:+30x30+20+20 + append_text "\t($msg)\n" + return 1 + } else { + append_text "\t*FAILED* $msg\n" + return 0 + } +} + +proc update_menu_vars {{query ""}} { + global all_settings menu_var + + set debug [in_debug_mode] + + if {$query == ""} { + set qstr $all_settings + } else { + set qstr $query + } + foreach piece [split_query $qstr] { + if {[regexp {^([^:][^:]*):(.*)$} $piece m0 item val]} { + if {[info exists menu_var($item)]} { + set old $menu_var($item) + if {$val == "N/A"} { + continue + } + if {$debug} { + puts "setting menuvar: $item: $old -> $val" + } + set menu_var($item) $val + } + if {$item == "clients"} { + update_clients_menu $val + } + } + } +} + +proc clear_all {} { + global menu_var unset_str + + set debug [in_debug_mode] + + foreach item [array names menu_var] { + if {$item == "debug_gui"} { + continue + } + if {[info exists menu_var($item)]} { + if [is_action $item] { + set menu_var($item) "" + } elseif {[value_is_bool $item]} { + set menu_var($item) 0 + } elseif {[value_is_string $item]} { + set menu_var($item) $unset_str + } + } + } +} + +proc all_query_vars {} { + global query_ans_list query_aro_list all_settings + + set qry "" + foreach item $query_ans_list { + if {$qry == ""} { + set qry $item + } else { + append qry ",$item" + } + } + foreach item $query_aro_list { + if {$qry == ""} { + set qry $item + } else { + append qry ",$item" + } + } + return $qry +} + +proc query_all {{quiet 0}} { + global query_ans_list query_aro_list all_settings + + set qry [all_query_vars] + + #puts "into query_all $quiet" + + set qargs [list "-Q" $qry] + set all [run_remote_cmd $qargs] + + if {[regexp {ans=} $all]} { + if {! $quiet} { + append_text "Retrieved all settings.\n" + } + set all_settings $all + update_menu_vars $all + } else { + if {! $quiet} { + append_text "Failed to retrieve settings.\n" + } + } + return $all +} + +proc set_info {str} { + global info_str + set info_str "$str" + update +} + +proc append_text {str} { + global text_area + $text_area insert end $str + $text_area see end +} + +proc show_all_settings {} { + global all_settings + set txt "\nRead-Write setting:\n\n" + foreach item [split_query $all_settings] { + regsub {:} $item {: } item + append txt " $item\n" + if {[regexp {noremote} $item]} { + append txt "\nRead-Only setting:\n\n" + } + } + textwin "Settings" "All Current Settings" $txt +} + +proc set_connected {yesno} { + global connected_to_x11vnc + set orig $connected_to_x11vnc + + if {$yesno == "yes"} { + set connected_to_x11vnc 1 + } else { + set connected_to_x11vnc 0 + no_x11_display + no_vnc_display + } + if {$orig != $connected_to_x11vnc} { + set_widgets + } +} + +proc detach_from_display {} { + global connected_to_x11vnc reply_xdisplay x11vnc_xdisplay + set str "Detaching from X display." + if {$reply_xdisplay != ""} { + set str "Detaching from $reply_xdisplay." + } elseif {$x11vnc_xdisplay != ""} { + set str "Detaching from $x11vnc_xdisplay." + } + if {$connected_to_x11vnc} { + append_text "$str\n" + } + set_connected no +} + +# Menu item is an action: +proc do_action {item} { + global menu_var connected_to_x11vnc + + if {[in_debug_mode]} { + append_text "action: \"$item\"\n" + } + + if {$item == "ping"} { + try_connect + return + } elseif {$item == "start"} { + start_x11vnc + return + } elseif {$item == "detach"} { + detach_from_display + return + } elseif {$item == "attach"} { + try_connect_and_query_all + return + } elseif {$item == "update-all"} { + query_all + return + } elseif {$item == "clear-all"} { + clear_all + return + } elseif {$item == "all-settings"} { + show_all_settings + return + } + + if {[value_is_string $item]} { + if {! [entry_dialog $item]} { + return + } + set new $menu_var($item) + set name $item + } else { + set new 1 + set name $item + } + + if {! $connected_to_x11vnc} { + ; + } elseif {[regexp {^(stop|quit|exit|shutdown)$} $item]} { + # just do -R + append_text "stopping remote x11vnc server...\n" + push_new_value $item $name $new 0 + set_connected no + + } elseif [opt_match Q $item] { + push_new_value $item $name $new 1 + } else { + push_new_value $item $name $new 0 + } +} + +proc do_var {item} { + global connected_to_x11vnc item_cascade menu_var + + set string 0 + if {[is_action $item]} { + # Menu item is action: + do_action $item + return + } + + if {[value_is_string $item]} { + # Menu item is a string: + if {$item_cascade($item) != ""} { + # Cascade sets variable automatically + } else { + # Otherwise Entry box + if {![entry_dialog $item]} { + return + } + } + set new $menu_var($item) + set name $item + } else { + # Menu item is a boolean: + set name [check_var $item] + if {$name == ""} { + return + } + set new 1 + } + if {$connected_to_x11vnc} { + push_new_value $item $name $new 1 + } +} + +proc menu_help {item} { + if ![help_win $item] { + textwin "nohelp" "No help available" \ + "Sorry, no help avaiable for \"$item\"" + } +} + +proc opt_match {c item} { + global item_opts + if {[info exists item_opts($item)]} { + if {[regexp "^\[A-z\]*$c" $item_opts($item)]} { + return 1 + } + } + return 0 +} + +proc is_action {item} { + return [opt_match A $item] +} + +proc is_gui_internal {item} { + return [opt_match G $item] +} + +proc is_browse {item} { + return [opt_match F $item] +} + +proc value_is_string {item} { + global item_bool + if {! $item_bool($item)} { + return 1 + } else { + return 0 + } +} + +proc value_is_bool {item} { + global item_bool + if {$item_bool($item)} { + return 1 + } else { + return 0 + } +} + +proc split_query {query} { + regsub -all {aro=} $query {ans=} query + set items {} + while {1} { + if {! [regexp {^ans=(.*)$} $query m0 m1]} { + break + } + set item $m1 + set m2 "" + regexp {,ans=.*$} $item m2 + regsub {,ans=.*$} $item "" item + if {$item != ""} { + lappend items $item + } + set query $m2 + regsub {^,} $query "" query + } + return $items +} + +proc set_x11_display {name} { + global x11_display + set x11_display "x11vnc X display: $name" +} +proc set_vnc_display {name} { + global vnc_display + set vnc_display "VNC display: $name" +} +proc no_x11_display {} { + set_x11_display "(*none*)" +} +proc no_vnc_display {} { + set_vnc_display "(*none*)" +} +proc fetch_displays {} { + + set qargs [list "-Q" "display,vncdisplay"] + set result [run_remote_cmd $qargs] + + set got_x11 0 + set got_vnc 0 + + foreach item [split_query $result] { + if {[regexp {^display:(.*)$} $item m0 m1]} { + set_x11_display $m1 + set got_x11 1 + } elseif {[regexp {^vncdisplay:(.*)$} $item m0 m1]} { + set_vnc_display $m1 + set got_vnc 1 + } + } + if {! $got_x11} { + no_x11_display + } + if {! $got_vnc} { + no_vnc_display + } +} + +proc disconnect_dialog {client} { + set cid "" + set host "" + set msg "\n" + append msg "*** Client info string: $client\n" + if {[regexp {^(.*):(.*)/(.*)-(.*)$} $client m0 m1 m2 m3 m4]} { + if {$m4 == "ro"} { + set view "(viewonly)" + } else { + set view "(interactive)" + } + set host $m1 + set cid $m3 + append msg "*** Host: $m1, Port: $m2 Id: $m3 $view\n" + } + if {$cid == ""} { + append_text "Invalid client info string: $client\n" + return + } + append msg "*** To disconnect this client press \"OK\", otherwise press \"Skip\"\n" + bell + if [warning_dialog $msg "current"] { + push_new_value "disconnect" "disconnect" $cid 1 + } else { + append_text "disconnect cancelled.\n" + } +} + +proc update_clients_menu {list} { + global item_cascade + set subm $item_cascade(current); + catch {destroy $subm} + menu $subm -tearoff 0 + $subm add command + $subm add separator + set count 0 + foreach client [split $list ","] { + regsub {:[0-9][0-9]*/} $client {/} lab + $subm add command -label "$client" \ + -command "disconnect_dialog $client" + incr count + } + $subm entryconfigure 0 -label "#clients: $count" +} + +proc set_widgets {} { + global connected_to_x11vnc item_case item_entry menu_m + + foreach item [array names item_case] { + set case $item_case($item) + set menu $menu_m($case) + set entry $item_entry($item) + set type [$menu type $entry] + if {$type == "separator" || $type == "tearoff"} { + continue + } + if {$connected_to_x11vnc} { + if {[active_when_connected $item]} { + $menu entryconfigure $entry -state normal + } else { + $menu entryconfigure $entry -state disabled + } + } else { + if {[active_when_starting $item]} { + $menu entryconfigure $entry -state normal + } else { + $menu entryconfigure $entry -state disabled + } + } + } +} + +proc make_widgets {} { + global template + global menu_b menu_m + global item_opts item_bool item_case item_entry menu_var unset_str + global item_cascade + global info_str x11_display vnc_display + global text_area + global entry_box entry_str entry_set entry_label entry_ok entry_browse + global entry_help entry_skip + global bfont + global helptext helpremote helplabel + +set v 0 + + label .info -textvariable info_str -bd 2 -relief groove -anchor w + pack .info -side top -fill x + + # Extract the Rows: + set row 0; + set colmax 0; + foreach line [split $template "\n"] { + if {[regexp {^Row: (.*)} $line rest]} { + set col 0 + foreach case [split $rest] { + if {$case == "" || $case == "Row:"} { + continue + } + set menu_row($case) $row + set menu_col($case) $col + set menu_count($case) 0 + + lappend cases($col) $case; + set len [string length $case] + if {[info exists max_len($col)]} { + if {$len > $max_len($col)} { + set max_len($col) $len + } + } else { + set max_len($col) $len + } + incr col + if {$col > $colmax} { + set colmax $col + } + } + incr row; + } + } + + # Make frames for the rows and make the menu buttons. + set f ".menuframe" + frame $f + for {set c 0} {$c < $colmax} {incr c} { + set colf "$f.menuframe$c" + frame $colf + pack $colf -side left -fill y + set fbg [$colf cget -background] + foreach case $cases($c) { + set menub "$colf.menu$case"; + set menu "$colf.menu$case.menu"; + set menu_b($case) $menub + set menu_m($case) $menu + menubutton $menub -text "$case" -underline 0 \ + -anchor w -menu $menu -background $fbg \ + -font $bfont + pack $menub -side top -fill x + menu $menu -tearoff 0 + } + } + pack $f -side top -fill x + + # Now extract the menu items: + set case ""; + foreach line [split $template "\n"] { + if {[regexp {^Row:} $line]} { + continue + } + if {[regexp {^[A-z]} $line]} { + set case [string trim $line] + continue; + } + set item [string trim $line] + regsub -all { *} $item " " item + if {$item == ""} { + continue; + } + set opts "" + if {[regexp {^=} $item]} { + set opts [lindex [split $item] 0] + regsub {^=} $opts "" opts + set item [lindex [split $item] 1] + } + if {[regexp {^0} $opts]} { + continue; + } + if {[regexp {:$} $item]} { + set bool 0 + } else { + set bool 1 + } + regsub {:$} $item {} item + + set item_opts($item) $opts + set item_case($item) $case + set item_bool($item) $bool + set item_cascade($item) "" + set item_entry($item) $menu_count($case) + +if {$v} { puts "ITEM: $item - $opts - $case - $bool - $menu_count($case)" } + + set mvar 0 + set m $menu_m($case) + + # Create the menu items, its variables, etc., etc. + + if {$item == "--"} { + $m add separator + + } elseif {$item == "Quit"} { + # Quit item must shut us down: + $m add command -label "$item" -underline 0 \ + -command {destroy .; exit 0} + + } elseif {$case == "Help"} { + # Help is simple help: + $m add command -label "$item" \ + -command "menu_help $item" + + } elseif {$item == "current"} { + # Current clients cascade + set subm $m.cascade$menu_count($case) + set item_cascade($item) $subm + update_clients_menu "" + $m add cascade -label "$item" \ + -menu $subm + + } elseif {[is_action $item]} { + # Action + $m add command -label "$item" \ + -command "do_var $item" + set menu_var($item) ""; # for convenience + + } elseif {! $item_bool($item)} { + # String + if {[regexp -- {-C:(.*)} $item_opts($item) m0 m1]} { + # Radiobutton select + set subm $m.cascade$menu_count($case) + menu $subm -tearoff 0 + foreach val [split $m1 ","] { + $subm add radiobutton -label "$val" \ + -command "do_var $item" \ + -value "$val" \ + -variable menu_var($item) + } + $m add cascade -label "$item" \ + -menu $subm + set item_cascade($item) $subm + } else { + # Arbitrary_string + $m add command -label "$item" \ + -command "do_var $item" + } + set mvar 1 + + } else { + # Boolean + $m add checkbutton -label "$item" \ + -command "do_var $item" \ + -variable menu_var($item) + set menu_var($item) 0 + } + + incr menu_count($case) + if {$mvar} { + set menu_var($item) $unset_str + } + } + + # Now make the litte "(?)" help buttons + foreach case [array names menu_m] { + if {$case == "Help"} { + continue; + } + set m $menu_m($case); + set n [$m index end] + +if {$v} { puts "$case end: $n" } + + for {set i 0} {$i <= $n} {incr i} { + set type [$m type $i] + if {$type == "separator"} { + $m add separator + } elseif {$type == "tearoff"} { + continue; + } else { + set label [$m entrycget $i -label] + set str "" + if {[info exists helpremote($label)]} { + set str "(?)" + } elseif {[info exists helptext($label)]} { + set str "(?)" + } + $m add command -label $str \ + -command "menu_help $label"; + +if {$v} { + set ht ""; set hr "" + if {[info exists helptext($label)]} { set ht "YES" } + if {[info exists helpremote($label)]} { set hr "YES" } + puts "'$label'\tht='$ht' hr='$hr'" +} + + if {$str == ""} { + $m entryconfigure end -state disabled + } + set arg "$m,$i" + set helplabel($arg) $label + set j [$m index end] + set arg "$m,$j" + set helplabel($arg) $label + } + if {$i == 0} { + $m entryconfigure end -columnbreak 1 + } + } + } + + # Make the x11 and vnc display label bar: + set df .displayframe + frame $df -bd 1 -relief groove + + set df_x11 "$df.xdisplay" + no_x11_display + label $df_x11 -textvariable x11_display -width 35 -anchor w + + set df_vnc "$df.vdisplay" + no_vnc_display + label $df_vnc -textvariable vnc_display -width 35 -anchor w + + pack $df_x11 $df_vnc -side left + pack $df -side top -fill x + + # text area + text .text -height 11 -relief ridge + set text_area .text + pack .text -side top -fill both -expand 1 + + + set str "Click Help -> gui for overview." + append_text "\n$str\n\n" + + # Make entry box stuff + set ef .entryframe + frame $ef -bd 1 -relief groove + + # Label + set ef_label "$ef.label" + label $ef_label -textvariable entry_str -anchor w -font $bfont + + set entry_str "Set... : " + set ef_entry "$ef.entry" + entry $ef_entry -relief sunken + bind $ef_entry <KeyPress-Return> {set entry_set 1} + bind $ef_entry <KeyPress-Escape> {set entry_set 0} + + # OK button + set ef_ok "$ef.ok" + button $ef_ok -text OK -pady 1 -command {set entry_set 1} \ + -font $bfont + + # Skip button + set ef_skip "$ef.skip" + button $ef_skip -text Skip -pady 0 -command {set entry_set 0} \ + -font $bfont + + # Help button + set ef_help "$ef.help" + button $ef_help -text Help -pady 0 -command \ + {menu_help $entry_dialog_item} -font $bfont + + # Browse button + set ef_browse "$ef.browse" + button $ef_browse -text "Browse..." -pady 0 -font $bfont \ + -command {entry_insert [tk_getOpenFile]} + + pack $ef_label -side left + pack $ef_entry -side left -fill x -expand 1 + pack $ef_ok -side right + pack $ef_skip -side right + pack $ef_help -side right + pack $ef -side bottom -fill x + + set entry_ok $ef_ok + set entry_skip $ef_skip + set entry_help $ef_help + set entry_box $ef_entry + set entry_browse $ef_browse + set entry_label $ef_label + entry_disable + + update + wm minsize . [winfo width .] [winfo height .] +} + +proc menu_bindings {} { + bind Menu <<MenuSelect>> { +#syntax hilite bug \ +MenuSelect>> + set n [%W index active] + set label " " + if {$n != "none"} { + set str %W,$n + set which "" + if {[info exists helplabel($str)]} { + set vname [format %%-16s $helplabel($str)] + set label "Click (?) for help on: $vname" + set which $helplabel($str) + } + if {$which == ""} { + ; + } elseif {[is_action $which]} { + if {[info exists menu_var($which)] + && $menu_var($which) != ""} { + set label "$label value: $menu_var($which)" + } else { + set label "$label (is action)" + } + } elseif {[info exists menu_var($which)]} { + set label "$label value: $menu_var($which)" + } + } + set_info $label + } +} + +proc key_bindings {} { + global env + if {[info exists env(USER)] && $env(USER) == "runge"} { + # quick restart + bind . <Control-KeyPress-c> {exec $argv0 $argv &; destroy .} + } + bind . <Control-KeyPress-p> {try_connect_and_query_all} + bind . <Control-KeyPress-u> {query_all 0} + bind . <Control-KeyPress-r> {query_all 0} + bind . <Control-KeyPress-d> {detach_from_display} + bind . <Control-KeyPress-a> {try_connect_and_query_all} +} + +proc stop_watch {onoff} { + global orig_cursor text_area entry_box + + set widgets [list . $text_area $entry_box] + + if {$onoff == "on"} { + foreach item $widgets { + $item config -cursor {watch} + } + } else { + foreach item $widgets { + $item config -cursor {} + } + } + update +} + +proc double_check_noremote {} { + set msg "\n\n" + append msg "WARNING: setting \"noremote\" will disable ALL remote control commands\n" + append msg "WARNING: (i.e. this gui will be locked out) Do you really want to do this?\n" + append msg "WARNING: If so, press \"OK\", otherwise press \"Skip\"\n" + append msg "\n" + bell + return [warning_dialog $msg "noremote"] +} + +proc double_check_start_x11vnc {} { + global hostname + set msg [get_start_x11vnc_txt] + append msg "\n" + append msg "*** To run the above command on machine \"$hostname\" to\n" + append msg "*** start x11vnc press \"OK\" otherwise press \"Skip\".\n" + return [warning_dialog $msg "start"] +} + +proc get_start_x11vnc_txt {} { + set cmd [get_start_x11vnc_cmd] + set str [join $cmd] + set msg "" + append msg "\n" + append msg "==== The command built so far is: ====\n"; + append msg "\n" + append msg "$str\n" + return $msg +} + +proc get_start_x11vnc_cmd {} { + global menu_var unset_str x11vnc_prog + + set xterm_cmd "xterm -iconic -geometry 80x35 -title x11vnc-console -e" + + set cmd [split $xterm_cmd] + + lappend cmd $x11vnc_prog + + foreach item [lsort [array names menu_var]] { + if {![active_when_starting $item]} { + continue + } + if {[is_action $item]} { + continue + } + + if {[value_is_bool $item]} { + if {[info exists menu_var($item)]} { + if {$menu_var($item)} { + lappend cmd "-$item" + } + } + } elseif {[value_is_string $item]} { + if {[info exists menu_var($item)]} { + if {$menu_var($item) != "" + && $menu_var($item) != $unset_str} { + lappend cmd "-$item" + lappend cmd $menu_var($item) + } + } + } + } + lappend cmd "2>" + lappend cmd "/dev/null" + lappend cmd "&" + + return $cmd +} + +proc start_x11vnc {} { + global menu_var unset_str + global x11vnc_prog x11vnc_xdisplay + global connected_to_x11vnc + + if {$connected_to_x11vnc} { + append_text "\n" + append_text "WARNING: Still connected to an x11vnc server.\n" + append_text "WARNING: Use \"stop\" or \"detach\" first.\n" + return 0 + } + + if {![double_check_start_x11vnc]} { + return + } + + set x11vnc_xdisplay "" + if {[info exists menu_var(display)]} { + if {$menu_var(display) != "" && $menu_var(display) != $unset_str} { + set x11vnc_xdisplay $menu_var(display) + } + } + + set cmd [get_start_x11vnc_cmd] + + set str [join $cmd] + regsub { -e} $str " -e \\\n " str + + if {0} { + puts "running: $str" + foreach word $cmd { + puts " word: $word" + } + } + + append_text "Starting x11vnc in an iconified xterm with command:\n" + append_text " $str\n\n" + catch {[eval exec $cmd]} + after 500 + try_connect_and_query_all 3 +} + +proc run_remote_cmd {opts} { + global menu_var x11vnc_prog x11vnc_cmdline x11vnc_xdisplay + + set debug [in_debug_mode] + + if {[lindex $opts 0] == "-R" && [lindex $opts 1] == "noremote"} { + set str [join $opts] + if ![double_check_noremote] { + append_text "skipping: x11vnc $str" + return "" + } else { + append_text "running: x11vnc $str (please do \"Actions -> detach\" to clean things up)\n" + append_text "subsequent -R/-Q commands should fail..." + } + } + + set cmd "" + + lappend cmd $x11vnc_prog; + + if {$x11vnc_xdisplay != ""} { + lappend cmd "-display" + lappend cmd $x11vnc_xdisplay + } + foreach word $opts { + lappend cmd $word + } + lappend cmd "2>" + lappend cmd "/dev/null" + +if {0} { + set str [join $cmd] + puts "running: $str" + foreach word $cmd { + puts " word: $word" + } +} + + set output "" + stop_watch on + catch {set output [eval exec $cmd]} + stop_watch off + if {$debug} { + append_text "output: $output\n" + } + return $output +} + +proc try_connect_and_query_all {{n 2}} { + for {set i 0} {$i < $n} {incr i} { + if {$i > 0} { + after 500 + append_text "trying again ...\n" + } + if {[try_connect]} { + query_all + break + } + } +} + +proc try_connect {} { + global x11vnc_xdisplay connected_to_x11vnc reply_xdisplay + global menu_var unset_str + + if {! $connected_to_x11vnc} { + if {[info exists menu_var(display)]} { + set d $menu_var(display) + if {$d != "" && $d != $unset_str && $d != $x11vnc_xdisplay} { + set x11vnc_xdisplay $menu_var(display) + append_text "Setting X display to: $x11vnc_xdisplay\n" + } + } + } + + set_info "Pinging $x11vnc_xdisplay ..." + set rargs [list "-Q" "ping"] + set result [run_remote_cmd $rargs] + + if {[regexp {^ans=ping:} $result]} { + regsub {^ans=ping:} $result {} reply_xdisplay + set msg "Connected to $reply_xdisplay" + set_info $msg + append_text "$msg\n" + set_connected yes + fetch_displays + return 1 + } else { + set str "x11vnc server." + if {$x11vnc_xdisplay != ""} { + set str $x11vnc_xdisplay + } + set msg "No reply from $str" + set_info $msg + append_text "$msg\n" + set_connected no + return 0 + } +} + +############################################################################ +# main: + +global env x11vnc_prog x11vnc_cmdline x11vnc_xdisplay x11vnc_connect; +global helpall helptext helpremote helplabel hostname; +global all_settings reply_xdisplay always_update +global max_text_height max_text_width +global menu_var unset_str +global bfont +global connected_to_x11vnc +global delay_sleep extra_sleep extra_sleep_split + +set unset_str "(unset)" +set connected_to_x11vnc 0 +set max_text_height 40 +set max_text_width 90 +set bfont -adobe-helvetica-bold-r-*-*-*-120-*-*-*-*-*-*; +set help_indent 24; +set reply_xdisplay "" +set all_settings "None so far." +set always_update 1 + +#set delay_sleep 500 +#set extra_sleep 1500 +set delay_sleep 350 +set extra_sleep 1000 +set extra_sleep_split 4 + +if {"$argv" == "-spit"} { + set fh [open $argv0 r] + puts "/*" + puts " * tkx11vnc.h: generated by 'tkx11vnc -spit'" + puts " * Abandon all hope, ye who enter here..." + puts " * ...edit tkx11vnc instead." + puts " */" + puts " char gui_code\[\] =" + while {[gets $fh line] > -1} { + regsub -all {\\} $line {\\\\} line + regsub -all {"} $line {\\"} line + puts "\"$line\\n\"" + } + close $fh + puts ";" + exit 0 +} + +# Read environment for clues: +if {[info exists env(X11VNC_PROG)]} { + set x11vnc_prog $env(X11VNC_PROG); +} else { + set x11vnc_prog "x11vnc"; +} + +if {[info exists env(X11VNC_CMDLINE)]} { + set x11vnc_cmdline $env(X11VNC_CMDLINE); +} else { + set x11vnc_cmdline ""; +} + +if {[info exists env(X11VNC_CONNECT)]} { + set x11vnc_connect 1 +} else { + set x11vnc_connect 0; +} + +if {[info exists env(X11VNC_XDISPLAY)]} { + set x11vnc_xdisplay $env(X11VNC_XDISPLAY); + set x11vnc_connect 1 + +} elseif {$argv != "" && [regexp {:[0-9]} $argv]} { + set x11vnc_xdisplay "$argv" + set x11vnc_connect 1 + +} elseif {[info exists env(DISPLAY)]} { + set x11vnc_xdisplay $env(DISPLAY); +} else { + set x11vnc_xdisplay ":0"; +} + +set hostname [exec uname -n] +#puts [exec env] +#puts "x11vnc_xdisplay: $x11vnc_xdisplay" + +set env(X11VNC_STD_HELP) 1 + +# scrape the help output for the text and remote control vars: +parse_help; +parse_remote_help; +parse_query_help; + +# tweaks to duplicate help text: +tweak_remote_help lock deny +tweak_remote_help unlock deny + +tweak_both quiet q +tweak_help logfile o +tweak_both xwarppointer xwarp +tweak_both screen_blank sb + +set_template + +wm title . "tkx11vnc" +make_widgets; + +menu_bindings; +key_bindings; + +if {$x11vnc_connect} { + try_connect_and_query_all +} +set_widgets + +# main loop. diff --git a/x11vnc/tkx11vnc.h b/x11vnc/tkx11vnc.h new file mode 100644 index 0000000..83891fe --- /dev/null +++ b/x11vnc/tkx11vnc.h @@ -0,0 +1,2194 @@ +/* + * tkx11vnc.h: generated by 'tkx11vnc -spit' + * Abandon all hope, ye who enter here... + * ...edit tkx11vnc instead. + */ + char gui_code[] = +"#!/bin/sh\n" +"# the next line restarts using wish. \\\n" +"exec wish \"$0\" \"$@\"\n" +"catch {rename send {}}\n" +"#\n" +"# Copyright (c) 2004 Karl J. Runge <runge@karlrunge.com>\n" +"# All rights reserved.\n" +"#\n" +"# This is free software; you can redistribute it and/or modify\n" +"# it under the terms of the GNU General Public License as published by\n" +"# the Free Software Foundation; either version 2 of the License, or\n" +"# (at your option) any later version.\n" +"#\n" +"# This software is distributed in the hope that it will be useful,\n" +"# but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"# GNU General Public License for more details.\n" +"#\n" +"# You should have received a copy of the GNU General Public License\n" +"# along with this software; if not, write to the Free Software\n" +"# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,\n" +"# USA.\n" +"\n" +"#\n" +"# tkx11vnc v0.1\n" +"# This is a simple frontend to x11vnc. It uses the remote control\n" +"# and query features (-remote/-query aka -R/-Q) to interact with it. \n" +"# It is just a quick-n-dirty hack (it parses -help output, etc), but\n" +"# it could be of use playing with or learning about the (way too) many\n" +"# parameters x11vnc has.\n" +"# \n" +"# It can be used to interact with a running x11vnc (see the x11vnc\n" +"# -gui option), or to set the parameters and then start up x11vnc. \n" +"# \n" +"\n" +"#\n" +"# Below is a simple picture of how the gui should be laid out and how\n" +"# the menus should be organized. Most menu items correspond to remote\n" +"# control commands. A trailing \":\" after the item name means it is a string\n" +"# to be set rather than a boolean that can be toggled (e.g. the entry\n" +"# box must be used).\n" +"#\n" +"# Some tweak options may be set in the prefix \"=\" string.\n" +"# A means it is an \"Action\" (not a true variable)\n" +"# R means it is an action only valid in remote mode.\n" +"# S means it is an action only valid in startup mode.\n" +"# Q means it is an action worth querying after running.\n" +"# D means it is a good idea to delay a little before querying \n" +"# (i.e. perhaps it causes x11vnc to do a lot of work, new fb)\n" +"# P means the string can be +/- appended/deleted (string may not\n" +"# be the same after the remote command)\n" +"# G means gui internal item\n" +"# F means can be set via file browse\n" +"# -C:val1,... means it will be a checkbox (radio button)\n" +"# the \"-\" means no other options follow\n" +"# 0 means to skip the item.\n" +"# -- means add a separator\n" +"#\n" +"proc set_template {} {\n" +" global template\n" +" set template \"\n" +"Row: Actions Clients Permissions Keyboard Pointer Help\n" +"Row: Displays Screen Tuning Debugging Misc\n" +"\n" +"Actions\n" +" =SA start\n" +" =RA stop\n" +" =GA attach\n" +" =RA detach\n" +" --\n" +" =RA ping\n" +" =RA update-all\n" +" =GA clear-all\n" +" --\n" +" =GA Quit \n" +"\n" +"Help\n" +" =GA gui\n" +" =GA all\n" +"\n" +"Clients\n" +" =RQA current:\n" +" =F connect:\n" +" =RQA disconnect:\n" +" --\n" +" accept:\n" +" gone:\n" +" vncconnect\n" +" --\n" +" =F httpdir:\n" +" httpport:\n" +" enablehttpproxy\n" +"\n" +"Displays\n" +" display:\n" +" =F auth:\n" +" desktop:\n" +" rfbport:\n" +" =0 gui:\n" +"\n" +"Screen\n" +" =DRA refresh\n" +" =DRA reset\n" +" =DRA blacken\n" +" --\n" +" =D id:\n" +" =D sid:\n" +" =D scale:\n" +" --\n" +" =D overlay\n" +" overlay_nocursor\n" +" --\n" +" =D visual:\n" +" flashcmap\n" +" notruecolor\n" +" --\n" +" =DP blackout:\n" +" =D xinerama\n" +" --\n" +" = xrandr\n" +" =-C:resize,newfbsize,exit xrandr_mode:\n" +" padgeom:\n" +"\n" +"Keyboard\n" +" norepeat\n" +" add_keysyms\n" +" modtweak\n" +" xkb\n" +" skip_keycodes:\n" +" --\n" +" =FP remap:\n" +" --\n" +" clear_mods\n" +" clear_keys\n" +"\n" +"Pointer\n" +" =-C:none,arrow,X,some,most cursor:\n" +" noxfixes\n" +" --\n" +" cursorpos\n" +" nocursorshape\n" +" --\n" +" buttonmap:\n" +" --\n" +" xwarppointer\n" +"\n" +"Misc\n" +" =F rc:\n" +" norc\n" +" --\n" +" nofb\n" +" --\n" +" nobell\n" +" nosel\n" +" noprimary\n" +" --\n" +" bg\n" +" =-C:ignore,exit sigpipe:\n" +" =0 inetd\n" +" --\n" +" =RA remote-cmd:\n" +" =GA all-settings\n" +"\n" +"Debugging\n" +" debug_pointer\n" +" debug_keyboard\n" +" =F logfile:\n" +" quiet\n" +" --\n" +" =G debug_gui\n" +"\n" +"Permissions\n" +" =RQA lock\n" +" =RQA unlock\n" +" =SQA deny_all\n" +" --\n" +" =FP allow:\n" +" localhost\n" +" =RA allowonce:\n" +" --\n" +" viewonly\n" +" shared\n" +" forever\n" +" --\n" +" =RA noremote\n" +" --\n" +" alwaysshared\n" +" nevershared\n" +" dontdisconnect\n" +" --\n" +" viewpasswd:\n" +" =F passwdfile:\n" +" =0 storepasswd\n" +" =F rfbauth:\n" +" passwd:\n" +" --\n" +" safer\n" +" unsafe\n" +"\n" +"Tuning\n" +" =-C:1,2,3,4 pointer_mode:\n" +" input_skip:\n" +" nodragging\n" +" --\n" +" =D noshm\n" +" flipbyteorder\n" +" onetile\n" +" --\n" +" wait:\n" +" defer:\n" +" nap\n" +" screen_blank:\n" +" --\n" +" fs:\n" +" gaps:\n" +" grow:\n" +" fuzz:\n" +" --\n" +" threads\n" +" rfbwait:\n" +" --\n" +" progressive:\n" +"\"\n" +"}\n" +"\n" +"proc set_internal_help {} {\n" +" global helptext helpall\n" +"\n" +" # set some internal item help here:\n" +" set helptext(start) \"\n" +"Launch x11vnc with the settings you have prescribed in the gui.\n" +"The x11vnc process is started in an xterm window so you can see the\n" +"output, kill it, etc.\n" +"\"\n" +"\n" +" set helptext(debug_gui) \"\n" +"Set debug_gui to get more output printed in the text area.\n" +"\"\n" +"\n" +" set helptext(detach) \"\n" +"No longer be associated with the x11vnc server. Switch to non-connected\n" +"state.\n" +"\"\n" +"\n" +" set helptext(attach) \"\n" +"Attach to the x11vnc server, if possible. Switches to connected state\n" +"if successful. To change or set the X display use \\\"Displays -> display\\\"\n" +"\"\n" +"\n" +" set helptext(ping) \"\n" +"Check if x11vnc still responds to \\\"ping\\\" remote command.\n" +"\"\n" +"\n" +" set helptext(update-all) \"\n" +"Query the x11vnc server for the current values of all variables.\n" +"Populate the values into the gui's database.\n" +"\"\n" +"\n" +" set helptext(clear-all) \"\n" +"Forget any variable settings either entered in by you or retrieved\n" +"from a running x11vnc server. Basically sets everything to 0 or\n" +"the string (unset).\n" +"\"\n" +"\n" +" set helptext(all-settings) \"\n" +"Displays the gui's database of all of the x11vnc server's current\n" +"settings. Use \\\"Actions -> update-all\\\" or \\\"Control+R\\\" to\n" +"refresh this list if it ever gets out of sync.\n" +"\"\n" +"\n" +" set helptext(remote-cmd) \"\n" +"Run a remote command (-R) or query (-Q) directly. Only a few\n" +"remote commands are not on a menu, but for those few you can\n" +"run the command directly this way. Just enter the command into\n" +"the Entry box when prompted. Use the prefix \\\"Q:\\\" to indicate\n" +"a -Q query. Examples: \\\"zero:20,20,100,100\\\", \\\"Q:ext_xfixes\\\" \n" +"\"\n" +"\n" +" set helptext(Quit) \"\n" +"Terminate the tkx11vnc gui. Any x11vnc servers will be left running.\n" +"\"\n" +"\n" +" set helptext(current) \"\n" +"Shows a menu of currently connected VNC clients on the x11vnc server.\n" +"\n" +"Allows you to find more information about them or disconnect them.\n" +"You will be prompted to confirm any disconnections.\n" +"\"\n" +"\n" +" set helptext(xrandr_mode) \"\n" +"Set the -xrandr mode value.\n" +"\"\n" +"\n" +" set helptext(all) $helpall\n" +"\n" +" set helptext(gui) \"\n" +"tkx11vnc is a simple frontend to x11vnc. Nothing fancy, it merely\n" +"provides an interface to each of the many x11vnc command line options and\n" +"remote control commands. See \\\"Help -> all\\\" for much info about x11vnc.\n" +"\n" +"Most menu items have a (?) button one can click on to get more information\n" +"about the option or command. In most cases it will be text extracted\n" +"from that in \\\"Help -> all\\\".\n" +"\n" +"There are two states tkx11vnc can be in:\n" +"\n" +" 1) Available to control a running x11vnc process.\n" +" 2) Getting ready to start a x11vnc process.\n" +"\n" +"In state 1) the Menu items available in the menus are those that\n" +"correspond to the x11vnc \\\"remote control\\\" commands. See the -remote\n" +"entry under \\\"Help -> all\\\" for a complete list. Also available is\n" +"the \\\"Actions -> stop\\\" item to shut down the running x11vnc server,\n" +"thereby changing to state 2). One could also simply \\\"Actions -> detach\\\"\n" +"leaving the x11vnc server running. \\\"Actions -> attach\\\" would\n" +"reestablish the connection.\n" +"\n" +"In state 2) the Menu items available in the menus (Actions, Clients,\n" +"etc.) are those that correspond to command line options used in starting\n" +"an x11vnc process, and the \\\"Actions -> start\\\" item executes\n" +"x11vnc thereby changing to state 1). To see what x11vnc startup command\n" +"you have built so far, look at the (?) help for \\\"Actions -> start\\\"\n" +"and it will show you what the command looks like.\n" +"\n" +"There is much overlap between the menu items available in state 1)\n" +"and state 2), but it is worth keeping in mind it is not 100%.\n" +"For example, you cannot set passwords or password files in state 1).\n" +"\n" +"Also note that there may be *two* separate X displays involved, not just\n" +"one: 1) the X display x11vnc will be polling (and making available to\n" +"VNC viewers), and 2) the X display this GUI is intended to display on.\n" +"For example, one might use ssh to access the remote machine where the\n" +"GUI would display on :11 and x11vnc would poll display :0.\n" +"\n" +"\n" +"GUI components: \n" +"--- ----------\n" +"\n" +"At the top of the gui is a info text label where information will\n" +"be posted, e.g. when traversing menu items text indicating how to get\n" +"help on the item and its current value will be displayed.\n" +"\n" +"Below the info label is the area where the menu buttons, Actions,\n" +"Clients, etc., are presented. If a menu item has a checkbox,\n" +"it corresponds to a boolean on/off variable. Otherwise it is\n" +"either a string variable, or an action not associated with a\n" +"variable (for the most part).\n" +"\n" +"Below the menu button area is a text label indicating the current x11vnc\n" +"X display being polled and the corresponding VNC display name. Both\n" +"will be \\\"(*none*)\\\" when there is no connection established.\n" +"\n" +"Below the x11 and vnc displays text label is a text area there scrolling\n" +"information about actions being taken and commands being run is displayed.\n" +"To scroll use PageUp/PageDown or the arrow keys.\n" +"\n" +"At the bottom is an entry area. When one selects a menu item that\n" +"requires supplying a string value, the label will be set to the\n" +"parameter name and one types in the new value. Then one presses the\n" +"\\\"OK\\\" button or presses \\\"Enter\\\" to set the value. Or you can press\n" +"\\\"Skip\\\" or \\\"Escape\\\" to avoid changing the variable. Some variables\n" +"are boolean toggles (for example, \\\"Permissions -> viewonly\\\") or Radio\n" +"button selections. Selecting these menu items will not activate the\n" +"entry area but rather toggle the variable directly.\n" +"\n" +"Cascades: There is a bug not yet worked around for the cascade menus\n" +"where the (?) help button gets in the way. To get the mouse over to\n" +"the cascade menu click and release mouse to activate the cascade, then\n" +"you can click on its items. Dragging with a mouse button held down\n" +"will not work (sorry).\n" +"\n" +"Key Bindings:\n" +"\n" +" In the Text Area: Control-/ selects all of the text.\n" +" Anywhere: Control-d invokes \\\"Actions -> detach\\\"\n" +" Anywhere: Control-a invokes \\\"Actions -> attach\\\"\n" +" Anywhere: Control-p invokes \\\"Actions -> ping\\\"\n" +" Anywhere: Control-u and Control-r invoke \\\"Actions -> update-all\\\"\n" +"\n" +"\"\n" +"}\n" +"\n" +"proc center_win {w} {\n" +" wm withdraw $w\n" +" set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2];\n" +" set y [expr [winfo screenheight $w]/2 - [winfo reqheight $w]/2];\n" +" wm geom $w +$x+$y\n" +" wm deiconify $w\n" +" update\n" +"}\n" +"\n" +"proc textwidth {text} {\n" +" set min 0;\n" +" foreach line [split $text \"\\n\"] {\n" +" set n [string length $line]\n" +" if {$n > $min} {\n" +" set min $n\n" +" }\n" +" }\n" +" return $min\n" +"}\n" +"\n" +"proc textheight {text} {\n" +" set count 0;\n" +" foreach line [split $text \"\\n\"] {\n" +" incr count\n" +" }\n" +" return $count\n" +"}\n" +"\n" +"proc make_toplevel {w {title \"\"}} {\n" +" catch {destroy $w}\n" +" toplevel $w;\n" +" bind $w <Escape> \"destroy $w\"\n" +" if {$title != \"\"} {\n" +" wm title $w $title\n" +" }\n" +"}\n" +"\n" +"proc textwin {name title text} {\n" +" global max_text_height max_text_width\n" +" global bfont\n" +"\n" +" set width [textwidth $text]\n" +" incr width\n" +" if {$width > $max_text_width} {\n" +" set width $max_text_width\n" +" }\n" +" set height [textheight $text]\n" +" if {$height > $max_text_height} {\n" +" set height $max_text_height\n" +" }\n" +"\n" +" set w \".text_$name\"\n" +" make_toplevel $w $title\n" +"\n" +" frame $w.f -bd 0;\n" +" pack $w.f -fill both -expand 1\n" +" text $w.f.t -width $width -height $height -setgrid 1 -bd 2 \\\n" +" -yscrollcommand \"$w.f.y set\" -relief ridge -font fixed;\n" +" scrollbar $w.f.y -orient v -relief sunken -command \"$w.f.t yview\";\n" +" button $w.f.b -text \"Dismiss\" -command \"destroy $w\" -font $bfont\n" +"\n" +" $w.f.t insert 1.0 $text;\n" +"\n" +" bind $w <Enter> \"focus $w.f.t\"\n" +"\n" +" wm withdraw $w\n" +" pack $w.f.b -side bottom -fill x \n" +" pack $w.f.y -side right -fill y;\n" +" pack $w.f.t -side top -fill both -expand 1;\n" +" update\n" +"\n" +" center_win $w\n" +"}\n" +"\n" +"proc active_when_connected {item} {\n" +" global helpremote helptext\n" +"\n" +" if {[opt_match G $item]} {\n" +" return 1\n" +" } elseif {[is_action $item]} {\n" +" if {[opt_match R $item]} {\n" +" return 1\n" +" } else {\n" +" return 0\n" +" }\n" +" } elseif {[info exists helpremote($item)]} {\n" +" return 1\n" +" } else {\n" +" return 0\n" +" }\n" +"}\n" +"\n" +"proc active_when_starting {item} {\n" +" global helpremote helptext\n" +"\n" +" if {[opt_match G $item]} {\n" +" return 1\n" +" } elseif {[is_action $item]} {\n" +" if {[opt_match S $item]} {\n" +" return 1\n" +" } else {\n" +" return 0\n" +" }\n" +" } elseif {[info exists helptext($item)]} {\n" +" return 1\n" +" } else {\n" +" return 0\n" +" }\n" +"}\n" +"\n" +"proc help_win {item} {\n" +" global helptext helpremote\n" +" global query_ans query_aro;\n" +"\n" +" set ok 0\n" +" set text \"Help on $item:\\n\\n\"\n" +"\n" +" if {[is_gui_internal $item]} {\n" +" ;\n" +" } elseif {[is_action $item]} {\n" +" append text \" + Is a remote control Action (cannot be set).\\n\";\n" +" } elseif {[active_when_connected $item]} {\n" +" append text \" + Can be changed in a running x11vnc.\\n\";\n" +" } else {\n" +" append text \" - Cannot be changed in a running x11vnc.\\n\";\n" +" }\n" +" if {[is_gui_internal $item]} {\n" +" ;\n" +" } elseif {[active_when_starting $item]} {\n" +" append text \" + Can be set at x11vnc startup.\\n\";\n" +" } else {\n" +" append text \" - Cannot be set at x11vnc startup.\\n\";\n" +" }\n" +" append text \"\\n\"\n" +"\n" +" if {[info exists helptext($item)]} {\n" +" append text \"\\n\"\n" +" if {[is_gui_internal $item]} {\n" +" append text \"==== x11vnc help: ====\\n\";\n" +" } else {\n" +" append text \"==== x11vnc startup option help: ====\\n\";\n" +" }\n" +" append text \"\\n\"\n" +" append text $helptext($item)\n" +" append text \"\\n\"\n" +" set ok 1\n" +" }\n" +"\n" +" if {[info exists helpremote($item)]} {\n" +" append text \"\\n\"\n" +" append text \"==== x11vnc remote control help: ====\\n\";\n" +" append text \"\\n\"\n" +" append text $helpremote($item)\n" +" set ok 1\n" +" }\n" +"\n" +" if {$item == \"start\"} {\n" +" set str [get_start_x11vnc_txt]\n" +" append text $str\n" +" append_text \"$str\\n\"\n" +" }\n" +"\n" +" regsub -all { } $item \" \" name\n" +"\n" +" if {$ok} {\n" +" textwin $name \"x11vnc help: $item\" \"$text\";\n" +" }\n" +" return $ok\n" +"}\n" +"\n" +"proc parse_help {} {\n" +" global env x11vnc_prog;\n" +" global helpall helptext;\n" +"\n" +" set helppipe [open \"| $x11vnc_prog -help\" \"r\"];\n" +" if {$helppipe == \"\"} {\n" +" puts stderr \"failed to run $x11vnc_prog -help\";\n" +" exit 1;\n" +" }\n" +"\n" +" set sawopts 0;\n" +" set curropt \"\";\n" +" while {[gets $helppipe line] > -1} {\n" +" append helpall \"$line\\n\" \n" +"\n" +" # XXX\n" +" if {[regexp {^Options:} $line]} {\n" +" set sawopts 1;\n" +" continue;\n" +" }\n" +" # XXX\n" +" if {[regexp {^These options} $line]} {\n" +" continue;\n" +" }\n" +"\n" +" if {! $sawopts} {\n" +" continue;\n" +" }\n" +" if {[regexp {^-([A-z_][A-z_]*)} $line match name]} {\n" +" set allnames($name) 1;\n" +" if {\"$curropt\" != \"no$name\" && \"no$curropt\" != \"$name\"} {\n" +" set curropt $name;\n" +" set helptext($curropt) \"$line\\n\";\n" +" } else {\n" +" append helptext($curropt) \"$line\\n\";\n" +" }\n" +" } elseif {$curropt != \"\"} {\n" +" append helptext($curropt) \"$line\\n\";\n" +" }\n" +" }\n" +" foreach name [array names allnames] {\n" +" if {[regexp {^no} $name]} {\n" +" regsub {^no} $name \"\" pair\n" +" } else {\n" +" set pair \"no$name\"\n" +" }\n" +" if {[info exists helptext($name)]} {\n" +" if ![info exists helptext($pair)] {\n" +" set helptext($pair) $helptext($name);\n" +" }\n" +" } elseif {[info exists helptext($pair)]} {\n" +" if ![info exists helptext($name)] {\n" +" set helptext($name) $helptext($pair);\n" +" }\n" +" }\n" +" }\n" +"\n" +" set_internal_help\n" +"}\n" +"\n" +"proc tweak_both {new old} {\n" +" tweak_help $new $old\n" +" tweak_remote_help $new $old\n" +"}\n" +"\n" +"proc tweak_remote_help {new old} {\n" +" global helpremote\n" +" if ![info exists helpremote($new)] {\n" +" if {[info exists helpremote($old)]} {\n" +" set helpremote($new) $helpremote($old)\n" +" }\n" +" }\n" +"}\n" +"\n" +"proc tweak_help {new old} {\n" +" global helptext\n" +" if ![info exists helptext($new)] {\n" +" if {[info exists helptext($old)]} {\n" +" set helptext($new) $helptext($old)\n" +" }\n" +" }\n" +"}\n" +"\n" +"proc parse_remote_help {} {\n" +" global helpremote helptext help_indent remote_name;\n" +"\n" +" set sawopts 0;\n" +" set curropt \"\";\n" +" set possopts \"\";\n" +" set offset [expr $help_indent - 1];\n" +" foreach line [split $helptext(remote) \"\\n\"] {\n" +" \n" +" set line [string range $line $offset end];\n" +"\n" +" # XXX\n" +" if {[regexp {^The following -remote/-R commands} $line]} {\n" +" set sawopts 1;\n" +" continue;\n" +" }\n" +" # XXX\n" +" if {[regexp {^The vncconnect.*command} $line]} {\n" +" set sawopts 0;\n" +" }\n" +"\n" +" if {! $sawopts} {\n" +" continue;\n" +" }\n" +" if {[regexp {^([A-z_][A-z_:]*)} $line match name]} {\n" +" regsub {:.*$} $name \"\" popt\n" +" lappend possopts $popt\n" +" if {\"$curropt\" != \"no$name\" && \"no$curropt\" != \"$name\"} {\n" +" set curropt $name;\n" +" regsub {:.*$} $curropt \"\" curropt\n" +" set remote_name($curropt) $name\n" +" set helpremote($curropt) \"$line\\n\";\n" +" } else {\n" +" append helpremote($curropt) \"$line\\n\";\n" +" }\n" +" } elseif {$curropt != \"\"} {\n" +" append helpremote($curropt) \"$line\\n\";\n" +" }\n" +" }\n" +"\n" +" foreach popt $possopts {\n" +" if {[info exists helpremote($popt)]} {\n" +" continue\n" +" }\n" +" if {[regexp {^no} $popt]} {\n" +" regsub {^no} $popt \"\" try\n" +" } else {\n" +" set try \"no$popt\"\n" +" }\n" +" if {[info exists helpremote($try)]} {\n" +" set helpremote($popt) $helpremote($try)\n" +" }\n" +" }\n" +"}\n" +"\n" +"proc parse_query_help {} {\n" +" global query_ans query_aro query_ans_list query_aro_list helptext;\n" +"\n" +" set sawans 0;\n" +" set sawaro 0;\n" +" set ans_str \"\"\n" +" set aro_str \"\"\n" +"\n" +" foreach line [split $helptext(query) \"\\n\"] {\n" +"\n" +" if {! $sawans && [regexp {^ *ans=} $line]} {\n" +" set sawans 1\n" +" }\n" +" if {! $sawans} {\n" +" continue\n" +" }\n" +"\n" +" if {[regexp {^ *aro=} $line]} {\n" +" set sawaro 1\n" +" }\n" +" if {$sawaro && [regexp {^[ ]*$} $line]} {\n" +" set sawans 0\n" +" break\n" +" }\n" +"\n" +" regsub {ans=} $line \"\" line\n" +" regsub {aro=} $line \"\" line\n" +" set line [string trim $line]\n" +"\n" +" if {$sawaro} {\n" +" set aro_str \"$aro_str $line\"\n" +" } else {\n" +" set ans_str \"$ans_str $line\"\n" +" }\n" +" }\n" +"\n" +" regsub -all { *} $ans_str \" \" ans_str\n" +" regsub -all { *} $aro_str \" \" aro_str\n" +"\n" +" set ans_str [string trim $ans_str]\n" +" set aro_str [string trim $aro_str]\n" +" set query_ans_list [split $ans_str]\n" +" set query_aro_list [split $aro_str]\n" +"\n" +" foreach item $query_ans_list {\n" +" if {[regexp {^[ ]*$} $item]} {\n" +" continue\n" +" }\n" +" set query_ans($item) 1\n" +" }\n" +" foreach item $query_aro_list {\n" +" if {[regexp {^[ ]*$} $item]} {\n" +" continue\n" +" }\n" +" set query_aro($item) 1\n" +" }\n" +"}\n" +"\n" +"proc in_debug_mode {} {\n" +" global menu_var\n" +" if {![info exists menu_var(debug_gui)]} {\n" +" return 0\n" +" }\n" +" return $menu_var(debug_gui)\n" +"}\n" +"\n" +"# Menubar utilities:\n" +"proc menus_state {state} {\n" +" global menu_b\n" +"\n" +" foreach case [array names menu_b] {\n" +" set menu_button $menu_b($case)\n" +" $menu_button configure -state $state\n" +" }\n" +"}\n" +"\n" +"proc menus_enable {} {\n" +" menus_state \"normal\"\n" +"}\n" +"\n" +"proc menus_disable {} {\n" +" menus_state \"disabled\"\n" +"}\n" +"\n" +"# Entry box utilities:\n" +"proc entry_state {x state} {\n" +" global entry_box entry_label entry_ok entry_help entry_skip entry_browse\n" +" if {$x == \"all\"} {\n" +" $entry_label configure -state $state\n" +" $entry_box configure -state $state\n" +" $entry_ok configure -state $state\n" +" $entry_skip configure -state $state\n" +" $entry_help configure -state $state\n" +" $entry_browse configure -state $state\n" +" } elseif {$x == \"label\"} {\n" +" $entry_label configure -state $state\n" +" } elseif {$x == \"box\"} {\n" +" $entry_box configure -state $state\n" +" } elseif {$x == \"ok\"} {\n" +" $entry_ok configure -state $state\n" +" } elseif {$x == \"skip\"} {\n" +" $entry_skip configure -state $state\n" +" } elseif {$x == \"help\"} {\n" +" $entry_help configure -state $state\n" +" } elseif {$x == \"browse\"} {\n" +" $entry_browse configure -state $state\n" +" }\n" +"}\n" +"\n" +"proc entry_enable {{x \"all\"}} {\n" +" entry_state $x normal\n" +"}\n" +"\n" +"proc entry_disable {{x \"all\"}} {\n" +" entry_state $x disabled\n" +"}\n" +"\n" +"proc entry_browse_button {{show 1}} {\n" +" global entry_browse\n" +" if {$show} {\n" +" pack $entry_browse -side left\n" +" } else {\n" +" pack forget $entry_browse\n" +" }\n" +"}\n" +"proc entry_focus {} {\n" +" global entry_box\n" +" focus $entry_box\n" +"}\n" +"proc entry_select {} {\n" +" global entry_box\n" +" $entry_box selection range 0 end\n" +"}\n" +"proc entry_get {} {\n" +" global entry_box\n" +" return [$entry_box get]\n" +"}\n" +"proc entry_insert {str} {\n" +" global entry_box\n" +" entry_delete\n" +" $entry_box insert end $str\n" +" $entry_box icursor end\n" +"}\n" +"proc entry_delete {} {\n" +" global entry_box\n" +" $entry_box delete 0 end\n" +"}\n" +"\n" +"\n" +"# Utilities for remote control and updating vars.\n" +"\n" +"proc push_new_value {item name new {query 1}} {\n" +" global menu_var always_update remote_output query_output\n" +" global delay_sleep extra_sleep extra_sleep_split\n" +"\n" +" set debug [in_debug_mode]\n" +" set do_query_all 0\n" +" set getout 0\n" +"\n" +" if {$item == \"remote-cmd\"} {\n" +" # kludge for arbitrary remote command:\n" +" if {[regexp {^Q:} $new]} {\n" +" # extra kludge for Q:var to mean -Q var\n" +" regsub {^Q:} $new \"\" new\n" +" set qonly 1\n" +" } else {\n" +" set qonly 0\n" +" }\n" +" # need to extract item from new:\n" +" set qtmp $new\n" +" regsub {:.*$} $qtmp \"\" qtmp\n" +" if {! $qonly} {\n" +" set rargs [list \"-R\" \"$new\"]\n" +" set qargs [list \"-Q\" \"$qtmp\"]\n" +" set getout 1\n" +" } else {\n" +" set rargs [list \"-Q\" \"$qtmp\"]\n" +" set qargs [list \"-Q\" \"$qtmp\"]\n" +" }\n" +"\n" +" } elseif {[value_is_string $item]} {\n" +" set rargs [list \"-R\" \"$name:$new\"]\n" +" set qargs [list \"-Q\" \"$name\"]\n" +" } else {\n" +" set rargs [list \"-R\" \"$name\"]\n" +" set qargs [list \"-Q\" \"$name\"]\n" +" }\n" +"\n" +" if {!$debug} {\n" +" append_text \"x11vnc $rargs ...\"\n" +" }\n" +" set remote_output [run_remote_cmd $rargs]\n" +"\n" +" if {[lindex $rargs 0] == \"-Q\"} {\n" +" append_text \"\\t$remote_output\"\n" +" set getout 1\n" +" } elseif {! $query && ! $always_update} {\n" +" set getout 1\n" +" } elseif {$item == \"noremote\"} {\n" +" set getout 1\n" +" } elseif {[is_action $item] && ![opt_match Q $item] && $rargs != \"\"} {\n" +" set getout 1\n" +" } elseif {[regexp {^(sid|id)$} $item] && ![regexp {^0x} $new]} {\n" +" set getout 1\n" +" }\n" +"\n" +" if {$getout} {\n" +" append_text \"\\n\"\n" +" return\n" +" }\n" +"\n" +" stop_watch on\n" +" after $delay_sleep\n" +" if {[opt_match D $item]} {\n" +" set s [expr $extra_sleep/$extra_sleep_split] \n" +" append_text \" \"\n" +" for {set i 0} {$i<$extra_sleep_split} {incr i} {\n" +" after $s\n" +" append_text \".\"\n" +" update\n" +" }\n" +" }\n" +" stop_watch off\n" +"\n" +" if {!$debug} {\n" +" append_text \", -Q ...\"\n" +" }\n" +"\n" +" if {$item == \"disconnect\"} {\n" +" set new \"N/A\"\n" +" set do_query_all 1\n" +" }\n" +"\n" +" if {$always_update || $do_query_all} {\n" +" set query [query_all 1]\n" +" } else {\n" +" set query [run_remote_cmd $qargs]\n" +" }\n" +" set query_output $query\n" +"\n" +" if {![see_if_ok $query $item \"$name:$new\"]} {\n" +" # failed\n" +" if {[regexp {^a..=} $query]} {\n" +" # but some result came back\n" +" if {! $always_update} {\n" +" # synchronize everything\n" +" set query_output [query_all 1]\n" +" }\n" +" } else {\n" +" # server may be dead\n" +" if {$item != \"ping\" && $item != \"attach\"} {\n" +" try_connect\n" +" }\n" +" }\n" +" } else {\n" +" # succeeded\n" +" if {! $always_update} {\n" +" # synchronize this variable\n" +" update_menu_vars $query\n" +" } else {\n" +" # already done in query_all\n" +" }\n" +" }\n" +"}\n" +"\n" +"# For updating a string variable. Also used for simple OK/Skip dialogs\n" +"# with entry = 0.\n" +"proc entry_dialog {item {entry 1}} {\n" +" global menu_var entry_str entry_set entry_dialog_item\n" +" global unset_str connected_to_x11vnc\n" +"\n" +" set entry_str \"Set $item\"\n" +" set entry_set 0\n" +" set entry_dialog_item $item\n" +"\n" +" entry_enable\n" +" menus_disable\n" +"\n" +" if {$entry} {\n" +" entry_insert \"\"\n" +" if {[info exists menu_var($item)] &&\n" +" $menu_var($item) != $unset_str} {\n" +" entry_insert $menu_var($item)\n" +" entry_select\n" +" }\n" +"\n" +" if {[is_browse $item]} {\n" +" entry_browse_button\n" +" }\n" +" set_info \"Set parameter in entry box, \"\n" +" entry_focus\n" +" } else {\n" +" entry_disable box\n" +" }\n" +"\n" +" update\n" +"\n" +" # wait for user reply:\n" +" vwait entry_set\n" +"\n" +" set rc $entry_set\n" +" set entry_set 0\n" +"\n" +" set value [entry_get]\n" +" update\n" +"\n" +" entry_browse_button 0\n" +" set entry_str \"Set... :\"\n" +"\n" +" entry_delete\n" +" entry_disable\n" +" menus_enable\n" +" update\n" +"\n" +" if {! $entry} {\n" +" ;\n" +" } elseif {$rc} {\n" +" set menu_var($item) $value\n" +" } else {\n" +" if {[in_debug_mode]} {\n" +" append_text \"skipped setting $item\\n\"\n" +" }\n" +" }\n" +" return $rc\n" +"}\n" +"\n" +"proc warning_dialog {msg {item \"gui\"} } {\n" +" append_text $msg\n" +" # just reuse the entry widgets for a yes/no dialog\n" +" return [entry_dialog $item 0]\n" +"}\n" +"\n" +"# For updating a boolean toggle:\n" +"proc check_var {item} {\n" +" global menu_var\n" +"\n" +" set inval $menu_var($item);\n" +"\n" +" if {$item == \"debug_gui\"} {\n" +" return \"\";\n" +" }\n" +"\n" +" set rname $item\n" +" if {! $inval} {\n" +" if {[regexp {^no} $item]} {\n" +" regsub {^no} $rname \"\" rname\n" +" } else {\n" +" set rname \"no$rname\"\n" +" }\n" +" }\n" +" return $rname\n" +"}\n" +"\n" +"proc see_if_ok {query item expected} {\n" +" set ok 0\n" +" set found \"\"\n" +" foreach q [split_query $query] {\n" +" if {[regexp \"^$item:\" $q]} {\n" +" set found $q\n" +" }\n" +" if {$q == $expected} {\n" +" set ok 1\n" +" }\n" +" }\n" +" if {$found == \"\"} {\n" +" set msg $query\n" +" regsub {^a..=} $msg {} msg\n" +" if {[string length $msg] > 60} {\n" +" set msg [string range $msg 0 60]\n" +" }\n" +" } else {\n" +" set msg $found\n" +" }\n" +" if {$ok} {\n" +" append_text \"\\tSet OK ($msg)\\n\"\n" +" return 1\n" +"\n" +" } elseif {[opt_match P $item] && [regexp {:(-|\\+)} $expected]} {\n" +" # e.g. blackout:+30x30+20+20\n" +" append_text \"\\t($msg)\\n\"\n" +" return 1\n" +" } else {\n" +" append_text \"\\t*FAILED* $msg\\n\"\n" +" return 0\n" +" }\n" +"}\n" +"\n" +"proc update_menu_vars {{query \"\"}} {\n" +" global all_settings menu_var\n" +"\n" +" set debug [in_debug_mode]\n" +"\n" +" if {$query == \"\"} {\n" +" set qstr $all_settings\n" +" } else {\n" +" set qstr $query\n" +" }\n" +" foreach piece [split_query $qstr] {\n" +" if {[regexp {^([^:][^:]*):(.*)$} $piece m0 item val]} {\n" +" if {[info exists menu_var($item)]} {\n" +" set old $menu_var($item)\n" +" if {$val == \"N/A\"} {\n" +" continue\n" +" }\n" +" if {$debug} {\n" +" puts \"setting menuvar: $item: $old -> $val\"\n" +" }\n" +" set menu_var($item) $val\n" +" }\n" +" if {$item == \"clients\"} {\n" +" update_clients_menu $val\n" +" }\n" +" }\n" +" }\n" +"}\n" +"\n" +"proc clear_all {} {\n" +" global menu_var unset_str\n" +"\n" +" set debug [in_debug_mode]\n" +" \n" +" foreach item [array names menu_var] {\n" +" if {$item == \"debug_gui\"} {\n" +" continue\n" +" }\n" +" if {[info exists menu_var($item)]} {\n" +" if [is_action $item] {\n" +" set menu_var($item) \"\"\n" +" } elseif {[value_is_bool $item]} {\n" +" set menu_var($item) 0\n" +" } elseif {[value_is_string $item]} {\n" +" set menu_var($item) $unset_str\n" +" }\n" +" }\n" +" }\n" +"}\n" +"\n" +"proc all_query_vars {} {\n" +" global query_ans_list query_aro_list all_settings\n" +" \n" +" set qry \"\"\n" +" foreach item $query_ans_list {\n" +" if {$qry == \"\"} {\n" +" set qry $item\n" +" } else {\n" +" append qry \",$item\"\n" +" }\n" +" }\n" +" foreach item $query_aro_list {\n" +" if {$qry == \"\"} {\n" +" set qry $item\n" +" } else {\n" +" append qry \",$item\"\n" +" }\n" +" }\n" +" return $qry\n" +"}\n" +"\n" +"proc query_all {{quiet 0}} {\n" +" global query_ans_list query_aro_list all_settings\n" +"\n" +" set qry [all_query_vars]\n" +"\n" +" #puts \"into query_all $quiet\"\n" +"\n" +" set qargs [list \"-Q\" $qry]\n" +" set all [run_remote_cmd $qargs]\n" +"\n" +" if {[regexp {ans=} $all]} {\n" +" if {! $quiet} {\n" +" append_text \"Retrieved all settings.\\n\"\n" +" }\n" +" set all_settings $all\n" +" update_menu_vars $all\n" +" } else {\n" +" if {! $quiet} {\n" +" append_text \"Failed to retrieve settings.\\n\"\n" +" }\n" +" }\n" +" return $all\n" +"}\n" +"\n" +"proc set_info {str} {\n" +" global info_str\n" +" set info_str \"$str\"\n" +" update\n" +"}\n" +"\n" +"proc append_text {str} {\n" +" global text_area\n" +" $text_area insert end $str\n" +" $text_area see end\n" +"}\n" +"\n" +"proc show_all_settings {} {\n" +" global all_settings\n" +" set txt \"\\nRead-Write setting:\\n\\n\"\n" +" foreach item [split_query $all_settings] {\n" +" regsub {:} $item {: } item\n" +" append txt \" $item\\n\"\n" +" if {[regexp {noremote} $item]} {\n" +" append txt \"\\nRead-Only setting:\\n\\n\"\n" +" }\n" +" }\n" +" textwin \"Settings\" \"All Current Settings\" $txt\n" +"}\n" +"\n" +"proc set_connected {yesno} {\n" +" global connected_to_x11vnc\n" +" set orig $connected_to_x11vnc\n" +" \n" +" if {$yesno == \"yes\"} {\n" +" set connected_to_x11vnc 1\n" +" } else {\n" +" set connected_to_x11vnc 0\n" +" no_x11_display\n" +" no_vnc_display\n" +" }\n" +" if {$orig != $connected_to_x11vnc} {\n" +" set_widgets\n" +" }\n" +"}\n" +"\n" +"proc detach_from_display {} {\n" +" global connected_to_x11vnc reply_xdisplay x11vnc_xdisplay\n" +" set str \"Detaching from X display.\"\n" +" if {$reply_xdisplay != \"\"} {\n" +" set str \"Detaching from $reply_xdisplay.\"\n" +" } elseif {$x11vnc_xdisplay != \"\"} {\n" +" set str \"Detaching from $x11vnc_xdisplay.\"\n" +" }\n" +" if {$connected_to_x11vnc} {\n" +" append_text \"$str\\n\"\n" +" }\n" +" set_connected no\n" +"}\n" +"\n" +"# Menu item is an action:\n" +"proc do_action {item} {\n" +" global menu_var connected_to_x11vnc\n" +"\n" +" if {[in_debug_mode]} {\n" +" append_text \"action: \\\"$item\\\"\\n\"\n" +" }\n" +"\n" +" if {$item == \"ping\"} {\n" +" try_connect\n" +" return\n" +" } elseif {$item == \"start\"} {\n" +" start_x11vnc\n" +" return\n" +" } elseif {$item == \"detach\"} {\n" +" detach_from_display\n" +" return\n" +" } elseif {$item == \"attach\"} {\n" +" try_connect_and_query_all\n" +" return\n" +" } elseif {$item == \"update-all\"} {\n" +" query_all\n" +" return\n" +" } elseif {$item == \"clear-all\"} {\n" +" clear_all\n" +" return\n" +" } elseif {$item == \"all-settings\"} {\n" +" show_all_settings\n" +" return\n" +" }\n" +"\n" +" if {[value_is_string $item]} {\n" +" if {! [entry_dialog $item]} {\n" +" return\n" +" }\n" +" set new $menu_var($item)\n" +" set name $item\n" +" } else {\n" +" set new 1\n" +" set name $item\n" +" }\n" +"\n" +" if {! $connected_to_x11vnc} {\n" +" ;\n" +" } elseif {[regexp {^(stop|quit|exit|shutdown)$} $item]} {\n" +" # just do -R\n" +" append_text \"stopping remote x11vnc server...\\n\"\n" +" push_new_value $item $name $new 0\n" +" set_connected no\n" +" \n" +" } elseif [opt_match Q $item] {\n" +" push_new_value $item $name $new 1\n" +" } else {\n" +" push_new_value $item $name $new 0\n" +" }\n" +"}\n" +"\n" +"proc do_var {item} {\n" +" global connected_to_x11vnc item_cascade menu_var\n" +"\n" +" set string 0\n" +" if {[is_action $item]} {\n" +" # Menu item is action:\n" +" do_action $item\n" +" return\n" +" }\n" +"\n" +" if {[value_is_string $item]} {\n" +" # Menu item is a string:\n" +" if {$item_cascade($item) != \"\"} {\n" +" # Cascade sets variable automatically\n" +" } else {\n" +" # Otherwise Entry box\n" +" if {![entry_dialog $item]} {\n" +" return\n" +" }\n" +" }\n" +" set new $menu_var($item)\n" +" set name $item\n" +" } else {\n" +" # Menu item is a boolean:\n" +" set name [check_var $item]\n" +" if {$name == \"\"} {\n" +" return\n" +" }\n" +" set new 1\n" +" }\n" +" if {$connected_to_x11vnc} {\n" +" push_new_value $item $name $new 1\n" +" }\n" +"}\n" +"\n" +"proc menu_help {item} {\n" +" if ![help_win $item] {\n" +" textwin \"nohelp\" \"No help available\" \\\n" +" \"Sorry, no help avaiable for \\\"$item\\\"\"\n" +" }\n" +"}\n" +"\n" +"proc opt_match {c item} {\n" +" global item_opts\n" +" if {[info exists item_opts($item)]} {\n" +" if {[regexp \"^\\[A-z\\]*$c\" $item_opts($item)]} {\n" +" return 1\n" +" }\n" +" }\n" +" return 0\n" +"}\n" +"\n" +"proc is_action {item} {\n" +" return [opt_match A $item]\n" +"}\n" +"\n" +"proc is_gui_internal {item} {\n" +" return [opt_match G $item]\n" +"}\n" +"\n" +"proc is_browse {item} {\n" +" return [opt_match F $item]\n" +"}\n" +"\n" +"proc value_is_string {item} {\n" +" global item_bool\n" +" if {! $item_bool($item)} {\n" +" return 1\n" +" } else {\n" +" return 0\n" +" }\n" +"}\n" +"\n" +"proc value_is_bool {item} {\n" +" global item_bool\n" +" if {$item_bool($item)} {\n" +" return 1\n" +" } else {\n" +" return 0\n" +" }\n" +"}\n" +"\n" +"proc split_query {query} {\n" +" regsub -all {aro=} $query {ans=} query\n" +" set items {}\n" +" while {1} {\n" +" if {! [regexp {^ans=(.*)$} $query m0 m1]} {\n" +" break\n" +" }\n" +" set item $m1\n" +" set m2 \"\"\n" +" regexp {,ans=.*$} $item m2\n" +" regsub {,ans=.*$} $item \"\" item\n" +" if {$item != \"\"} {\n" +" lappend items $item\n" +" }\n" +" set query $m2\n" +" regsub {^,} $query \"\" query\n" +" }\n" +" return $items\n" +"}\n" +"\n" +"proc set_x11_display {name} {\n" +" global x11_display\n" +" set x11_display \"x11vnc X display: $name\"\n" +"}\n" +"proc set_vnc_display {name} {\n" +" global vnc_display\n" +" set vnc_display \"VNC display: $name\"\n" +"}\n" +"proc no_x11_display {} {\n" +" set_x11_display \"(*none*)\"\n" +"}\n" +"proc no_vnc_display {} {\n" +" set_vnc_display \"(*none*)\"\n" +"}\n" +"proc fetch_displays {} {\n" +"\n" +" set qargs [list \"-Q\" \"display,vncdisplay\"]\n" +" set result [run_remote_cmd $qargs]\n" +"\n" +" set got_x11 0\n" +" set got_vnc 0\n" +"\n" +" foreach item [split_query $result] {\n" +" if {[regexp {^display:(.*)$} $item m0 m1]} {\n" +" set_x11_display $m1\n" +" set got_x11 1\n" +" } elseif {[regexp {^vncdisplay:(.*)$} $item m0 m1]} {\n" +" set_vnc_display $m1\n" +" set got_vnc 1\n" +" }\n" +" }\n" +" if {! $got_x11} {\n" +" no_x11_display\n" +" }\n" +" if {! $got_vnc} {\n" +" no_vnc_display\n" +" }\n" +"}\n" +"\n" +"proc disconnect_dialog {client} {\n" +" set cid \"\"\n" +" set host \"\"\n" +" set msg \"\\n\"\n" +" append msg \"*** Client info string: $client\\n\"\n" +" if {[regexp {^(.*):(.*)/(.*)-(.*)$} $client m0 m1 m2 m3 m4]} {\n" +" if {$m4 == \"ro\"} {\n" +" set view \"(viewonly)\"\n" +" } else {\n" +" set view \"(interactive)\"\n" +" }\n" +" set host $m1\n" +" set cid $m3\n" +" append msg \"*** Host: $m1, Port: $m2 Id: $m3 $view\\n\"\n" +" }\n" +" if {$cid == \"\"} {\n" +" append_text \"Invalid client info string: $client\\n\"\n" +" return\n" +" }\n" +" append msg \"*** To disconnect this client press \\\"OK\\\", otherwise press \\\"Skip\\\"\\n\"\n" +" bell\n" +" if [warning_dialog $msg \"current\"] {\n" +" push_new_value \"disconnect\" \"disconnect\" $cid 1\n" +" } else {\n" +" append_text \"disconnect cancelled.\\n\"\n" +" }\n" +"}\n" +"\n" +"proc update_clients_menu {list} {\n" +" global item_cascade\n" +" set subm $item_cascade(current);\n" +" catch {destroy $subm}\n" +" menu $subm -tearoff 0\n" +" $subm add command\n" +" $subm add separator\n" +" set count 0\n" +" foreach client [split $list \",\"] {\n" +" regsub {:[0-9][0-9]*/} $client {/} lab\n" +" $subm add command -label \"$client\" \\\n" +" -command \"disconnect_dialog $client\"\n" +" incr count\n" +" }\n" +" $subm entryconfigure 0 -label \"#clients: $count\"\n" +"}\n" +"\n" +"proc set_widgets {} {\n" +" global connected_to_x11vnc item_case item_entry menu_m\n" +"\n" +" foreach item [array names item_case] {\n" +" set case $item_case($item)\n" +" set menu $menu_m($case)\n" +" set entry $item_entry($item)\n" +" set type [$menu type $entry]\n" +" if {$type == \"separator\" || $type == \"tearoff\"} {\n" +" continue\n" +" }\n" +" if {$connected_to_x11vnc} {\n" +" if {[active_when_connected $item]} {\n" +" $menu entryconfigure $entry -state normal\n" +" } else {\n" +" $menu entryconfigure $entry -state disabled\n" +" }\n" +" } else {\n" +" if {[active_when_starting $item]} {\n" +" $menu entryconfigure $entry -state normal\n" +" } else {\n" +" $menu entryconfigure $entry -state disabled\n" +" }\n" +" }\n" +" }\n" +"}\n" +"\n" +"proc make_widgets {} {\n" +" global template \n" +" global menu_b menu_m\n" +" global item_opts item_bool item_case item_entry menu_var unset_str\n" +" global item_cascade\n" +" global info_str x11_display vnc_display\n" +" global text_area\n" +" global entry_box entry_str entry_set entry_label entry_ok entry_browse\n" +" global entry_help entry_skip\n" +" global bfont\n" +" global helptext helpremote helplabel\n" +"\n" +"set v 0\n" +" \n" +" label .info -textvariable info_str -bd 2 -relief groove -anchor w \n" +" pack .info -side top -fill x \n" +"\n" +" # Extract the Rows:\n" +" set row 0;\n" +" set colmax 0;\n" +" foreach line [split $template \"\\n\"] {\n" +" if {[regexp {^Row: (.*)} $line rest]} {\n" +" set col 0\n" +" foreach case [split $rest] {\n" +" if {$case == \"\" || $case == \"Row:\"} {\n" +" continue\n" +" }\n" +" set menu_row($case) $row\n" +" set menu_col($case) $col\n" +" set menu_count($case) 0\n" +"\n" +" lappend cases($col) $case;\n" +" set len [string length $case]\n" +" if {[info exists max_len($col)]} {\n" +" if {$len > $max_len($col)} {\n" +" set max_len($col) $len\n" +" }\n" +" } else {\n" +" set max_len($col) $len\n" +" }\n" +" incr col\n" +" if {$col > $colmax} {\n" +" set colmax $col\n" +" }\n" +" }\n" +" incr row;\n" +" }\n" +" }\n" +"\n" +" # Make frames for the rows and make the menu buttons.\n" +" set f \".menuframe\"\n" +" frame $f\n" +" for {set c 0} {$c < $colmax} {incr c} {\n" +" set colf \"$f.menuframe$c\"\n" +" frame $colf\n" +" pack $colf -side left -fill y\n" +" set fbg [$colf cget -background]\n" +" foreach case $cases($c) {\n" +" set menub \"$colf.menu$case\";\n" +" set menu \"$colf.menu$case.menu\";\n" +" set menu_b($case) $menub\n" +" set menu_m($case) $menu\n" +" menubutton $menub -text \"$case\" -underline 0 \\\n" +" -anchor w -menu $menu -background $fbg \\\n" +" -font $bfont\n" +" pack $menub -side top -fill x\n" +" menu $menu -tearoff 0\n" +" }\n" +" }\n" +" pack $f -side top -fill x\n" +"\n" +" # Now extract the menu items:\n" +" set case \"\";\n" +" foreach line [split $template \"\\n\"] {\n" +" if {[regexp {^Row:} $line]} {\n" +" continue\n" +" }\n" +" if {[regexp {^[A-z]} $line]} {\n" +" set case [string trim $line]\n" +" continue;\n" +" }\n" +" set item [string trim $line]\n" +" regsub -all { *} $item \" \" item\n" +" if {$item == \"\"} {\n" +" continue;\n" +" }\n" +" set opts \"\"\n" +" if {[regexp {^=} $item]} {\n" +" set opts [lindex [split $item] 0]\n" +" regsub {^=} $opts \"\" opts\n" +" set item [lindex [split $item] 1]\n" +" }\n" +" if {[regexp {^0} $opts]} {\n" +" continue;\n" +" }\n" +" if {[regexp {:$} $item]} {\n" +" set bool 0\n" +" } else {\n" +" set bool 1\n" +" }\n" +" regsub {:$} $item {} item\n" +"\n" +" set item_opts($item) $opts\n" +" set item_case($item) $case\n" +" set item_bool($item) $bool\n" +" set item_cascade($item) \"\"\n" +" set item_entry($item) $menu_count($case)\n" +"\n" +"if {$v} { puts \"ITEM: $item - $opts - $case - $bool - $menu_count($case)\" }\n" +"\n" +" set mvar 0 \n" +" set m $menu_m($case)\n" +"\n" +" # Create the menu items, its variables, etc., etc.\n" +"\n" +" if {$item == \"--\"} {\n" +" $m add separator\n" +"\n" +" } elseif {$item == \"Quit\"} {\n" +" # Quit item must shut us down:\n" +" $m add command -label \"$item\" -underline 0 \\\n" +" -command {destroy .; exit 0}\n" +"\n" +" } elseif {$case == \"Help\"} {\n" +" # Help is simple help:\n" +" $m add command -label \"$item\" \\\n" +" -command \"menu_help $item\"\n" +"\n" +" } elseif {$item == \"current\"} {\n" +" # Current clients cascade\n" +" set subm $m.cascade$menu_count($case)\n" +" set item_cascade($item) $subm\n" +" update_clients_menu \"\"\n" +" $m add cascade -label \"$item\" \\\n" +" -menu $subm\n" +"\n" +" } elseif {[is_action $item]} {\n" +" # Action\n" +" $m add command -label \"$item\" \\\n" +" -command \"do_var $item\"\n" +" set menu_var($item) \"\"; # for convenience\n" +"\n" +" } elseif {! $item_bool($item)} {\n" +" # String\n" +" if {[regexp -- {-C:(.*)} $item_opts($item) m0 m1]} {\n" +" # Radiobutton select\n" +" set subm $m.cascade$menu_count($case)\n" +" menu $subm -tearoff 0\n" +" foreach val [split $m1 \",\"] {\n" +" $subm add radiobutton -label \"$val\" \\\n" +" -command \"do_var $item\" \\\n" +" -value \"$val\" \\\n" +" -variable menu_var($item)\n" +" }\n" +" $m add cascade -label \"$item\" \\\n" +" -menu $subm\n" +" set item_cascade($item) $subm\n" +" } else {\n" +" # Arbitrary_string\n" +" $m add command -label \"$item\" \\\n" +" -command \"do_var $item\"\n" +" }\n" +" set mvar 1\n" +"\n" +" } else {\n" +" # Boolean\n" +" $m add checkbutton -label \"$item\" \\\n" +" -command \"do_var $item\" \\\n" +" -variable menu_var($item)\n" +" set menu_var($item) 0\n" +" }\n" +"\n" +" incr menu_count($case)\n" +" if {$mvar} {\n" +" set menu_var($item) $unset_str\n" +" }\n" +" }\n" +"\n" +" # Now make the litte \"(?)\" help buttons\n" +" foreach case [array names menu_m] {\n" +" if {$case == \"Help\"} {\n" +" continue;\n" +" }\n" +" set m $menu_m($case);\n" +" set n [$m index end]\n" +"\n" +"if {$v} { puts \"$case end: $n\" }\n" +"\n" +" for {set i 0} {$i <= $n} {incr i} {\n" +" set type [$m type $i]\n" +" if {$type == \"separator\"} {\n" +" $m add separator\n" +" } elseif {$type == \"tearoff\"} {\n" +" continue;\n" +" } else {\n" +" set label [$m entrycget $i -label]\n" +" set str \"\"\n" +" if {[info exists helpremote($label)]} {\n" +" set str \"(?)\"\n" +" } elseif {[info exists helptext($label)]} {\n" +" set str \"(?)\"\n" +" }\n" +" $m add command -label $str \\\n" +" -command \"menu_help $label\";\n" +"\n" +"if {$v} {\n" +" set ht \"\"; set hr \"\"\n" +" if {[info exists helptext($label)]} { set ht \"YES\" }\n" +" if {[info exists helpremote($label)]} { set hr \"YES\" }\n" +" puts \"'$label'\\tht='$ht' hr='$hr'\"\n" +"}\n" +"\n" +" if {$str == \"\"} {\n" +" $m entryconfigure end -state disabled\n" +" }\n" +" set arg \"$m,$i\"\n" +" set helplabel($arg) $label\n" +" set j [$m index end]\n" +" set arg \"$m,$j\"\n" +" set helplabel($arg) $label\n" +" }\n" +" if {$i == 0} {\n" +" $m entryconfigure end -columnbreak 1\n" +" }\n" +" }\n" +" }\n" +"\n" +" # Make the x11 and vnc display label bar:\n" +" set df .displayframe\n" +" frame $df -bd 1 -relief groove\n" +"\n" +" set df_x11 \"$df.xdisplay\"\n" +" no_x11_display\n" +" label $df_x11 -textvariable x11_display -width 35 -anchor w\n" +"\n" +" set df_vnc \"$df.vdisplay\"\n" +" no_vnc_display\n" +" label $df_vnc -textvariable vnc_display -width 35 -anchor w\n" +"\n" +" pack $df_x11 $df_vnc -side left \n" +" pack $df -side top -fill x\n" +"\n" +" # text area\n" +" text .text -height 11 -relief ridge\n" +" set text_area .text\n" +" pack .text -side top -fill both -expand 1\n" +"\n" +"\n" +" set str \"Click Help -> gui for overview.\"\n" +" append_text \"\\n$str\\n\\n\"\n" +"\n" +" # Make entry box stuff\n" +" set ef .entryframe\n" +" frame $ef -bd 1 -relief groove\n" +"\n" +" # Label\n" +" set ef_label \"$ef.label\"\n" +" label $ef_label -textvariable entry_str -anchor w -font $bfont\n" +"\n" +" set entry_str \"Set... : \"\n" +" set ef_entry \"$ef.entry\"\n" +" entry $ef_entry -relief sunken\n" +" bind $ef_entry <KeyPress-Return> {set entry_set 1}\n" +" bind $ef_entry <KeyPress-Escape> {set entry_set 0}\n" +"\n" +" # OK button\n" +" set ef_ok \"$ef.ok\"\n" +" button $ef_ok -text OK -pady 1 -command {set entry_set 1} \\\n" +" -font $bfont\n" +"\n" +" # Skip button\n" +" set ef_skip \"$ef.skip\"\n" +" button $ef_skip -text Skip -pady 0 -command {set entry_set 0} \\\n" +" -font $bfont\n" +"\n" +" # Help button\n" +" set ef_help \"$ef.help\"\n" +" button $ef_help -text Help -pady 0 -command \\\n" +" {menu_help $entry_dialog_item} -font $bfont\n" +"\n" +" # Browse button\n" +" set ef_browse \"$ef.browse\"\n" +" button $ef_browse -text \"Browse...\" -pady 0 -font $bfont \\\n" +" -command {entry_insert [tk_getOpenFile]} \n" +"\n" +" pack $ef_label -side left\n" +" pack $ef_entry -side left -fill x -expand 1\n" +" pack $ef_ok -side right\n" +" pack $ef_skip -side right\n" +" pack $ef_help -side right\n" +" pack $ef -side bottom -fill x\n" +"\n" +" set entry_ok $ef_ok\n" +" set entry_skip $ef_skip\n" +" set entry_help $ef_help\n" +" set entry_box $ef_entry\n" +" set entry_browse $ef_browse\n" +" set entry_label $ef_label\n" +" entry_disable\n" +"\n" +" update\n" +" wm minsize . [winfo width .] [winfo height .]\n" +"}\n" +"\n" +"proc menu_bindings {} {\n" +" bind Menu <<MenuSelect>> {\n" +"#syntax hilite bug \\\n" +"MenuSelect>>\n" +" set n [%W index active]\n" +" set label \" \"\n" +" if {$n != \"none\"} {\n" +" set str %W,$n\n" +" set which \"\"\n" +" if {[info exists helplabel($str)]} {\n" +" set vname [format %%-16s $helplabel($str)]\n" +" set label \"Click (?) for help on: $vname\"\n" +" set which $helplabel($str)\n" +" }\n" +" if {$which == \"\"} {\n" +" ;\n" +" } elseif {[is_action $which]} {\n" +" if {[info exists menu_var($which)] \n" +" && $menu_var($which) != \"\"} {\n" +" set label \"$label value: $menu_var($which)\"\n" +" } else {\n" +" set label \"$label (is action)\"\n" +" }\n" +" } elseif {[info exists menu_var($which)]} {\n" +" set label \"$label value: $menu_var($which)\"\n" +" }\n" +" }\n" +" set_info $label\n" +" }\n" +"}\n" +"\n" +"proc key_bindings {} {\n" +" global env\n" +" if {[info exists env(USER)] && $env(USER) == \"runge\"} {\n" +" # quick restart\n" +" bind . <Control-KeyPress-c> {exec $argv0 $argv &; destroy .}\n" +" }\n" +" bind . <Control-KeyPress-p> {try_connect_and_query_all}\n" +" bind . <Control-KeyPress-u> {query_all 0}\n" +" bind . <Control-KeyPress-r> {query_all 0}\n" +" bind . <Control-KeyPress-d> {detach_from_display}\n" +" bind . <Control-KeyPress-a> {try_connect_and_query_all}\n" +"}\n" +"\n" +"proc stop_watch {onoff} {\n" +" global orig_cursor text_area entry_box\n" +"\n" +" set widgets [list . $text_area $entry_box] \n" +"\n" +" if {$onoff == \"on\"} {\n" +" foreach item $widgets {\n" +" $item config -cursor {watch}\n" +" }\n" +" } else {\n" +" foreach item $widgets {\n" +" $item config -cursor {}\n" +" }\n" +" }\n" +" update\n" +"}\n" +"\n" +"proc double_check_noremote {} {\n" +" set msg \"\\n\\n\"\n" +" append msg \"WARNING: setting \\\"noremote\\\" will disable ALL remote control commands\\n\"\n" +" append msg \"WARNING: (i.e. this gui will be locked out) Do you really want to do this?\\n\"\n" +" append msg \"WARNING: If so, press \\\"OK\\\", otherwise press \\\"Skip\\\"\\n\"\n" +" append msg \"\\n\"\n" +" bell\n" +" return [warning_dialog $msg \"noremote\"]\n" +"}\n" +"\n" +"proc double_check_start_x11vnc {} {\n" +" global hostname\n" +" set msg [get_start_x11vnc_txt]\n" +" append msg \"\\n\"\n" +" append msg \"*** To run the above command on machine \\\"$hostname\\\" to\\n\"\n" +" append msg \"*** start x11vnc press \\\"OK\\\" otherwise press \\\"Skip\\\".\\n\"\n" +" return [warning_dialog $msg \"start\"]\n" +"}\n" +"\n" +"proc get_start_x11vnc_txt {} {\n" +" set cmd [get_start_x11vnc_cmd]\n" +" set str [join $cmd]\n" +" set msg \"\"\n" +" append msg \"\\n\"\n" +" append msg \"==== The command built so far is: ====\\n\";\n" +" append msg \"\\n\"\n" +" append msg \"$str\\n\"\n" +" return $msg\n" +"}\n" +"\n" +"proc get_start_x11vnc_cmd {} {\n" +" global menu_var unset_str x11vnc_prog\n" +"\n" +" set xterm_cmd \"xterm -iconic -geometry 80x35 -title x11vnc-console -e\"\n" +"\n" +" set cmd [split $xterm_cmd]\n" +"\n" +" lappend cmd $x11vnc_prog\n" +"\n" +" foreach item [lsort [array names menu_var]] {\n" +" if {![active_when_starting $item]} {\n" +" continue\n" +" }\n" +" if {[is_action $item]} {\n" +" continue\n" +" }\n" +"\n" +" if {[value_is_bool $item]} {\n" +" if {[info exists menu_var($item)]} {\n" +" if {$menu_var($item)} {\n" +" lappend cmd \"-$item\"\n" +" }\n" +" }\n" +" } elseif {[value_is_string $item]} {\n" +" if {[info exists menu_var($item)]} {\n" +" if {$menu_var($item) != \"\"\n" +" && $menu_var($item) != $unset_str} {\n" +" lappend cmd \"-$item\"\n" +" lappend cmd $menu_var($item)\n" +" }\n" +" }\n" +" }\n" +" }\n" +" lappend cmd \"2>\"\n" +" lappend cmd \"/dev/null\"\n" +" lappend cmd \"&\"\n" +" \n" +" return $cmd\n" +"}\n" +"\n" +"proc start_x11vnc {} {\n" +" global menu_var unset_str\n" +" global x11vnc_prog x11vnc_xdisplay\n" +" global connected_to_x11vnc\n" +"\n" +" if {$connected_to_x11vnc} {\n" +" append_text \"\\n\"\n" +" append_text \"WARNING: Still connected to an x11vnc server.\\n\"\n" +" append_text \"WARNING: Use \\\"stop\\\" or \\\"detach\\\" first.\\n\"\n" +" return 0\n" +" }\n" +"\n" +" if {![double_check_start_x11vnc]} {\n" +" return\n" +" }\n" +"\n" +" set x11vnc_xdisplay \"\"\n" +" if {[info exists menu_var(display)]} {\n" +" if {$menu_var(display) != \"\" && $menu_var(display) != $unset_str} {\n" +" set x11vnc_xdisplay $menu_var(display)\n" +" }\n" +" }\n" +"\n" +" set cmd [get_start_x11vnc_cmd]\n" +"\n" +" set str [join $cmd]\n" +" regsub { -e} $str \" -e \\\\\\n \" str\n" +"\n" +" if {0} {\n" +" puts \"running: $str\"\n" +" foreach word $cmd {\n" +" puts \" word: $word\"\n" +" }\n" +" }\n" +"\n" +" append_text \"Starting x11vnc in an iconified xterm with command:\\n\"\n" +" append_text \" $str\\n\\n\"\n" +" catch {[eval exec $cmd]}\n" +" after 500\n" +" try_connect_and_query_all 3\n" +"}\n" +"\n" +"proc run_remote_cmd {opts} {\n" +" global menu_var x11vnc_prog x11vnc_cmdline x11vnc_xdisplay\n" +"\n" +" set debug [in_debug_mode]\n" +"\n" +" if {[lindex $opts 0] == \"-R\" && [lindex $opts 1] == \"noremote\"} {\n" +" set str [join $opts]\n" +" if ![double_check_noremote] {\n" +" append_text \"skipping: x11vnc $str\"\n" +" return \"\"\n" +" } else {\n" +" append_text \"running: x11vnc $str (please do \\\"Actions -> detach\\\" to clean things up)\\n\"\n" +" append_text \"subsequent -R/-Q commands should fail...\"\n" +" }\n" +" }\n" +"\n" +" set cmd \"\"\n" +"\n" +" lappend cmd $x11vnc_prog;\n" +"\n" +" if {$x11vnc_xdisplay != \"\"} {\n" +" lappend cmd \"-display\"\n" +" lappend cmd $x11vnc_xdisplay\n" +" }\n" +" foreach word $opts {\n" +" lappend cmd $word\n" +" }\n" +" lappend cmd \"2>\"\n" +" lappend cmd \"/dev/null\"\n" +"\n" +"if {0} {\n" +" set str [join $cmd]\n" +" puts \"running: $str\"\n" +" foreach word $cmd {\n" +" puts \" word: $word\"\n" +" }\n" +"}\n" +"\n" +" set output \"\"\n" +" stop_watch on\n" +" catch {set output [eval exec $cmd]}\n" +" stop_watch off\n" +" if {$debug} {\n" +" append_text \"output: $output\\n\"\n" +" }\n" +" return $output\n" +"}\n" +"\n" +"proc try_connect_and_query_all {{n 2}} {\n" +" for {set i 0} {$i < $n} {incr i} {\n" +" if {$i > 0} {\n" +" after 500\n" +" append_text \"trying again ...\\n\"\n" +" }\n" +" if {[try_connect]} {\n" +" query_all\n" +" break\n" +" }\n" +" }\n" +"}\n" +"\n" +"proc try_connect {} {\n" +" global x11vnc_xdisplay connected_to_x11vnc reply_xdisplay\n" +" global menu_var unset_str\n" +"\n" +" if {! $connected_to_x11vnc} {\n" +" if {[info exists menu_var(display)]} {\n" +" set d $menu_var(display)\n" +" if {$d != \"\" && $d != $unset_str && $d != $x11vnc_xdisplay} {\n" +" set x11vnc_xdisplay $menu_var(display)\n" +" append_text \"Setting X display to: $x11vnc_xdisplay\\n\"\n" +" }\n" +" }\n" +" }\n" +"\n" +" set_info \"Pinging $x11vnc_xdisplay ...\"\n" +" set rargs [list \"-Q\" \"ping\"]\n" +" set result [run_remote_cmd $rargs]\n" +"\n" +" if {[regexp {^ans=ping:} $result]} {\n" +" regsub {^ans=ping:} $result {} reply_xdisplay\n" +" set msg \"Connected to $reply_xdisplay\"\n" +" set_info $msg\n" +" append_text \"$msg\\n\"\n" +" set_connected yes\n" +" fetch_displays\n" +" return 1\n" +" } else {\n" +" set str \"x11vnc server.\"\n" +" if {$x11vnc_xdisplay != \"\"} {\n" +" set str $x11vnc_xdisplay\n" +" }\n" +" set msg \"No reply from $str\"\n" +" set_info $msg\n" +" append_text \"$msg\\n\"\n" +" set_connected no\n" +" return 0\n" +" }\n" +"}\n" +"\n" +"############################################################################\n" +"# main:\n" +"\n" +"global env x11vnc_prog x11vnc_cmdline x11vnc_xdisplay x11vnc_connect;\n" +"global helpall helptext helpremote helplabel hostname;\n" +"global all_settings reply_xdisplay always_update\n" +"global max_text_height max_text_width\n" +"global menu_var unset_str\n" +"global bfont\n" +"global connected_to_x11vnc\n" +"global delay_sleep extra_sleep extra_sleep_split\n" +"\n" +"set unset_str \"(unset)\"\n" +"set connected_to_x11vnc 0\n" +"set max_text_height 40\n" +"set max_text_width 90\n" +"set bfont -adobe-helvetica-bold-r-*-*-*-120-*-*-*-*-*-*;\n" +"set help_indent 24;\n" +"set reply_xdisplay \"\"\n" +"set all_settings \"None so far.\"\n" +"set always_update 1\n" +"\n" +"#set delay_sleep 500\n" +"#set extra_sleep 1500\n" +"set delay_sleep 350\n" +"set extra_sleep 1000\n" +"set extra_sleep_split 4\n" +"\n" +"if {\"$argv\" == \"-spit\"} {\n" +" set fh [open $argv0 r]\n" +" puts \"/*\"\n" +" puts \" * tkx11vnc.h: generated by 'tkx11vnc -spit'\"\n" +" puts \" * Abandon all hope, ye who enter here...\"\n" +" puts \" * ...edit tkx11vnc instead.\"\n" +" puts \" */\"\n" +" puts \" char gui_code\\[\\] =\"\n" +" while {[gets $fh line] > -1} {\n" +" regsub -all {\\\\} $line {\\\\\\\\} line\n" +" regsub -all {\"} $line {\\\\\"} line\n" +" puts \"\\\"$line\\\\n\\\"\"\n" +" }\n" +" close $fh\n" +" puts \";\"\n" +" exit 0\n" +"}\n" +"\n" +"# Read environment for clues:\n" +"if {[info exists env(X11VNC_PROG)]} {\n" +" set x11vnc_prog $env(X11VNC_PROG);\n" +"} else {\n" +" set x11vnc_prog \"x11vnc\";\n" +"}\n" +"\n" +"if {[info exists env(X11VNC_CMDLINE)]} {\n" +" set x11vnc_cmdline $env(X11VNC_CMDLINE);\n" +"} else {\n" +" set x11vnc_cmdline \"\";\n" +"}\n" +"\n" +"if {[info exists env(X11VNC_CONNECT)]} {\n" +" set x11vnc_connect 1\n" +"} else {\n" +" set x11vnc_connect 0;\n" +"}\n" +"\n" +"if {[info exists env(X11VNC_XDISPLAY)]} {\n" +" set x11vnc_xdisplay $env(X11VNC_XDISPLAY);\n" +" set x11vnc_connect 1\n" +"\n" +"} elseif {$argv != \"\" && [regexp {:[0-9]} $argv]} {\n" +" set x11vnc_xdisplay \"$argv\"\n" +" set x11vnc_connect 1\n" +"\n" +"} elseif {[info exists env(DISPLAY)]} {\n" +" set x11vnc_xdisplay $env(DISPLAY);\n" +"} else {\n" +" set x11vnc_xdisplay \":0\";\n" +"}\n" +"\n" +"set hostname [exec uname -n]\n" +"#puts [exec env]\n" +"#puts \"x11vnc_xdisplay: $x11vnc_xdisplay\"\n" +"\n" +"set env(X11VNC_STD_HELP) 1\n" +"\n" +"# scrape the help output for the text and remote control vars:\n" +"parse_help;\n" +"parse_remote_help;\n" +"parse_query_help;\n" +"\n" +"# tweaks to duplicate help text:\n" +"tweak_remote_help lock deny\n" +"tweak_remote_help unlock deny\n" +"\n" +"tweak_both quiet q\n" +"tweak_help logfile o\n" +"tweak_both xwarppointer xwarp\n" +"tweak_both screen_blank sb\n" +"\n" +"set_template\n" +"\n" +"wm title . \"tkx11vnc\"\n" +"make_widgets;\n" +"\n" +"menu_bindings;\n" +"key_bindings;\n" +"\n" +"if {$x11vnc_connect} {\n" +" try_connect_and_query_all\n" +"}\n" +"set_widgets\n" +"\n" +"# main loop.\n" +; diff --git a/x11vnc/x11vnc.1 b/x11vnc/x11vnc.1 index d711f71..396316c 100644 --- a/x11vnc/x11vnc.1 +++ b/x11vnc/x11vnc.1 @@ -1,8 +1,8 @@ .\" This file was automatically generated from x11vnc -help output. -.TH X11VNC "1" "August 2004" "x11vnc " "User Commands" +.TH X11VNC "1" "December 2004" "x11vnc " "User Commands" .SH NAME x11vnc - allow VNC connections to real X11 displays - version: 0.6.3pre, lastmod: 2004-08-31 + version: 0.6.3pre, lastmod: 2004-12-17 .SH SYNOPSIS .B x11vnc [OPTION]... @@ -19,15 +19,18 @@ Then run this in another window on the machine you are sitting at: .IP vncviewer far-host:0 .PP -Once x11vnc establishes connections with the X11 server and starts -listening as a VNC server it will print out a string: PORT=XXXX where -XXXX is typically 5900 (the default VNC port). One would next run something -like this on the local machine: "vncviewer host:N" where N is XXXX - 5900, -i.e. usually "vncviewer host:0" +Once x11vnc establishes connections with the X11 server and starts listening +as a VNC server it will print out a string: PORT=XXXX where XXXX is typically +5900 (the default VNC server port). One would next run something like +this on the local machine: "vncviewer hostname:N" where "hostname" is +the name of the machine running x11vnc and N is XXXX - 5900, i.e. usually +"vncviewer hostname:0". .PP -By default x11vnc will not allow the screen to be shared and it will -exit as soon as a client disconnects. See \fB-shared\fR and \fB-forever\fR below -to override these protections. +By default x11vnc will not allow the screen to be shared and it will exit +as soon as a client disconnects. See \fB-shared\fR and \fB-forever\fR below to override +these protections. See the FAQ on how to tunnel the VNC connection through +an encrypted channel such as +.IR ssh (1). .PP For additional info see: http://www.karlrunge.com/x11vnc/ and http://www.karlrunge.com/x11vnc/#faq @@ -35,8 +38,8 @@ and http://www.karlrunge.com/x11vnc/#faq Rudimentary config file support: if the file $HOME/.x11vncrc exists then each line in it is treated as a single command line option. Disable with \fB-norc.\fR For each option name, the leading character "-" is not required. E.g. a -line that is either "nap" or "-nap" may be used and are equivalent. -Likewise "wait 100" or "-wait 100" are acceptable and equivalent lines. +line that is either "nap" or "\fB-nap\fR" may be used and are equivalent. +Likewise "wait 100" or "\fB-wait\fR \fI100\fR" are acceptable and equivalent lines. The "#" character comments out to the end of the line in the usual way. Leading and trailing whitespace is trimmed off. Lines may be continued with a "\\" as the last character of a line (it becomes a space character). @@ -65,17 +68,24 @@ man pages. .IP Show the window corresponding to \fIwindowid\fR not the entire display. New windows like popup menus, -etc may not be seen, or will be clipped. x11vnc may -crash if the window changes size, is iconified, etc. -Use +transient toplevels, etc, may not be seen or may be +clipped. Disabling SaveUnders or BackingStore in the +X server may help show them. x11vnc may crash if the +window is initially partially obscured, changes size, +is iconified, etc. Some steps are taken to avoid this +and the \fB-xrandr\fR mechanism is used to track resizes. Use .IR xwininfo (1) -to get the window id. Primarily useful -for exporting very simple applications. +to get the window id, or use "\fB-id\fR \fIpick\fR" +to have x11vnc run +.IR xwininfo (1) +for you and extract +the id. The \fB-id\fR option is useful for exporting very +simple applications (e.g. the current view on a webcam). .PP \fB-sid\fR \fIwindowid\fR .IP As \fB-id,\fR but instead of using the window directly it -shifts a root view to it: this shows saveUnders menus, +shifts a root view to it: this shows SaveUnders menus, etc, although they will be clipped if they extend beyond the window. .PP @@ -89,26 +99,45 @@ as the pointer moves from window to window (slow). For 8bpp displays, force indexed color (i.e. a colormap) even if it looks like 8bpp TrueColor. (rare problem) .PP +\fB-visual\fR \fIn\fR +.IP +Experimental option: probably does not do what you +think. It simply *forces* the visual used for the +framebuffer; this may be a bad thing... (e.g. messes +up colors or cause a crash). It is useful for testing +and for some workarounds. n may be a decimal number, +or 0x hex. Run +.IR xdpyinfo (1) +for the values. One may +also use "TrueColor", etc. see <X11/X.h> for a list. +If the string ends in ":m" for better or for worse +the visual depth is forced to be m. +.PP \fB-overlay\fR .IP Handle multiple depth visuals on one screen, e.g. 8+24 and 24+8 overlay visuals (the 32 bits per pixel are packed with 8 for PseudoColor and 24 for TrueColor). .IP -Currently \fB-overlay\fR only works on Solaris (it uses -XReadScreen(3X11)). There is a problem with image -"bleeding" around transient popup menus (but not -for the menu itself): a workaround is to disable -SaveUnders by passing the "-su" argument to Xsun -(in /etc/dt/config/Xservers, say). Also note that, -the mouse cursor shape is exactly correct in this mode. +Currently \fB-overlay\fR only works on Solaris via +.IR XReadScreen (3X11) +and IRIX using +.IR XReadDisplay (3). +On Solaris there is a problem with image "bleeding" +around transient popup menus (but not for the menu +itself): a workaround is to disable SaveUnders +by passing the "\fB-su\fR" argument to Xsun (in +/etc/dt/config/Xservers). Also note that the mouse +cursor shape is exactly correct in this mode. .IP Use \fB-overlay\fR as a workaround for situations like these: -Some legacy applications require the default visual +Some legacy applications require the default visual to be 8bpp (8+24), or they will use 8bpp PseudoColor even when the default visual is depth 24 TrueColor (24+8). In these cases colors in some windows will be messed -up in x11vnc unless \fB-overlay\fR is used. +up in x11vnc unless \fB-overlay\fR is used. Another use of +\fB-overlay\fR is to enable showing the exact mouse cursor +shape (details below). .IP Under \fB-overlay,\fR performance will be somewhat degraded due to the extra image transformations required. @@ -122,28 +151,16 @@ visual (some apps have \fB-use24\fR or \fB-visual\fR options). Sets \fB-overlay,\fR but does not try to draw the exact mouse cursor shape using the overlay mechanism. .PP -\fB-visual\fR \fIn\fR -.IP -Experimental option: probably does not do what you -think. It simply *forces* the visual used for the -framebuffer; this may be a bad thing... It is useful for -testing and for some workarounds. n may be a decimal -number, or 0x hex. Run -.IR xdpyinfo (1) -for the values. -One may also use "TrueColor", etc. see <X11/X.h> -for a list. If the string ends in ":m" for better -or for worse the visual depth is forced to be m. -.PP \fB-scale\fR \fIfraction\fR .IP -Scale the framebuffer by factor \fIfraction\fR. -Values less than 1 shrink the fb. Note: image may not -be sharp and response may be slower. Currently the -cursor shape is not scaled. If \fIfraction\fR contains -a decimal point "." it is taken as a floating point -number, alternatively the notation "m/n" may be used -to denote fractions exactly, e.g. \fB-scale\fR 2/3. +Scale the framebuffer by factor \fIfraction\fR. Values +less than 1 shrink the fb, larger ones expand it. +Note: image may not be sharp and response may be +slower. Currently the cursor shape is not scaled. +If \fIfraction\fR contains a decimal point "." it +is taken as a floating point number, alternatively +the notation "m/n" may be used to denote fractions +exactly, e.g. \fB-scale\fR 2/3. .IP Scaling Options: can be added after \fIfraction\fR via ":", to supply multiple ":" options use commas. @@ -174,6 +191,15 @@ disconnects, opposite of \fB-forever.\fR This is the Default. Keep listening for more connections rather than exiting as soon as the first client(s) disconnect. Same as \fB-many\fR .PP +\fB-inetd\fR +.IP +Launched by +.IR inetd (1): +stdio instead of listening socket. +Note: if you are not redirecting stderr to a log file +(via shell 2> or \fB-o\fR option) you must also specify the +\fB-q\fR option, otherwise the stderr goes to the viewer. +.PP \fB-connect\fR \fIstring\fR .IP For use with "vncviewer -listen" reverse connections. @@ -184,41 +210,29 @@ contains "/" it is instead interpreted as a file to periodically check for new hosts. The first line is read and then the file is truncated. .PP -\fB-vncconnect\fR +\fB-vncconnect,\fR \fB-novncconnect\fR .IP Monitor the VNC_CONNECT X property set by the standard -.PP -\fB-novncconnect\fR -.IP VNC program -.IR vncconnect (1) -. When the property is +.IR vncconnect (1). +When the property is set to "host" or "host:port" establish a reverse connection. Using .IR xprop (1) instead of vncconnect may -work, see the FAQ. Default: \fB-vncconnect\fR -.PP -\fB-inetd\fR -.IP -Launched by -.IR inetd (1) -: stdio instead of listening socket. -Note: if you are not redirecting stderr to a log file -(via shell 2> or \fB-o\fR option) you must also specify the -\fB-q\fR option. +work (see the FAQ). Default: \fB-vncconnect\fR .PP -\fB-allow\fR \fIaddr1[,addr2..]\fR +\fB-allow\fR \fIhost1[,host2..]\fR .IP -Only allow client connections from IP addresses matching -the comma separated list of numerical addresses. -Can be a prefix, e.g. "192.168.100." to match a -simple subnet, for more control build libvncserver -with libwrap support. If the list contains a "/" -it instead is a interpreted as a file containing -addresses or prefixes that is re-read each time a new -client connects. Lines can be commented out with the -"#" character in the usual way. +Only allow client connections from hosts matching +the comma separated list of hostnames or IP addresses. +Can also be a numerical IP prefix, e.g. "192.168.100." +to match a simple subnet, for more control build +libvncserver with libwrap support (See the FAQ). If the +list contains a "/" it instead is a interpreted as a +file containing addresses or prefixes that is re-read +each time a new client connects. Lines can be commented +out with the "#" character in the usual way. .PP \fB-localhost\fR .IP @@ -236,13 +250,13 @@ the file \fIfilename\fR instead of via command line. If a second non blank line exists in the file it is taken as a view-only password (i.e. \fB-viewpasswd)\fR Note: this is a simple plaintext passwd, see also \fB-rfbauth\fR -and \fB-storepasswd\fR below. +and \fB-storepasswd\fR below for obfuscated passwords. .PP \fB-storepasswd\fR \fIpass\fR \fIfile\fR .IP Store password \fIpass\fR as the VNC password in the file \fIfile\fR. Once the password is stored the -program exits. Use the password via "-rfbauth file" +program exits. Use the password via "\fB-rfbauth\fR \fIfile\fR" .PP \fB-accept\fR \fIstring\fR .IP @@ -253,10 +267,10 @@ an external command run via .IR system (3) or some special cases described below. Be sure to quote \fIstring\fR -if it contains spaces, etc. If the external command -returns 0 the client is accepted, otherwise the client -is rejected. See below for an extension to accept a -client view-only. +if it contains spaces, shell characters, etc. If the +external command returns 0 the client is accepted, +otherwise the client is rejected. See below for an +extension to accept a client view-only. .IP Environment: The RFB_CLIENT_IP environment variable will be set to the incoming client IP number and the port @@ -266,7 +280,7 @@ of the connection), are set to allow identification of the tcp virtual circuit. The x11vnc process id will be in RFB_X11VNC_PID, a client id number in RFB_CLIENT_ID, and the number of other connected clients -in RFB_CLIENT_COUNT. +in RFB_CLIENT_COUNT. RFB_MODE will be "accept" .IP If \fIstring\fR is "popup" then a builtin popup window is used. The popup will time out after 120 seconds, @@ -275,7 +289,8 @@ use "popup:N" to modify the timeout to N seconds .IP If \fIstring\fR is "xmessage" then an .IR xmessage (1) -invocation is used for the command. +invocation is used for the command. xmessage must be +installed on the machine for this to work. .IP Both "popup" and "xmessage" will present an option for accepting the client "View-Only" (the client @@ -291,22 +306,26 @@ respectively. Use "*" instead of a number to indicate the default action (in case the command returns an unexpected value). E.g. "no:*" is a good choice. .IP -Note that x11vnc blocks while the external command or +Note that x11vnc blocks while the external command or popup is running (other clients may see no updates during this period). .IP More \fB-accept\fR tricks: use "popupmouse" to only allow mouse clicks in the builtin popup to be recognized. -Similarly use "popupkey" to only recognize keystroke -responses. All 3 of the popup keywords can be followed +Similarly use "popupkey" to only recognize +keystroke responses. These are to help avoid the +user accidentally accepting a client by typing or +clicking. All 3 of the popup keywords can be followed by +N+M to supply a position for the popup window. The default is to center the popup window. .PP \fB-gone\fR \fIstring\fR .IP As \fB-accept,\fR except to run a user supplied command when -a client goes away (disconnects). Unlike \fB-accept,\fR -the command return code is not interpreted by x11vnc. +a client goes away (disconnects). RFB_MODE will be +set to "gone" and the other RFB_* variables are as +in \fB-accept.\fR Unlike \fB-accept,\fR the command return code +is not interpreted by x11vnc. Example: \fB-gone\fR 'xlock &' .PP \fB-noshm\fR .IP @@ -342,10 +361,53 @@ areas to black out (if your system has libXinerama). In general on XINERAMA displays you may need to use the \fB-xwarppointer\fR option if the mouse pointer misbehaves. .PP +\fB-xrandr\fR \fI[mode]\fR +.IP +If the display supports the XRANDR (X Resize, Rotate +and Reflection) extension, and you expect XRANDR events +to occur to the display while x11vnc is running, this +options indicates x11vnc should try to respond to +them (as opposed to simply crashing by assuming the +old screen size). See the +.IR xrandr (1) +manpage and run +\'xrandr \fB-q'\fR for more info. [mode] is optional and +described below. +.IP +Since watching for XRANDR events and errors increases +polling overhead, only use this option if XRANDR changes +are expected. For example on a rotatable screen PDA or +laptop, or using a XRANDR-aware Desktop where you resize +often. It is best to be viewing with a vncviewer that +supports the NewFBSize encoding, since it knows how to +react to screen size changes. Otherwise, libvncserver +tries to do so something reasonable for viewers that +cannot do this (portions of the screen may be clipped, +unused, etc). +.IP +"mode" defaults to "resize", which means create a +new, resized, framebuffer and hope all viewers can cope +with the change. "newfbsize" means first disconnect +all viewers that do not support the NewFBSize VNC +encoding, and then resize the framebuffer. "exit" +means disconnect all viewer clients, and then terminate +x11vnc. +.PP +\fB-padgeom\fR \fIWxH\fR +.IP +Whenever a new vncviewer connects, the framebuffer is +replaced with a fake, solid black one of geometry WxH. +Shortly afterwards the framebuffer is replaced with the +real one. This is intended for use with vncviewers +that do not support NewFBSize and one wants to make +sure the initial viewer geometry will be big enough +to handle all subsequent resizes (e.g. under \fB-xrandr,\fR +\fB-remote\fR id:windowid, rescaling, etc. +.PP \fB-o\fR \fIlogfile\fR .IP Write stderr messages to file \fIlogfile\fR instead of -to the terminal. Same as \fB-logfile\fR "file". +to the terminal. Same as "\fB-logfile\fR \fIfile\fR". .PP \fB-rc\fR \fIfilename\fR .IP @@ -395,19 +457,21 @@ and "," + "<" keys). Default: \fB-modtweak\fR .PP \fB-xkb\fR .IP -When in modtweak mode, use the XKEYBOARD extension -(if it exists) to do the modifier tweaking. This is -powerful and should be tried if there are still -keymapping problems when using the simpler \fB-modtweak.\fR +When in modtweak mode, use the XKEYBOARD extension (if +the X display supports it) to do the modifier tweaking. +This is powerful and should be tried if there are still +keymapping problems when using \fB-modtweak\fR by itself. .PP \fB-skip_keycodes\fR \fIstring\fR .IP -Skip keycodes not on your keyboard but your X server -thinks exist. Currently only applies to \fB-xkb\fR mode. -\fIstring\fR is a comma separated list of decimal -keycodes. Use this option to help x11vnc in the reverse -problem it tries to solve: Keysym -> Keycode(s) when -ambiguities exist. E.g. \fB-skip_keycodes\fR 94,114 +Ignore the comma separated list of decimal keycodes. +Perhaps these are keycodes not on your keyboard but +your X server thinks exist. Currently only applies +to \fB-xkb\fR mode. Use this option to help x11vnc in the +reverse problem it tries to solve: Keysym -> Keycode(s) +when ambiguities exist (more than one Keycode per +Keysym). Run 'xmodmap \fB-pk'\fR to see your keymapping. +E.g. "\fB-skip_keycodes\fR \fI94,114\fR" .PP \fB-add_keysyms\fR .IP @@ -437,10 +501,10 @@ or hex value) separated by a space. If no file named \fIstring\fR exists, it is instead interpreted as this form: key1-key2,key3-key4,... See <X11/keysymdef.h> header file for a list of Keysym names, or use -.IR xev (1) -. To map a key to a button click, use the -fake Keysyms "Button1", ..., etc. -E.g. \fB-remap\fR Super_R-Button2 +.IR xev (1). +To map a key to a button click, use the +fake Keysyms "Button1", ..., etc. E.g. "-remap +Super_R-Button2" (useful for pasting on a laptop) .PP \fB-norepeat,\fR \fB-repeat\fR .IP @@ -451,7 +515,7 @@ delays between key down and key up client events: either from large screen changes or high latency). Note: your VNC viewer side will likely do autorepeating, so this is no loss unless someone is simultaneously at -the real X display. Default: \fB-repeat\fR +the real X display. Default: \fB-norepeat\fR .PP \fB-nofb\fR .IP @@ -489,27 +553,36 @@ and CursorShapeUpdates extensions (cuts down on network traffic by not having to send the cursor image every time the pointer is moved), in which case these extensions are used (see \fB-nocursorshape\fR and \fB-nocursorpos\fR -below). For other viewers the cursor shape is written -directly to the framebuffer every time the pointer is -moved or changed and gets sent along with the other -framebuffer updates. In this case, there will be -some lag between the vnc viewer pointer and the remote -cursor position. +below to disable). For other viewers the cursor shape +is written directly to the framebuffer every time the +pointer is moved or changed and gets sent along with +the other framebuffer updates. In this case, there +will be some lag between the vnc viewer pointer and +the remote cursor position. .IP If the X display supports retrieving the cursor shape -information from the X server, then the default -is to use that mode. On Solaris this requires -the SUN_OVL extension and the \fB-overlay\fR option to be -supplied. (see also the \fB-overlay_nomouse\fR option). (Soon) -on XFree86/Xorg the XFIXES extension is required. -Either can be disabled with \fB-nocursor,\fR and also some -values of the "mode" option below. +information from the X server, then the default is +to use that mode. On Solaris this can be done with +the SUN_OVL extension using \fB-overlay\fR (see also the +\fB-overlay_nomouse\fR option). A similar overlay scheme +is used on IRIX. Xorg (e.g. Linux) and recent Solaris +Xsun servers support the XFIXES extension to retrieve +the exact cursor shape from the X server. If XFIXES +is present it is preferred over Overlay and is used by +default (see \fB-noxfixes\fR below). This can be disabled +with \fB-nocursor,\fR and also some values of the "mode" +option below. .IP The "mode" string can be used to fine-tune the displaying of cursor shapes. It can be used the following ways: .IP -"-cursor X" - when the cursor appears to be on the +"\fB-cursor\fR \fIarrow\fR" - just show the standard arrow +nothing more or nothing less. +.IP +"\fB-cursor\fR \fInone\fR" - same as "\fB-nocursor\fR" +.IP +"\fB-cursor\fR \fIX\fR" - when the cursor appears to be on the root window, draw the familiar X shape. Some desktops such as GNOME cover up the root window completely, and so this will not work, try "X1", etc, to try to @@ -517,17 +590,23 @@ shift the tree depth. On high latency links or slow machines there will be a time lag between expected and the actual cursor shape. .IP -"-cursor some" - like "X" but use additional +"\fB-cursor\fR \fIsome\fR" - like "X" but use additional heuristics to try to guess if the window should have a windowmanager-like resizer cursor or a text input I-beam cursor. This is a complete hack, but may be useful in some situations because it provides a little more feedback about the cursor shape. .IP -"-cursor most" - try to show as many cursors as -possible. Often this will only be the same as "some". -On Solaris if XFIXES is not available, \fB-overlay\fR mode -will be used. +"\fB-cursor\fR \fImost\fR" - try to show as many cursors as +possible. Often this will only be the same as "some" +unless the display has overlay visuals or XFIXES +extensions available. On Solaris and IRIX if XFIXES +is not available, \fB-overlay\fR mode will be attempted. +.PP +\fB-noxfixes\fR +.IP +Do not use the XFIXES extension to draw the exact cursor +shape even if it is available. .PP \fB-nocursorshape\fR .IP @@ -543,11 +622,13 @@ to see the pointer motions. Default: \fB-cursorpos\fR .PP \fB-xwarppointer\fR .IP -Move the pointer with XWarpPointer(3X) instead of XTEST -extension. Use this as a workaround if the pointer -motion behaves incorrectly, e.g. on touchscreens or -other non-standard setups. Also sometimes needed on -XINERAMA displays. +Move the pointer with +.IR XWarpPointer (3X) +instead of +the XTEST extension. Use this as a workaround +if the pointer motion behaves incorrectly, e.g. +on touchscreens or other non-standard setups. +Also sometimes needed on XINERAMA displays. .PP \fB-buttonmap\fR \fIstring\fR .IP @@ -586,22 +667,29 @@ improves response on slow setups, but you lose all visual feedback for drags, text selection, and some menu traversals. .PP -\fB-old_pointer\fR -.IP -Use the original pointer input handling mechanism. -See check_input() and pointer() in source file for -details. -.PP -\fB-old_pointer2\fR -.IP -The default pointer input handling algorithm was changed -again, this option indicates to use the second one. +\fB-pointer_mode\fR \fIn\fR +.IP +Various pointer update schemes. The problem is pointer +motion can cause rapid changes on the screen, e.g. a +window drag. Neither x11vnc nor the bandwidth to the +vncviewers can keep up these rapid screen changes: +everything bogs down when dragging or scrolling. +Note that most video h/w is optimized for writing, not +reading (a 50X rate difference is possible) and x11vnc +is reading all the time. So a scheme has to be used to +"eat" much of that pointer input before re-polling the +screen. n can be 1 to 4. n=1 was the original scheme +used to about Jan 2004. n=2 is an improved scheme. +n=3 is basically a dynamic \fB-nodragging\fR mode: it detects +if the mouse drag motion has paused and refreshes +the display. n=4 is TBD. The default n is 2. .PP \fB-input_skip\fR \fIn\fR .IP -For the old pointer handling when non-threaded: try to +For the pointer handling when non-threaded: try to read n user input events before scanning display. n < 0 means to act as though there is always user input. +Default: 10 .PP \fB-debug_pointer\fR .IP @@ -629,6 +717,12 @@ down on load. Default: 30 Monitor activity and if low take longer naps between polls to really cut down load when idle. Default: off .PP +\fB-sb\fR \fItime\fR +.IP +Time in seconds after NO activity (e.g. screen blank) +to really throttle down the screen polls (i.e. sleep +for about 1.5 secs). Use 0 to disable. Default: 60 +.PP \fB-sigpipe\fR \fIstring\fR .IP Broken pipe (SIGPIPE) handling. \fIstring\fR can be @@ -663,6 +757,444 @@ by checking the tile near the boundary. Default: 3 Tolerance in pixels to mark a tiles edges as changed. Default: 2 .PP +\fB-gui\fR \fI[gui-opts]\fR +.IP +Start up a simple tcl/tk gui based on the the remote +control options \fB-remote/-query\fR described below. +Requires the "wish" program to be installed on the +machine. "gui-opts" is not required: the default is +to start up both the gui and x11vnc with the gui showing +up on the X display in the environment variable DISPLAY. +.IP +"gui-opts" can be a comma separated list of items. +Currently there are only two types of items: 1) a gui +mode and 2) the X display the gui should display on. +The gui mode can be "start", "conn", or "wait" +"start" is the default mode above and is not required. +"conn" means do not automatically start up x11vnc, +but instead just try to connect to an existing x11vnc +process. "wait" means just start the gui and nothing +else (you will later instruct the gui to start x11vnc +or connect to an existing one.) +.IP +Note the possible confusion regarding the potentially +two different X displays: x11vnc polls one, but you +may want the gui to appear on another. For example, if +you ssh in and x11vnc is not running yet you may want +the gui to come back to you via your ssh redirected X +display (e.g. localhost:10). +.IP +Examples: "x11vnc \fB-gui",\fR "x11vnc \fB-gui\fR localhost:10", +"x11vnc \fB-gui\fR :10", "x11vnc \fB-gui\fR wait,:10", +"x11vnc \fB-gui\fR <x11vnc-opts...>" +.IP +If you do not specify a gui X display in "gui-opts" +then the DISPLAY environment variable and \fB-display\fR +option are tried (in that order). Regarding the x11vnc +X display the gui will try to connect to, it first +tries \fB-display\fR and then DISPLAY. For example, "x11vnc +\fB-display\fR :0 \fB-gui\fR otherhost:0", will remote control an +x11vnc polling :0 and display the gui on otherhost:0 +.IP +If you do not intend to start x11vnc from the gui +(i.e. just remote control an existing one), then the +gui process can run on a different machine from the +x11vnc server as long as X permissions, etc. permit +communication between the two. +.PP +\fB-remote\fR \fIcommand\fR +.IP +Remotely control some aspects of an already running +x11vnc server. "\fB-R\fR" and "\fB-r\fR" are aliases for +"\fB-remote\fR". After the remote control command is +sent to the running server the 'x11vnc \fB-remote\fR ...' +command exits. You can often use the \fB-query\fR command +(see below) to see if the x11vnc server processed your +\fB-remote\fR command. +.IP +The default communication channel is that of X +properties (specifically VNC_CONNECT), and so this +command must be run with correct settings for DISPLAY +and possibly XAUTHORITY to connect to the X server +and set the property. Alternatively, use the \fB-display\fR +and \fB-auth\fR options to set them to the correct values. +The running server cannot use the \fB-novncconnect\fR option +because that disables the communication channel. +See below for alternate channels. +.IP +For example: 'x11vnc \fB-remote\fR stop' (which is the same as +\'x11vnc \fB-R\fR stop') will close down the x11vnc server. +\'x11vnc \fB-R\fR shared' will enable shared connections, and +\'x11vnc \fB-R\fR scale:3/4' will rescale the desktop. +.IP +Note: the more drastic the change induced by the \fB-remote\fR +command, the bigger the chance for bugs or crashes. +Please report reproducible bugs. +.IP +.IP +The following \fB-remote/-R\fR commands are supported: +.IP +stop terminate the server, same as "quit" + "exit" or "shutdown" +.IP +ping see if the x11vnc server responds. + Return is: ans=ping:<xdisplay> +.IP +blacken try to push a black fb update to all + clients (due to timings a client + could miss it). Same as "zero", also + "zero:x1,y1,x2,y2" for a rectangle. +.IP +refresh send the entire fb to all clients. +.IP +reset recreate the fb, polling memory, etc. +.IP +id:windowid set \fB-id\fR window to "windowid". empty + or "root" to go back to root window +.IP +sid:windowid set \fB-sid\fR window to "windowid" +.IP +flashcmap enable \fB-flashcmap\fR mode. +.IP +noflashcmap disable \fB-flashcmap\fR mode. +.IP +notruecolor enable \fB-notruecolor\fR mode. +.IP +truecolor disable \fB-notruecolor\fR mode. +.IP +overlay enable \fB-overlay\fR mode (if applicable). +.IP +nooverlay disable \fB-overlay\fR mode. +.IP +overlay_cursor in \fB-overlay\fR mode, enable cursor drawing. +.IP +overlay_nocursor disable cursor drawing. same as + nooverlay_cursor. +.IP +visual:vis set \fB-visual\fR to "vis" +.IP +scale:frac set \fB-scale\fR to "frac" +.IP +viewonly enable \fB-viewonly\fR mode. +.IP +noviewonly disable \fB-viewonly\fR mode. +.IP +shared enable \fB-shared\fR mode. +.IP +noshared disable \fB-shared\fR mode. +.IP +forever enable \fB-forever\fR mode. +.IP +noforever disable \fB-forever\fR mode. +.IP +deny deny any new connections, same as "lock" +.IP +nodeny allow new connections, same as "unlock" +.IP +connect:host do reverse connection to host, "host" + may be a comma separated list of hosts + or host:ports. See \fB-connect.\fR +.IP +disconnect:host disconnect any clients from "host" + same as "close:host". Use host + "all" to close all current clients. + If you know the client internal hex ID, + e.g. 0x3 (returned by \fB-query\fR clients and + RFB_CLIENT_ID), you can use that too. +.IP +allowonce:host For the next connection only, allow + connection from "host". +.IP +allow:hostlist set \fB-allow\fR list to (comma separated) + "hostlist". See \fB-allow\fR and \fB-localhost.\fR + Do not use with \fB-allow\fR /path/to/file + Use "+host" to add a single host, and + use "\fB-host\fR" to delete a single host +.IP +localhost enable \fB-localhost\fR mode +.IP +nolocalhost disable \fB-localhost\fR mode +.IP +accept:cmd set \fB-accept\fR "cmd" (empty to disable). +.IP +gone:cmd set \fB-gone\fR "cmd" (empty to disable). +.IP +noshm enable \fB-noshm\fR mode. +.IP +shm disable \fB-noshm\fR mode (i.e. use shm). +.IP +flipbyteorder enable \fB-flipbyteorder\fR mode, you may need + to set noshm for this to do something. +.IP +noflipbyteorder disable \fB-flipbyteorder\fR mode. +.IP +onetile enable \fB-onetile\fR mode. (you may need to + set shm for this to do something) +.IP +noonetile disable \fB-onetile\fR mode. +.IP +blackout:str set \fB-blackout\fR "str" (empty to disable). + See \fB-blackout\fR for the form of "str" + (basically: WxH+X+Y,...) + Use "+WxH+X+Y" to append a single + rectangle use "-WxH+X+Y" to delete one +.IP +xinerama enable \fB-xinerama\fR mode. (if applicable) +.IP +noxinerama disable \fB-xinerama\fR mode. +.IP +xrandr enable \fB-xrandr\fR mode. (if applicable) +.IP +noxrandr disable \fB-xrandr\fR mode. +.IP +xrandr_mode:mode set the \fB-xrandr\fR mode to "mode". +.IP +padgeom:WxH set \fB-padgeom\fR to WxH (empty to disable) + If WxH is "force" or "do" the padded + geometry fb is immediately applied. +.IP +quiet enable \fB-quiet\fR mode. +.IP +noquiet disable \fB-quiet\fR mode. +.IP +modtweak enable \fB-modtweak\fR mode. +.IP +nomodtweak enable \fB-nomodtweak\fR mode. +.IP +xkb enable \fB-xkb\fR modtweak mode. +.IP +noxkb disable \fB-xkb\fR modtweak mode. +.IP +skip_keycodes:str enable \fB-xkb\fR \fB-skip_keycodes\fR "str". +.IP +add_keysyms enable \fB-add_keysyms\fR mode. +.IP +noadd_keysyms stop adding keysyms. those added will + still be removed at exit. +.IP +clear_mods enable \fB-clear_mods\fR mode and clear them. +.IP +noclear_mods disable \fB-clear_mods\fR mode. +.IP +clear_keys enable \fB-clear_keys\fR mode and clear them. +.IP +noclear_keys disable \fB-clear_keys\fR mode. +.IP +remap:str set \fB-remap\fR "str" (empty to disable). + See \fB-remap\fR for the form of "str" + (basically: key1-key2,key3-key4,...) + Use "+key1-key2" to append a single + keymapping, use "-key1-key2" to delete. +.IP +norepeat enable \fB-norepeat\fR mode. +.IP +repeat disable \fB-norepeat\fR mode. +.IP +bell enable bell (if supported). +.IP +nobell disable bell. +.IP +sel disable \fB-nosel\fR mode. +.IP +nosel enable \fB-nosel\fR mode. +.IP +primary disable \fB-noprimary\fR mode. +.IP +noprimary enable \fB-noprimary\fR mode. +.IP +cursor:mode enable \fB-cursor\fR "mode". +.IP +show_cursor enable showing a cursor. +.IP +noshow_cursor disable showing a cursor. (same as + "nocursor") +.IP +xfixes enable xfixes cursor shape mode. +.IP +noxfixes disable xfixes cursor shape mode. +.IP +cursorshape disable \fB-nocursorshape\fR mode. +.IP +nocursorshape enable \fB-nocursorshape\fR mode. +.IP +cursorpos disable \fB-nocursorpos\fR mode. +.IP +nocursorpos enable \fB-nocursorpos\fR mode. +.IP +xwarp enable \fB-xwarppointer\fR mode. +.IP +noxwarp disable \fB-xwarppointer\fR mode. +.IP +buttonmap:str set \fB-buttonmap\fR "str", empty to disable +.IP +dragging disable \fB-nodragging\fR mode. +.IP +nodragging enable \fB-nodragging\fR mode. +.IP +pointer_mode n set \fB-pointer_mode\fR to n. +.IP +input_skip n set \fB-input_skip\fR to n. +.IP +debug_pointer enable \fB-debug_pointer,\fR same as "dp" +.IP +nodebug_pointer disable \fB-debug_pointer,\fR same as "nodp" +.IP +debug_keyboard enable \fB-debug_keyboard,\fR same as "dk" +.IP +nodebug_keyboard disable \fB-debug_keyboard,\fR same as "nodk" +.IP +defer:n set \fB-defer\fR to n ms,same as deferupdate:n +.IP +wait:n set \fB-wait\fR to n ms. +.IP +nap enable \fB-nap\fR mode. +.IP +nonap disable \fB-nap\fR mode. +.IP +sb:n set \fB-sb\fR to n s, same as screen_blank:n +.IP +fs:frac set \fB-fs\fR fraction to "frac", e.g. 0.5 +.IP +gaps:n set \fB-gaps\fR to n. +.IP +grow:n set \fB-grow\fR to n. +.IP +fuzz:n set \fB-fuzz\fR to n. +.IP +progressive:n set libvncserver \fB-progressive\fR slice + height parameter to n. +.IP +file:name run \fB-remote\fR commands from file "name", + one command per line,blank and # skipped +.IP +noremote disable the \fB-remote\fR command processing, + it cannot be turned back on. +.IP +.IP +The +.IR vncconnect (1) +command from standard VNC +.IP +distributions may also be used if string is prefixed +.IP +with "cmd=" E.g. 'vncconnect cmd=stop'. Under some +.IP +circumstances +.IR xprop (1) +can used if it supports \fB-set\fR +.IP +(see the FAQ). +.IP +.IP +If "\fB-connect\fR \fI/path/to/file\fR" has been supplied to the +.IP +running x11vnc server then that file can be used as a +.IP +communication channel (this is the only way to remote +.IP +control one of many x11vnc's polling the same X display) +.IP +Simply run: 'x11vnc \fB-connect\fR /path/to/file \fB-remote\fR ...' +.IP +or you can directly write to the file via something +.IP +like: "echo cmd=stop > /path/to/file", etc. +.PP +\fB-query\fR \fIvariable\fR +.IP +Like \fB-remote,\fR except just query the value of +\fIvariable\fR. "\fB-Q\fR" is an alias for "\fB-query\fR". +Multiple queries can be done by separating variables +by commas, e.g. \fB-query\fR var1,var2. The results come +back in the form ans=var1:value1,ans=var2:value2,... +to the standard output. If a variable is read-only, +it comes back with prefix "aro=" instead of "ans=". +.IP +Some \fB-remote\fR commands are pure actions that do not make +sense as variables, e.g. "stop" or "disconnect", +in these cases the value returned is "N/A". To direct +a query straight to the VNC_CONNECT property or connect +file use "qry=..." instead of "cmd=..." +.IP +Here is the current list of "variables" that can +be supplied to the \fB-query\fR command. This includes the +"N/A" ones that return no useful info. For variables +names that do not correspond to an x11vnc option or +remote command, we hope the name makes it obvious what +the returned value corresponds to (hint: the ext_* +variables correspond to the presence of X extensions): +.IP +ans= stop quit exit shutdown ping blacken zero refresh +reset close disconnect id sid flashcmap noflashcmap +truecolor notruecolor overlay nooverlay overlay_cursor +overlay_yescursor nooverlay_cursor overlay_nocursor +visual scale viewonly noviewonly shared noshared +forever noforever once deny lock nodeny unlock connect +allowonce allow localhost nolocalhost accept gone shm +noshm flipbyteorder noflipbyteorder onetile noonetile +blackout xinerama noxinerama xrandr noxrandr xrandr_mode +padgeom quiet q noquiet modtweak nomodtweak xkb noxkb +skip_keycodes add_keysyms noadd_keysyms clear_mods +noclear_mods clear_keys noclear_keys remap repeat +norepeat bell nobell sel nosel primary noprimary +cursorshape nocursorshape cursorpos nocursorpos cursor +show_cursor noshow_cursor nocursor xfixes noxfixes xwarp +xwarppointer noxwarp noxwarppointer buttonmap dragging +nodragging pointer_mode input_skip debug_pointer dp +nodebug_pointer nodp debug_keyboard dk nodebug_keyboard +nodk deferupdate defer wait nap nonap sb screen_blank +fs gaps grow fuzz progressive noremote +.IP +aro= display vncdisplay desktopname desktop auth +rootshift scale_str scaled_x scaled_y scale_numer +scale_denom scale_fac scaling_noblend scaling_nomult4 +scaling_pad scaling_interpolate inetd safer unsafe +passwdfile using_shm logfile o rc norc h help V version +lastmod bg nofb sigpipe threads clients client_count +pid ext_xtest ext_xkb ext_xshm ext_xinerama ext_overlay +ext_xfixes ext_xdamage ext_xrandr rootwin num_buttons +button_mask mouse_x mouse_y bpp depth indexed_color +dpy_x dpy_y rfbport rfbwait rfbauth passwd alwaysshared +dontdisconnect httpdir enablehttpproxy +.PP +\fB-noremote\fR +.IP +Do not process any remote control commands or queries. +.IP +A note about security wrt remote control commands. +If someone can connect to the X display and change the +property VNC_CONNECT, then they can remotely control +x11vnc. Normally access to the X display is protected. +Note that if they can modify VNC_CONNECT, they could +also run their own x11vnc and have complete control +of the desktop. If the "\fB-connect\fR \fI/path/to/file\fR" +channel is being used, obviously anyone who can write +to /path/to/file can remotely control x11vnc. So be +sure to protect the X display and that file's write +permissions. +.PP +\fB-unsafe\fR +.IP +If x11vnc is running as root (e.g. inetd or Xsetup for +a display manager) a few remote commands are disabled +(currently: id:pick, accept:<cmd>, and gone:<cmd>) +because they are associated with running external +programs. If you specify \fB-unsafe,\fR then these remote +control commands are allowed when running as root. +When running as non-root all commands are allowed. +See \fB-safer\fR below. +.PP +\fB-safer\fR +.IP +Even if not running as root, disable the above unsafe +remote control commands. +.PP +\fB-deny_all\fR +.IP +For use with \fB-remote\fR nodeny: start out denying all +incoming clients until "\fB-remote\fR \fInodeny\fR" is used to +let them in. +.PP These options are passed to libvncserver: .PP \fB-rfbport\fR \fIport\fR @@ -736,7 +1268,8 @@ run by \fB-accept\fR and \fB-gone\fR: .IR RFB_SERVER_PORT , .IR RFB_X11VNC_PID , .IR RFB_CLIENT_ID , -.IR RFB_CLIENT_COUNT +.IR RFB_CLIENT_COUNT , +.IR RFB_MODE .SH "SEE ALSO" .IR vncviewer (1), .IR vncpasswd (1), @@ -754,7 +1287,8 @@ run by \fB-accept\fR and \fB-gone\fR: .IR ipcrm (1), .IR http://www.tightvnc.com , .IR http://www.realvnc.com , -.IR http://www.karlrunge.com/x11vnc/ +.IR http://www.karlrunge.com/x11vnc/ , +.IR http://www.karlrunge.com/x11vnc/#faq .SH AUTHORS x11vnc was written by Karl J. Runge <runge@karlrunge.com>, it is part of the LibVNCServer project <http://sf.net/projects/libvncserver>. diff --git a/x11vnc/x11vnc.c b/x11vnc/x11vnc.c index 5df5db3..c26db31 100644 --- a/x11vnc/x11vnc.c +++ b/x11vnc/x11vnc.c @@ -47,27 +47,33 @@ * gcc should be used on all platforms. To build a threaded version put * "-D_REENTRANT -DX11VNC_THREADED" in the environment variable CFLAGS * or CPPFLAGS (e.g. before running the libvncserver configure). The - * threaded mode is a bit more responsive, but can be unstable. + * threaded mode is a bit more responsive, but can be unstable (e.g. + * if more than one client the same tight or zrle encoding). * * Known shortcomings: * * The screen updates are good, but of course not perfect since the X * display must be continuously polled and read for changes (as opposed to * receiving a change callback from the X server, if that were generally - * possible...). So, e.g., opaque moves and similar window activity - * can be very painful; one has to modify one's behavior a bit. + * possible... (Update: this seems to be handled now with the X DAMAGE + * extension, but unfortunately that doesn't seem to address the slow + * read from the video h/w). So, e.g., opaque moves and similar window + * activity can be very painful; one has to modify one's behavior a bit. * * General audio at the remote display is lost unless one separately * sets up some audio side-channel such as esd. * * It does not appear possible to query the X server for the current * cursor shape. We can use XTest to compare cursor to current window's - * cursor, but we cannot extract what the cursor is... + * cursor, but we cannot extract what the cursor is... (Update: we now + * use XFIXES extension for this. Also on Solaris and IRIX Overlay + * extensions exists that allow drawing the mouse into the framebuffer) * - * Nevertheless, the current *position* of the remote X mouse pointer - * is shown with the -cursor option. Further, if -cursorX or -X is used, a - * trick is done to at least show the root window cursor vs non-root cursor. - * (perhaps some heuristic can be done to further distinguish cases...) + * The current *position* of the remote X mouse pointer is shown with + * the -cursor option. Further, if -cursorX or -X is used, a trick + * is done to at least show the root window cursor vs non-root cursor. + * (perhaps some heuristic can be done to further distinguish cases..., + * currently -cursor some is a first hack at this) * * Windows using visuals other than the default X visual may have * their colors messed up. When using 8bpp indexed color, the colormap @@ -75,11 +81,11 @@ * -flashcmap option to have colormap flashing as the pointer moves * windows with private colormaps (slow). Displays with mixed depth 8 and * 24 visuals will incorrectly display windows using the non-default one. - * On Sun hardware we try to work around this with -overlay. + * On Sun and Sgi hardware we can to work around this with -overlay. * * Feature -id <windowid> can be picky: it can crash for things like the - * window not sufficiently mapped into server memory, use of -cursor, etc. - * SaveUnders menus, popups, etc will not be seen. + * window not sufficiently mapped into server memory, etc. SaveUnders + * menus, popups, etc will not be seen. * * Occasionally, a few tile updates can be missed leaving a patch of * color that needs to be refreshed. This may only be when threaded, @@ -92,11 +98,11 @@ */ /* - * These ' -- filename -- ' comments represent a partial cleanup: + * These ' -- filename.[ch] -- ' comments represent a partial cleanup: * they are an odd way to indicate how this huge file would be split up * someday into multiple files. Not finished, externs and other things - * would need to be done, but it indicates the breakup, including static - * keyword for local items. + * would need to be done, but it indicates a breakup, including static + * keyword for some items. * * The primary reason we do not break up this file is for user * convenience: those wanting to use the latest version download a single @@ -105,52 +111,37 @@ /* -- x11vnc.h -- */ -#define NON_CVS 0 /* - * At some point beyond 0.7pre remove these two definitions since we - * have them set in configure (for all users of this x11vnc.c file). - * Then move them to the comment below. + * if you are inserting this file, x11vnc.c into an old CVS tree you + * may need to set OLD_TREE to 1. */ -#if NON_CVS -#define LIBVNCSERVER_HAVE_XSHM -#define LIBVNCSERVER_HAVE_XTEST -#endif +#define OLD_TREE 0 +#if OLD_TREE /* * If you are building in an older libvncserver tree with this newer - * x11vnc.c file you may need to uncomment some of these lines since - * your older libvncserver configure is not setting them. - * - * For LIBVNCSERVER_HAVE_LIBXINERAMA you may also need to add to the - * linking -lXinerama (by setting LDFLAGS=-lXinerama before configure). - * -#define LIBVNCSERVER_HAVE_LIBXINERAMA -#define LIBVNCSERVER_HAVE_XFIXES -#define LIBVNCSERVER_HAVE_XDAMAGE + * x11vnc.c file using OLD_TREE=1 you may need to set some of these lines + * since your older libvncserver configure is not setting them. * + * For the features LIBVNCSERVER_HAVE_LIBXINERAMA and + * LIBVNCSERVER_HAVE_XFIXES you may also need to add + * -lXinerama or -lXfixes, respectively, to the linking line, e.g. + * by setting them in LD_FLAGS before running configure. */ +#define LIBVNCSERVER_HAVE_XSHM +#define LIBVNCSERVER_HAVE_XTEST /* - * This is another transient for building in older libvncserver trees. +#define LIBVNCSERVER_HAVE_LIBXINERAMA +#define LIBVNCSERVER_HAVE_XFIXES +#define LIBVNCSERVER_HAVE_XDAMAGE */ -#if NON_CVS -#define dontDisconnect rfbDontDisconnect -#define neverShared rfbNeverShared -#define alwaysShared rfbAlwaysShared -#define clientHead rfbClientHead -#define serverFormat rfbServerFormat -#define port rfbPort -#define listenSock rfbListenSock -#define deferUpdateTime rfbDeferUpdateTime -#define authPasswdData rfbAuthPasswdData -#define rfbEncryptAndStorePasswd vncEncryptAndStorePasswd #endif #include <unistd.h> #include <signal.h> #include <sys/utsname.h> - #include <X11/Xlib.h> #include <X11/Xutil.h> @@ -164,6 +155,23 @@ #include <rfb/rfb.h> #include <rfb/rfbregion.h> +/* + * This is another transient for building in older libvncserver trees, + * due to the API change: + */ +#if OLD_TREE +#define dontDisconnect rfbDontDisconnect +#define neverShared rfbNeverShared +#define alwaysShared rfbAlwaysShared +#define clientHead rfbClientHead +#define serverFormat rfbServerFormat +#define port rfbPort +#define listenSock rfbListenSock +#define deferUpdateTime rfbDeferUpdateTime +#define authPasswdData rfbAuthPasswdData +#define rfbEncryptAndStorePasswd vncEncryptAndStorePasswd +#endif + #ifdef LIBVNCSERVER_HAVE_XSHM #include <sys/ipc.h> #include <sys/shm.h> @@ -182,23 +190,73 @@ #include <X11/extensions/Xinerama.h> #endif -#if defined (__SVR4) && defined (__sun) -#define SOLARIS -#include <X11/extensions/transovl.h> -#endif - #ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H #include <sys/socket.h> #endif +#include <netdb.h> +extern int h_errno; + #ifdef LIBVNCSERVER_HAVE_NETINET_IN_H #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> #endif +/* + * overlay/multi-depth screen reading support + * undef SOLARIS_OVERLAY or IRIX_OVERLAY if there are problems building. + */ + +/* solaris/sun */ +#if defined (__SVR4) && defined (__sun) +#define SOLARIS +#define SOLARIS_OVERLAY +#define OVERLAY_OS +#endif +#ifdef SOLARIS_OVERLAY +#include <X11/extensions/transovl.h> +#endif + +/* irix/sgi */ +#if defined(__sgi) +#define IRIX +#define IRIX_OVERLAY +#define OVERLAY_OS +#endif +#ifdef IRIX_OVERLAY +#include <X11/extensions/readdisplay.h> +#endif + +int overlay_present = 0; + +/* + * Ditto for librandr. + * (e.g. LDFLAGS=-lXrandr before configure). +#define LIBVNCSERVER_HAVE_LIBXRANDR + */ +#ifdef LIBVNCSERVER_HAVE_LIBXRANDR +#include <X11/extensions/Xrandr.h> +static int xrandr_base_event_type; +#endif + +int xfixes_present = 0; +int use_xfixes = 1; +int got_xfixes_cursor_notify = 0; + +#ifdef LIBVNCSERVER_HAVE_LIBXFIXES +#include <X11/extensions/Xfixes.h> +static int xfixes_base_event_type; +#endif + +int xdamage_present = 0; +#ifdef LIBVNCSERVER_HAVE_LIBXDAMAGE +#include <X11/extensions/Xdamage.h> +static int xdamage_base_event_type; +#endif + /* date +'lastmod: %Y-%m-%d' */ -char lastmod[] = "0.6.3pre lastmod: 2004-08-31"; +char lastmod[] = "0.6.3pre lastmod: 2004-12-17"; /* X display info */ @@ -207,7 +265,7 @@ int scr; Window window, rootwin; /* polled window, root window (usu. same) */ Visual *default_visual; /* the default visual (unless -visual) */ int bpp, depth; -int indexed_colour = 0; +int indexed_color = 0; int dpy_x, dpy_y; /* size of display */ int off_x, off_y; /* offsets for -sid */ int button_mask = 0; /* button state and info */ @@ -217,10 +275,11 @@ int num_buttons = -1; XImage *scanline; XImage *fullscreen; XImage **tile_row; /* for all possible row runs */ +XImage *fb0; #ifndef LIBVNCSERVER_HAVE_XSHM /* - * for simplicity, define these since we'll never use them + * for simplicity, define this struct since we'll never use them * under using_shm = 0. */ typedef struct { @@ -234,16 +293,27 @@ XShmSegmentInfo fullscreen_shm; XShmSegmentInfo *tile_row_shm; /* for all possible row runs */ /* rfb screen info */ -rfbScreenInfoPtr screen; +rfbScreenInfoPtr screen = NULL; +char *rfb_desktop_name = NULL; +char vnc_desktop_name[256]; char *main_fb; /* our copy of the X11 fb */ char *rfb_fb; /* same as main_fb unless transformation */ +char *fake_fb = NULL; /* used under -padgeom */ int rfb_bytes_per_line; int main_bytes_per_line; unsigned long main_red_mask, main_green_mask, main_blue_mask; unsigned short main_red_max, main_green_max, main_blue_max; unsigned short main_red_shift, main_green_shift, main_blue_shift; +/* we now have a struct with client specific data: */ +typedef struct _ClientData { + int had_cursor_shape_updates; + int had_cursor_pos_updates; + int uid; +} ClientData; + /* scaling parameters */ +char *scale_str = NULL; double scale_fac = 1.0; int scaling = 0; int scaling_noblend = 0; /* no blending option (very course) */ @@ -253,7 +323,6 @@ int scaling_interpolate = 0; /* use interpolation scheme when shrinking */ int scaled_x = 0, scaled_y = 0; /* dimensions of scaled display */ int scale_numer = 0, scale_denom = 0; /* n/m */ - /* size of the basic tile unit that is polled for changes: */ int tile_x = 32; int tile_y = 32; @@ -268,30 +337,46 @@ time_t last_event, last_input, last_client = 0; /* last client to move pointer */ rfbClientPtr last_pointer_client = NULL; +/* more transient kludge variables: */ int cursor_x, cursor_y; /* x and y from the viewer(s) */ int got_user_input = 0; int got_pointer_input = 0; int got_keyboard_input = 0; int last_keyboard_input = 0; int fb_copy_in_progress = 0; +int drag_in_progress = 0; int shut_down = 0; +int do_copy_screen = 0; +time_t damage_time = 0; +int damage_delay = 0; + +char *program_name = NULL; +char *program_cmdline = NULL; /* string for the VNC_CONNECT property */ -#define VNC_CONNECT_MAX 512 +#define VNC_CONNECT_MAX 16384 char vnc_connect_str[VNC_CONNECT_MAX+1]; Atom vnc_connect_prop = None; /* function prototypes (see filename comment above) */ int all_clients_initialized(void); +void close_all_clients(void); +void close_clients(char *); void autorepeat(int restore); -char *bitprint(unsigned int); +char *bitprint(unsigned int, int); void blackout_tiles(void); void check_connect_inputs(void); +void check_padded_fb(void); void clean_up_exit(int); void clear_modifiers(int init); void clear_keys(void); -void copy_screen(void); +int copy_screen(void); +void check_black_fb(void); +void do_new_fb(int); +void install_padded_fb(char *); +void install_fake_fb(int, int, int); +void remove_fake_fb(void); int add_keysym(KeySym); void delete_keycode(KeyCode); @@ -299,16 +384,23 @@ void delete_added_keycodes(void); double dtime(double *); -void initialize_blackout(char *); +void initialize_blackouts(char *); +void initialize_blackouts_and_xinerama(void); +void initialize_keyboard_and_pointer(void); void initialize_modtweak(void); void initialize_pointer_map(char *); +void initialize_cursors_mode(void); void initialize_remap(char *); void initialize_screen(int *argc, char **argv, XImage *fb); -void initialize_shm(void); +void initialize_polling_images(void); void initialize_signals(void); void initialize_tiles(void); +void free_tiles(void); void initialize_watch_bell(void); void initialize_xinerama(void); +void initialize_xfixes(void); +void initialize_xrandr(void); +XImage *initialize_xdisplay_fb(void); void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client); @@ -335,14 +427,24 @@ void pointer(int mask, int x, int y, rfbClientPtr client); void cursor_position(int, int); void read_vnc_connect_prop(void); +void set_vnc_connect_prop(char *); +char *process_remote_cmd(char *, int); void rfbPE(rfbScreenInfoPtr, long); void rfbCFD(rfbScreenInfoPtr, long); int scan_for_updates(void); -void set_colormap(void); +void set_colormap(int); void set_offset(void); +void set_rfb_cursor(int); void set_visual(char *vstring); void set_cursor(int, int, int); +void set_no_cursor(void); +void set_cursor_was_changed(rfbScreenInfoPtr); int get_which_cursor(void); +int get_xfixes_cursor(int); + +void disable_cursor_shape_updates(rfbScreenInfoPtr); +void restore_cursor_shape_updates(rfbScreenInfoPtr); +int new_fb_size_clients(rfbScreenInfoPtr); void shm_clean(XShmSegmentInfo *, XImage *); void shm_delete(XShmSegmentInfo *); @@ -350,18 +452,40 @@ void shm_delete(XShmSegmentInfo *); void check_x11_pointer(void); void check_bell_event(void); void check_xevents(void); +char *this_host(void); + +int get_remote_port(int sock); +int get_local_port(int sock); +char *get_remote_host(int sock); +char *get_local_host(int sock); void xcut_receive(char *text, int len, rfbClientPtr client); void zero_fb(int, int, int, int); +void push_black_screen(int); +void push_sleep(int); +void refresh_screen(void); /* -- options.h -- */ /* * variables for the command line options */ +char *use_dpy = NULL; +char *auth_file = NULL; +char *visual_str = NULL; +char *logfile = NULL; +char *passwdfile = NULL; +char *blackout_string = NULL; +char *rc_rcfile = NULL; +int rc_norc = 0; +int opts_bg = 0; int shared = 0; /* share vnc display. */ +int deny_all = 0; /* global locking of new clients */ +int accept_remote_cmds = 1; /* -noremote */ +int safe_remote_only = 0; /* -safer, -unsafe */ char *allow_list = NULL; /* for -allow and -localhost */ +char *allow_once = NULL; /* one time -allow */ char *accept_cmd = NULL; /* for -accept */ char *gone_cmd = NULL; /* for -gone */ int view_only = 0; /* clients can only watch. */ @@ -370,15 +494,26 @@ int inetd = 0; /* spawned from inetd(1) */ int connect_once = 1; /* disconnect after first connection session. */ int flash_cmap = 0; /* follow installed colormaps */ int force_indexed_color = 0; /* whether to force indexed color for 8bpp */ +int launch_gui = 0; /* -gui */ int use_modifier_tweak = 1; /* use the shift/altgr modifier tweak */ int use_iso_level3 = 0; /* ISO_Level3_Shift instead of Mode_switch */ int clear_mods = 0; /* -clear_mods (1) and -clear_keys (2) */ int nofb = 0; /* do not send any fb updates */ -int subwin = 0; /* -id */ +unsigned long subwin = 0x0; /* -id, -sid */ int xinerama = 0; /* -xinerama */ +int xrandr = 0; /* -xrandr */ +int xrandr_present = 0; +int xrandr_width = -1; +int xrandr_height = -1; +int xrandr_rotation = -1; +Time xrandr_timestamp = 0; +Time xrandr_cfg_time = 0; +char *xrandr_mode = NULL; +char *pad_geometry = NULL; +time_t pad_geometry_time; char *client_connect = NULL; /* strings for -connect option */ char *client_connect_file = NULL; @@ -386,21 +521,28 @@ int vnc_connect = 1; /* -vncconnect option */ int show_cursor = 1; /* show cursor shapes */ int show_multiple_cursors = 0; /* show X when on root background, etc */ -char *multiple_cursors_mode = "default"; +char *multiple_cursors_mode = NULL; int cursor_pos_updates = 1; /* cursor position updates -cursorpos */ int cursor_shape_updates = 1; /* cursor shape updates -nocursorshape */ int use_xwarppointer = 0; /* use XWarpPointer instead of XTestFake... */ int show_dragging = 1; /* process mouse movement events */ -int no_autorepeat = 0; /* turn off autorepeat with clients */ +int no_autorepeat = 1; /* turn off autorepeat with clients */ int watch_bell = 1; /* watch for the bell using XKEYBOARD */ +int sound_bell = 1; /* actually send it */ int xkbcompat = 0; /* ignore XKEYBOARD extension */ int use_xkb = 0; /* try to open Xkb connection (for bell or other) */ int use_xkb_modtweak = 0; /* -xkb */ char *skip_keycodes = NULL; int add_keysyms = 0; /* automatically add keysyms to X server */ -int old_pointer = 0; /* use the old ways of updating the pointer */ +char *remap_file = NULL; /* -remap */ +char *pointer_remap = NULL; +int pointer_mode = 2; /* use the various ways of updating pointer */ +int pointer_mode_max = 4; int single_copytile = 0; /* use the old way copy_tiles() */ +int single_copytile_orig = 0; +int single_copytile_count = 0; +int tile_shm_count = 0; int using_shm = 1; /* whether mit-shm is used */ int flip_byte_order = 0; /* sometimes needed when using_shm = 0 */ @@ -422,21 +564,33 @@ int ui_skip = 10; /* see watchloop. negative means ignore input */ int watch_selection = 1; /* normal selection/cutbuffer maintenance */ int watch_primary = 1; /* more dicey, poll for changes in PRIMARY */ -int sigpipe = 1; /* 0=skip, 1=ignore, 2=exit */ +char *sigpipe = NULL; /* skip, ignore, exit */ /* visual stuff for -visual override or -overlay */ VisualID visual_id = (VisualID) 0; int visual_depth = 0; -/* for -overlay mode on Solaris. X server draws cursor correctly. */ +/* for -overlay mode on Solaris/IRIX. X server draws cursor correctly. */ int overlay = 0; int overlay_cursor = 1; +#ifdef LIBVNCSERVER_HAVE_XSHM +int xshm_present = 1; +#else +int xshm_present = 0; +#endif #ifdef LIBVNCSERVER_HAVE_XTEST int xtest_present = 1; #else int xtest_present = 0; #endif +#ifdef LIBVNCSERVER_HAVE_XKEYBOARD +int xkb_present = 1; +#else +int xkb_present = 0; +#endif +int xinerama_present = 0; + /* tile heuristics: */ double fs_frac = 0.75; /* threshold tile fraction to do fullscreen updates. */ @@ -469,6 +623,7 @@ int got_nevershared = 0; /* -- util.h -- */ +#define NONUL(x) ((x) ? (x) : "") /* XXX usleep(3) is not thread safe on some older systems... */ struct timeval _mysleep; @@ -497,7 +652,7 @@ MUTEX(x11Mutex); #define X_UNLOCK UNLOCK(x11Mutex) #define X_INIT INIT_MUTEX(x11Mutex) -/* -- util.c -- ? */ +/* -- util.c -- */ /* * routine to keep 0 <= i < n, should use in more places... @@ -511,6 +666,14 @@ int nfix(int i, int n) { return i; } +int nabs(int n) { + if (n < 0) { + return -n; + } else { + return n; + } +} + void lowercase(char *str) { char *p; if (str == NULL) { @@ -523,6 +686,235 @@ void lowercase(char *str) { } } +int scan_hexdec(char *str, unsigned long *num) { + if (sscanf(str, "0x%lx", num) != 1) { + if (sscanf(str, "%ld", num) != 1) { + return 0; + } + } + return 1; +} + +void set_env(char *name, char *value) { + char *str; + str = malloc(strlen(name)+strlen(value)+2); + sprintf(str, "%s=%s", name, value); + putenv(str); +} + +int pick_windowid(unsigned long *num) { + char line[512]; + int ok = 0, n = 0, msec = 10, secmax = 15; + FILE *p; + + if (use_dpy) { + set_env("DISPLAY", use_dpy); + } + p = popen("xwininfo", "r"); + + if (! p) { + return 0; + } + + fprintf(stderr, "\n"); + fprintf(stderr, " Please select the window for x11vnc to poll\n"); + fprintf(stderr, " by clicking the mouse in that window.\n"); + fprintf(stderr, "\n"); + + while (msec * n++ < 1000 * secmax) { + unsigned long tmp; + char *q; + fd_set set; + struct timeval tv; + + if (screen && screen->clientHead) { + /* they may be doing the pointer-pick thru vnc: */ + tv.tv_sec = 0; + tv.tv_usec = msec * 1000; + FD_ZERO(&set); + FD_SET(fileno(p), &set); + if (select(fileno(p)+1, &set, NULL, NULL, &tv) == 0) { + /* note that rfbPE takes about 30ms too */ + rfbPE(screen, -1); + continue; + } + } + + if (fgets(line, 512, p) == NULL) { + break; + } + q = strstr(line, " id: 0x"); + if (q) { + q += 5; + if (sscanf(q, "0x%lx ", &tmp) == 1) { + ok = 1; + *num = tmp; + fprintf(stderr, " Picked: 0x%lx\n\n", tmp); + break; + } + } + } + pclose(p); + return ok; +} + +char *bitprint(unsigned int st, int nbits) { + static char str[33]; + int i, mask; + if (nbits > 32) { + nbits = 32; + } + for (i=0; i<nbits; i++) { + str[i] = '0'; + } + str[nbits] = '\0'; + mask = 1; + for (i=nbits-1; i>=0; i--) { + if (st & mask) { + str[i] = '1'; + } + mask = mask << 1; + } + return str; /* take care to use or copy immediately */ +} + +/* + * Simple utility to map host name to dotted IP address. Ignores aliases. + * Up to caller to free returned string. + */ +char *host2ip(char *host) { + struct hostent *hp; + struct sockaddr_in addr; + char *str; + + hp = gethostbyname(host); + if (!hp) { + return NULL; + } + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = *(unsigned long *)hp->h_addr; + str = strdup(inet_ntoa(addr.sin_addr)); + return str; +} + +int dotted_ip(char *host) { + char *p = host; + while (*p != '\0') { + if (*p == '.' || isdigit(*p)) { + p++; + continue; + } + return 0; + } + return 1; +} + +int get_remote_port(int sock) { + struct sockaddr_in saddr; + int saddr_len, saddr_port; + + saddr_len = sizeof(saddr); + memset(&saddr, 0, sizeof(saddr)); + saddr_port = -1; + if (!getpeername(sock, (struct sockaddr *)&saddr, &saddr_len)) { + saddr_port = ntohs(saddr.sin_port); + } + return saddr_port; +} + +char *get_remote_host(int sock) { + struct sockaddr_in saddr; + int saddr_len, saddr_port; + char *saddr_ip_str = NULL; + + saddr_len = sizeof(saddr); + memset(&saddr, 0, sizeof(saddr)); + saddr_port = -1; + if (!getsockname(sock, (struct sockaddr *)&saddr, &saddr_len)) { +#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H + saddr_ip_str = inet_ntoa(saddr.sin_addr); +#endif + } + if (! saddr_ip_str) { + saddr_ip_str = strdup("unknown"); + } + return saddr_ip_str; +} + +int get_local_port(int sock) { + struct sockaddr_in saddr; + int saddr_len, saddr_port; + + saddr_len = sizeof(saddr); + memset(&saddr, 0, sizeof(saddr)); + saddr_port = -1; + if (!getsockname(sock, (struct sockaddr *)&saddr, &saddr_len)) { + saddr_port = ntohs(saddr.sin_port); + } + return saddr_port; +} + +char *get_local_host(int sock) { + struct sockaddr_in saddr; + int saddr_len, saddr_port; + char *saddr_ip_str = NULL; + + saddr_len = sizeof(saddr); + memset(&saddr, 0, sizeof(saddr)); + saddr_port = -1; + if (!getsockname(sock, (struct sockaddr *)&saddr, &saddr_len)) { +#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H + saddr_ip_str = inet_ntoa(saddr.sin_addr); +#endif + } + if (! saddr_ip_str) { + saddr_ip_str = strdup("unknown"); + } + return saddr_ip_str; +} + +/* + * used in rfbGetScreen and rfbNewFramebuffer: and estimate to the number + * of bits per color, of course for some visuals, e.g. 565, the number + * is not the same for each color. This is just a sane default. + */ +int guess_bits_per_color(int bits_per_pixel) { + int bits_per_color; + + /* first guess, spread them "evenly" over R, G, and B */ + bits_per_color = bits_per_pixel/3; + if (bits_per_color < 1) { + bits_per_color = 1; /* 1bpp, 2bpp... */ + } + + /* choose safe values for usual cases: */ + if (bits_per_pixel == 8) { + bits_per_color = 2; + } else if (bits_per_pixel == 15 || bits_per_pixel == 16) { + bits_per_color = 5; + } else if (bits_per_pixel == 24 || bits_per_pixel == 32) { + bits_per_color = 8; + } + return bits_per_color; +} + +/* count number of clients supporting NewFBSize */ +int new_fb_size_clients(rfbScreenInfoPtr s) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int count = 0; + + iter = rfbGetClientIterator(s); + while( (cl = rfbClientIteratorNext(iter)) ) { + if (cl->useNewFBSize) { + count++; + } + } + rfbReleaseClientIterator(iter); + return count; +} + /* * Kludge to interpose image gets and limit to a subset rectangle of * the rootwin. This is the -sid option trying to work around invisible @@ -537,6 +929,8 @@ int rootshift = 0; y += off_y; \ } +/* -- ximage.c -- */ + /* * Wrappers for Image related X calls */ @@ -545,7 +939,7 @@ Status XShmGetImage_wr(Display *disp, Drawable d, XImage *image, int x, int y, ADJUST_ROOTSHIFT - /* The Solaris overlay stuff is all non-shm (using_shm = 0) */ + /* Note: the Solaris overlay stuff is all non-shm (using_shm = 0) */ #ifdef LIBVNCSERVER_HAVE_XSHM return XShmGetImage(disp, d, image, x, y, mask); @@ -590,18 +984,39 @@ Bool XShmQueryExtension_wr(Display *disp) { #endif } +/* wrapper for overlay screen reading: */ + +XImage *xreadscreen(Display *disp, Drawable d, int x, int y, + unsigned int width, unsigned int height, Bool show_cursor) { +#ifdef SOLARIS_OVERLAY + return XReadScreen(disp, d, x, y, width, height, + show_cursor); +#endif +#ifdef IRIX_OVERLAY + { unsigned long hints = 0, hints_ret; + if (show_cursor) hints |= XRD_READ_POINTER; + return XReadDisplay(disp, d, x, y, width, height, + hints, &hints_ret); + } +#endif + return NULL; +} + XImage *XGetSubImage_wr(Display *disp, Drawable d, int x, int y, unsigned int width, unsigned int height, unsigned long plane_mask, int format, XImage *dest_image, int dest_x, int dest_y) { ADJUST_ROOTSHIFT -#ifdef SOLARIS if (overlay && dest_x == 0 && dest_y == 0) { size_t size = dest_image->height * dest_image->bytes_per_line; - XImage *xi = XReadScreen(disp, d, x, y, width, height, + XImage *xi; + + xi = xreadscreen(disp, d, x, y, width, height, (Bool) overlay_cursor); + if (! xi) return NULL; + /* * There is extra overhead from memcpy and free... * this is not like the real XGetSubImage(). We hope @@ -613,7 +1028,6 @@ XImage *XGetSubImage_wr(Display *disp, Drawable d, int x, int y, XDestroyImage(xi); return (dest_image); } -#endif return XGetSubImage(disp, d, x, y, width, height, plane_mask, format, dest_image, dest_x, dest_y); } @@ -624,28 +1038,26 @@ XImage *XGetImage_wr(Display *disp, Drawable d, int x, int y, ADJUST_ROOTSHIFT -#ifdef SOLARIS if (overlay) { - return XReadScreen(disp, d, x, y, width, height, + return xreadscreen(disp, d, x, y, width, height, (Bool) overlay_cursor); } -#endif return XGetImage(disp, d, x, y, width, height, plane_mask, format); } XImage *XCreateImage_wr(Display *disp, Visual *visual, unsigned int depth, int format, int offset, char *data, unsigned int width, unsigned int height, int bitmap_pad, int bytes_per_line) { -/* - * This is a kludge to get a created XImage to exactly match what - * XReadScreen returns: we noticed the rgb masks are different from - * XCreateImage with the high color visual (red mask <-> blue mask). - * Note we read from the root window(!) then free the data. - */ -#ifdef SOLARIS + /* + * This is a kludge to get a created XImage to exactly match what + * XReadScreen returns: we noticed the rgb masks are different + * from XCreateImage with the high color visual (red mask <-> + * blue mask). Note we read from the root window(!) then free + * the data. + */ if (overlay) { XImage *xi; - xi = XReadScreen(disp, window, 0, 0, width, height, False); + xi = xreadscreen(disp, window, 0, 0, width, height, False); if (xi == NULL) { return xi; } @@ -655,7 +1067,6 @@ XImage *XCreateImage_wr(Display *disp, Visual *visual, unsigned int depth, xi->data = data; return xi; } -#endif return XCreateImage(disp, visual, depth, format, offset, data, width, height, bitmap_pad, bytes_per_line); @@ -764,23 +1175,53 @@ void XTestDiscard_wr(Display *dpy) { static int exit_flag = 0; int exit_sig = 0; +void clean_shm(int quick) { + int i, cnt = 0; + + /* + * to avoid deadlock, etc, under quick=1 we just delete the shm + * areas and leave the X stuff hanging. + */ + if (quick) { + shm_delete(&scanline_shm); + shm_delete(&fullscreen_shm); + } else { + shm_clean(&scanline_shm, scanline); + shm_clean(&fullscreen_shm, fullscreen); + } + + /* + * Here we have to clean up quite a few shm areas for all + * the possible tile row runs (40 for 1280), not as robust + * as one might like... sometimes need to run ipcrm(1). + */ + for(i=1; i<=ntiles_x; i++) { + if (i > tile_shm_count) { + break; + } + if (quick) { + shm_delete(&tile_row_shm[i]); + } else { + shm_clean(&tile_row_shm[i], tile_row[i]); + } + cnt++; + if (single_copytile_count && i >= single_copytile_count) { + break; + } + } + if (!quiet && !quick) { + rfbLog("deleted %d tile_row polling images.\n", cnt); + } +} + /* * Normal exiting */ void clean_up_exit (int ret) { - int i; exit_flag = 1; /* remove the shm areas: */ - shm_clean(&scanline_shm, scanline); - shm_clean(&fullscreen_shm, fullscreen); - - for(i=1; i<=ntiles_x; i++) { - shm_clean(&tile_row_shm[i], tile_row[i]); - if (single_copytile && i >= single_copytile) { - break; - } - } + clean_shm(0); /* X keyboard cleanups */ delete_added_keycodes(); @@ -807,7 +1248,6 @@ void clean_up_exit (int ret) { * General problem handler */ static void interrupted (int sig) { - int i; exit_sig = sig; if (exit_flag) { exit_flag++; @@ -828,24 +1268,11 @@ static void interrupted (int sig) { shut_down = 1; return; } - /* - * to avoid deadlock, etc, just delete the shm areas and - * leave the X stuff hanging. - */ - shm_delete(&scanline_shm); - shm_delete(&fullscreen_shm); - /* - * Here we have to clean up quite a few shm areas for all - * the possible tile row runs (40 for 1280), not as robust - * as one might like... sometimes need to run ipcrm(1). - */ - for(i=1; i<=ntiles_x; i++) { - shm_delete(&tile_row_shm[i]); - if (single_copytile && i >= single_copytile) { - break; - } - } + X_UNLOCK; + + /* remove the shm areas with quick=1: */ + clean_shm(1); /* X keyboard cleanups */ delete_added_keycodes(); @@ -858,6 +1285,7 @@ static void interrupted (int sig) { if (no_autorepeat) { autorepeat(1); } + if (sig) { exit(2); } @@ -867,10 +1295,19 @@ static void interrupted (int sig) { static XErrorHandler Xerror_def; static XIOErrorHandler XIOerr_def; +XErrorEvent *trapped_xerror_event; int trapped_xerror = 0; +int trapped_getimage_xerror = 0; int trap_xerror(Display *d, XErrorEvent *error) { trapped_xerror = 1; + trapped_xerror_event = error; + return 0; +} + +int trap_getimage_xerror(Display *d, XErrorEvent *error) { + trapped_getimage_xerror = 1; + trapped_xerror_event = error; return 0; } @@ -886,6 +1323,74 @@ static int XIOerr(Display *d) { return (*XIOerr_def)(d); } +char *xerrors[] = { + "Success", + "BadRequest", + "BadValue", + "BadWindow", + "BadPixmap", + "BadAtom", + "BadCursor", + "BadFont", + "BadMatch", + "BadDrawable", + "BadAccess", + "BadAlloc", + "BadColor", + "BadGC", + "BadIDChoice", + "BadName", + "BadLength", + "BadImplementation", + "unknown" +}; +int xerrors_max = BadImplementation; + +char *xerror_string(XErrorEvent *error) { + int index = -1; + if (error) { + index = (int) error->error_code; + } + if (0 <= index && index <= xerrors_max) { + return xerrors[index]; + } else { + return xerrors[xerrors_max+1]; + } +} + +/* trapping utility to check for a valid window: */ +int valid_window(Window win) { + XErrorHandler old_handler; + XWindowAttributes attr; + int ok = 0; + + trapped_xerror = 0; + old_handler = XSetErrorHandler(trap_xerror); + if (XGetWindowAttributes(dpy, win, &attr)) { + ok = 1; + } + if (trapped_xerror && trapped_xerror_event && ! quiet) { + rfbLog("trapped XError: %s (0x%lx)\n", + xerror_string(trapped_xerror_event), win); + } + XSetErrorHandler(old_handler); + trapped_xerror = 0; + + return ok; +} + +int get_window_size(Window win, int *x, int *y) { + XWindowAttributes attr; + /* valid_window? */ + if (XGetWindowAttributes(dpy, win, &attr)) { + *x = attr.width; + *y = attr.height; + return 1; + } else { + return 0; + } +} + /* signal handlers */ void initialize_signals(void) { signal(SIGHUP, interrupted); @@ -897,11 +1402,13 @@ void initialize_signals(void) { signal(SIGSEGV, interrupted); signal(SIGFPE, interrupted); - if (sigpipe == 1) { + if (!sigpipe || *sigpipe == '\0' || !strcmp(sigpipe, "skip")) { + ; + } else if (!strcmp(sigpipe, "ignore")) { #ifdef SIG_IGN signal(SIGPIPE, SIG_IGN); #endif - } else if (sigpipe == 2) { + } else if (!strcmp(sigpipe, "exit")) { rfbLog("initialize_signals: will exit on SIGPIPE\n"); signal(SIGPIPE, interrupted); } @@ -919,6 +1426,7 @@ void initialize_signals(void) { static int accepted_client = 0; static int client_count = 0; +static int clients_served = 0; /* * check that all clients are in RFB_NORMAL state @@ -940,11 +1448,110 @@ int all_clients_initialized(void) { return ok; } +char *list_clients(void) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + char *list, tmp[32]; + int count = 0; + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + count++; + } + rfbReleaseClientIterator(iter); + + list = (char *) malloc((count+1)*100); + + list[0] = '\0'; + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + ClientData *cd = (ClientData *) cl->clientData; + if (*list != '\0') { + strcat(list, ","); + } + strcat(list, cl->host); + sprintf(tmp, ":%d/0x%x", get_remote_port(cl->sock), cd->uid); + strcat(list, tmp); + if (cl->viewOnly) { + strcat(list, "-ro"); + } else { + strcat(list, "-rw"); + } + } + rfbReleaseClientIterator(iter); + return list; +} + +void close_all_clients(void) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + rfbCloseClient(cl); + rfbClientConnectionGone(cl); + } + rfbReleaseClientIterator(iter); +} + +void close_clients(char *str) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int host_warn = 0, hex_warn = 0; + + if (!strcmp(str, "all") || !strcmp(str, "*")) { + close_all_clients(); + return; + } + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + if (strstr(str, "0x")) { + int id; + ClientData *cd = (ClientData *) cl->clientData; + if (sscanf(str, "0x%x", &id) != 1) { + if (hex_warn++) { + continue; + } + rfbLog("skipping bad client hex id: %s\n", str); + continue; + } + if ( cd->uid == id) { + rfbCloseClient(cl); + rfbClientConnectionGone(cl); + } + } else { + char *rstr = str; + if (! dotted_ip(str)) { + rstr = host2ip(str); + if (rstr == NULL) { + if (host_warn++) { + continue; + } + rfbLog("skipping bad lookup: \"%s\"\n", + str); + continue; + } + rfbLog("lookup: %s -> %s\n", str, rstr); + } + if (!strcmp(rstr, cl->host)) { + rfbCloseClient(cl); + rfbClientConnectionGone(cl); + } + if (rstr != str) { + free(rstr); + } + } + } + rfbReleaseClientIterator(iter); +} + /* * utility to run a user supplied command setting some RFB_ env vars. * used by, e.g., accept_client() and client_gone() */ -static int run_user_command(char *cmd, rfbClientPtr client) { +static int run_user_command(char *cmd, rfbClientPtr client, char *mode) { char *dpystr = DisplayString(dpy); static char *display_env = NULL; static char env_rfb_client_id[100]; @@ -954,18 +1561,24 @@ static int run_user_command(char *cmd, rfbClientPtr client) { static char env_rfb_server_port[100]; static char env_rfb_x11vnc_pid[100]; static char env_rfb_client_count[100]; + static char env_rfb_mode[100]; char *addr = client->host; - int rc; - char *saddr_ip_str = NULL; - int saddr_len, saddr_port; - struct sockaddr_in saddr; + int rc, hport; + char *ip_str = NULL; + ClientData *cd = (ClientData *) client->clientData; if (addr == NULL || addr[0] == '\0') { addr = "unknown-host"; } /* set RFB_CLIENT_ID to semi unique id for command to use */ - sprintf(env_rfb_client_id, "RFB_CLIENT_ID=%p", (void *) client); + if (cd && cd->uid) { + sprintf(env_rfb_client_id, "RFB_CLIENT_ID=0x%x", cd->uid); + } else { + /* not accepted yet: */ + sprintf(env_rfb_client_id, "RFB_CLIENT_ID=0x%x", + clients_served); + } putenv(env_rfb_client_id); /* set RFB_CLIENT_IP to IP addr for command to use */ @@ -977,34 +1590,25 @@ static int run_user_command(char *cmd, rfbClientPtr client) { putenv(env_rfb_x11vnc_pid); /* set RFB_CLIENT_PORT to peer port for command to use */ - saddr_len = sizeof(saddr); - memset(&saddr, 0, sizeof(saddr)); - saddr_port = -1; - if (!getpeername(client->sock, (struct sockaddr *)&saddr, &saddr_len)) { - saddr_port = ntohs(saddr.sin_port); - } - sprintf(env_rfb_client_port, "RFB_CLIENT_PORT=%d", saddr_port); + hport = get_remote_port(client->sock); + sprintf(env_rfb_client_port, "RFB_CLIENT_PORT=%d", hport); putenv(env_rfb_client_port); + sprintf(env_rfb_mode, "RFB_MODE=%s", mode); + putenv(env_rfb_mode); + /* * now do RFB_SERVER_IP and RFB_SERVER_PORT (i.e. us!) * This will establish a 5-tuple (including tcp) the external * program can potentially use to work out the virtual circuit * for this connection. */ - saddr_len = sizeof(saddr); - memset(&saddr, 0, sizeof(saddr)); - saddr_port = -1; - saddr_ip_str = "unknown"; - if (!getsockname(client->sock, (struct sockaddr *)&saddr, &saddr_len)) { - saddr_port = ntohs(saddr.sin_port); -#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H - saddr_ip_str = inet_ntoa(saddr.sin_addr); -#endif - } - sprintf(env_rfb_server_ip, "RFB_SERVER_IP=%s", saddr_ip_str); + ip_str = get_local_host(client->sock); + hport = get_local_port(client->sock); + + sprintf(env_rfb_server_ip, "RFB_SERVER_IP=%s", ip_str); putenv(env_rfb_server_ip); - sprintf(env_rfb_server_port, "RFB_SERVER_PORT=%d", saddr_port); + sprintf(env_rfb_server_port, "RFB_SERVER_PORT=%d", hport); putenv(env_rfb_server_port); @@ -1039,7 +1643,6 @@ static int run_user_command(char *cmd, rfbClientPtr client) { return rc; } - /* * callback for when a client disconnects */ @@ -1048,12 +1651,16 @@ static void client_gone(rfbClientPtr client) { client_count--; rfbLog("client_count: %d\n", client_count); + if (client->clientData) { + free(client->clientData); + } + if (no_autorepeat && client_count == 0) { autorepeat(1); } - if (gone_cmd) { + if (gone_cmd && *gone_cmd != '\0') { rfbLog("client_gone: using cmd for: %s\n", client->host); - run_user_command(gone_cmd, client); + run_user_command(gone_cmd, client, "gone"); } if (inetd) { @@ -1074,6 +1681,11 @@ static void client_gone(rfbClientPtr client) { accepted_client = 0; return; } + if (shared && client_count > 0) { + rfbLog("connect_once: other shared clients still " + "connected, not exiting.\n"); + return; + } rfbLog("viewer exited.\n"); clean_up_exit(0); @@ -1088,8 +1700,19 @@ static int check_access(char *addr) { int allowed = 0; char *p, *list; + if (deny_all) { + rfbLog("check_access: new connections are currently " + "blocked.\n"); + return 0; + } + if (allow_list == NULL || *allow_list == '\0') { - return 1; + if (allow_once == NULL) { + return 1; + } + } + if (allow_list == NULL) { + allow_list = strdup(""); } if (addr == NULL || *addr == '\0') { rfbLog("check_access: denying empty host IP address string.\n"); @@ -1110,6 +1733,9 @@ static int check_access(char *addr) { clean_up_exit(1); } len = sbuf.st_size + 1; /* 1 more for '\0' at end */ + if (allow_once) { + len += strlen(allow_once) + 2; + } list = malloc(len); list[0] = '\0'; @@ -1129,17 +1755,46 @@ static int check_access(char *addr) { strcat(list, line); } fclose(in); + if (allow_once) { + strcat(list, allow_once); + strcat(list, "\n"); + } } else { - list = strdup(allow_list); + int len = strlen(allow_list); + if (allow_once) { + len += strlen(allow_once) + 2; + } + list = malloc(len); + list[0] = '\0'; + strcat(list, allow_list); + if (allow_once) { + strcat(list, ","); + strcat(list, allow_once); + } } + if (allow_once) { + free(allow_once); + allow_once = NULL; + } p = strtok(list, ", \t\n\r"); while (p) { - char *q; + char *q, *r = NULL; if (*p == '\0') { + p = strtok(NULL, ", \t\n\r"); continue; } + if (! dotted_ip(p)) { + r = host2ip(p); + if (r == NULL) { + rfbLog("check_access: bad lookup \"%s\"\n", p); + p = strtok(NULL, ", \t\n\r"); + continue; + } + rfbLog("check_access: lookup %s -> %s\n", p, r); + p = r; + } q = strstr(addr, p); if (q == addr) { rfbLog("check_access: client %s matches pattern %s\n", @@ -1150,6 +1805,12 @@ static int check_access(char *addr) { allowed = 1; } p = strtok(NULL, ", \t\n\r"); + if (r) { + free(r); + } + if (allowed) { + break; + } } free(list); return allowed; @@ -1157,7 +1818,7 @@ static int check_access(char *addr) { /* * x11vnc's first (and only) visible widget: accept/reject dialog window. - * We go through this pain to avoid dependency on libXt. + * We go through this pain to avoid dependency on libXt... */ static int ugly_accept_window(char *addr, int X, int Y, int timeout, char *mode) { @@ -1364,8 +2025,8 @@ static char t2x2_bits[] = { strlen(str_v)); tw = (Vi_w - tw)/2; if (tw < 0) tw = 1; - XDrawString(dpy, awin, gc, Vi_x+tw, Vi_y+Vi_h-5, - str_v, strlen(str_v)); + XDrawString(dpy, awin, gc, Vi_x+tw, + Vi_y+Vi_h-5, str_v, strlen(str_v)); } break; @@ -1529,7 +2190,7 @@ static int accept_client(rfbClientPtr client) { char *addr = client->host; char *action = NULL; - if (accept_cmd == NULL) { + if (accept_cmd == NULL || *accept_cmd == '\0') { return 1; /* no command specified, so we accept */ } @@ -1637,7 +2298,7 @@ static int accept_client(rfbClientPtr client) { int rc; rfbLog("accept_client: using cmd for: %s\n", addr); - rc = run_user_command(cmd, client); + rc = run_user_command(cmd, client, "accept"); if (action) { int result; @@ -1689,6 +2350,9 @@ static void check_connect_file(char *file) { static time_t last_time = 0; time_t now = time(0); + if (last_time == 0) { + last_time = now; + } if (now - last_time < 1) { /* check only once a second */ return; @@ -1717,8 +2381,9 @@ static void check_connect_file(char *file) { if (fgets(line, 1024, in) != NULL) { if (sscanf(line, "%s", host) == 1) { if (strlen(host) > 0) { - client_connect = strdup(host); + char *str = strdup(host); rfbLog("read connect file: %s\n", host); + client_connect = str; } } } @@ -1836,6 +2501,11 @@ static void reverse_connect(char *str) { * Routines for monitoring the VNC_CONNECT property for changes. * The vncconnect(1) will set it on our X display. */ +void set_vnc_connect_prop(char *str) { + XChangeProperty(dpy, rootwin, vnc_connect_prop, XA_STRING, 8, + PropModeReplace, (unsigned char *)str, strlen(str)); +} + void read_vnc_connect_prop(void) { Atom type; int format, slen, dlen; @@ -1873,7 +2543,15 @@ void read_vnc_connect_prop(void) { } while (bytes_after > 0); vnc_connect_str[VNC_CONNECT_MAX] = '\0'; - rfbLog("read property VNC_CONNECT: %s\n", vnc_connect_str); + if (strlen(vnc_connect_str) > 100) { + char trim[101]; + trim[0] = '\0'; + strncat(trim, vnc_connect_str, 100); + rfbLog("read X property VNC_CONNECT: %s ...\n", trim); + + } else { + rfbLog("read X property VNC_CONNECT: %s\n", vnc_connect_str); + } } /* @@ -1881,7 +2559,14 @@ void read_vnc_connect_prop(void) { */ static void send_client_connect(void) { if (client_connect != NULL) { - reverse_connect(client_connect); + char *str = client_connect; + if (strstr(str, "cmd=") == str || strstr(str, "qry=") == str) { + process_remote_cmd(client_connect, 0); + } else if (strstr(str, "ans=") || strstr(str, "aro=") == str) { + ; + } else { + reverse_connect(client_connect); + } free(client_connect); client_connect = NULL; } @@ -1913,6 +2598,7 @@ void check_connect_inputs(void) { * libvncserver callback for when a new client connects */ enum rfbNewClientAction new_client(rfbClientPtr client) { + ClientData *cd; last_event = last_input = time(0); if (inetd) { @@ -1923,6 +2609,8 @@ enum rfbNewClientAction new_client(rfbClientPtr client) { client->clientGoneHook = client_gone; } + clients_served++; + if (connect_once) { if (screen->dontDisconnect && screen->neverShared) { if (! shared && accepted_client) { @@ -1946,11 +2634,12 @@ enum rfbNewClientAction new_client(rfbClientPtr client) { } if (view_only) { - client->clientData = (void *) -1; client->viewOnly = TRUE; - } else { - client->clientData = (void *) 0; } + client->clientData = (void *) calloc(sizeof(ClientData), 1); + cd = (ClientData *) client->clientData; + + cd->uid = clients_served; client->clientGoneHook = client_gone; client_count++; @@ -1960,6 +2649,10 @@ enum rfbNewClientAction new_client(rfbClientPtr client) { autorepeat(0); } + if (pad_geometry) { + install_padded_fb(pad_geometry); + } + accepted_client = 1; last_client = time(0); @@ -2253,6 +2946,21 @@ void initialize_remap(char *infile) { KeySym ksym1, ksym2; keyremap_t *remap, *current; + if (keyremaps != NULL) { + /* free last remapping */ + keyremap_t *next_remap, *curr_remap = keyremaps; + while (curr_remap != NULL) { + next_remap = curr_remap->next; + free(curr_remap); + curr_remap = next_remap; + } + keyremaps = NULL; + } + if (infile == NULL || *infile == '\0') { + /* just unset remapping */ + return; + } + in = fopen(infile, "r"); if (in == NULL) { /* assume cmd line key1-key2,key3-key4 */ @@ -2581,11 +3289,11 @@ xkbmodifiers[] For the KeySym bound to this (keycode,group,level) store if (debug_keyboard > 1) { fprintf(stderr, " %03d G%d L%d mod=%s ", - kc, grp+1, lvl+1, bitprint(ms)); + kc, grp+1, lvl+1, bitprint(ms, 8)); fprintf(stderr, "state=%s ", - bitprint(xkbstate[kc][grp][lvl])); + bitprint(xkbstate[kc][grp][lvl], 8)); fprintf(stderr, "ignore=%s ", - bitprint(xkbignore[kc][grp][lvl])); + bitprint(xkbignore[kc][grp][lvl], 8)); fprintf(stderr, " ks=0x%08lx \"%s\"\n", ks, XKeysymToString(ks)); } @@ -2688,9 +3396,9 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, XKeysymToString(keysym), XKeysymToString( XKeycodeToKeysym(dpy, kc, 0))); fprintf(stderr, " need state: %s\n", - bitprint(state)); + bitprint(state, 8)); fprintf(stderr, " ignorable : %s\n", - bitprint(xkbignore[kc][grp][lvl])); + bitprint(xkbignore[kc][grp][lvl], 8)); } /* save it if state is OK and not told to skip */ @@ -2719,11 +3427,11 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, #define PKBSTATE \ fprintf(stderr, " --- current mod state:\n"); \ - fprintf(stderr, " mods : %s\n", bitprint(kbstate.mods)); \ - fprintf(stderr, " base_mods : %s\n", bitprint(kbstate.base_mods)); \ - fprintf(stderr, " latch_mods: %s\n", bitprint(kbstate.latched_mods)); \ - fprintf(stderr, " lock_mods : %s\n", bitprint(kbstate.locked_mods)); \ - fprintf(stderr, " compat : %s\n", bitprint(kbstate.compat_state)); + fprintf(stderr, " mods : %s\n", bitprint(kbstate.mods, 8)); \ + fprintf(stderr, " base_mods : %s\n", bitprint(kbstate.base_mods, 8)); \ + fprintf(stderr, " latch_mods: %s\n", bitprint(kbstate.latched_mods, 8)); \ + fprintf(stderr, " lock_mods : %s\n", bitprint(kbstate.locked_mods, 8)); \ + fprintf(stderr, " compat : %s\n", bitprint(kbstate.compat_state, 8)); /* * Now get the current state of the keyboard from the X server. @@ -2902,7 +3610,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, if (debug_keyboard > 1) { fprintf(stderr, " +++ needmods: mod=%d %s " - "need it to be: %d %s\n", i, bitprint(b), + "need it to be: %d %s\n", i, bitprint(b, 8), needmods[i], needmods[i] ? "down" : "up"); } @@ -2958,7 +3666,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, fprintf(stderr, " === for " "mod=%s found kc=%03d/G%d" "/L%d it is %d %s skip=%d " - "(%s)\n", bitprint(b), kc, + "(%s)\n", bitprint(b,8), kc, grp+1, lvl+1, kt, kt ? "down" : "up ", skip, str ? str : "null"); @@ -3104,21 +3812,41 @@ static char modifiers[0x100]; static KeyCode keycodes[0x100]; static KeyCode left_shift_code, right_shift_code, altgr_code, iso_level3_code; -char *bitprint(unsigned int st) { - static char str[9]; - int i, mask; - for (i=0; i<8; i++) { - str[i] = '0'; +/* workaround for X11R5, Latin 1 only */ +#ifndef XConvertCase +#define XConvertCase(sym, lower, upper) \ +*(lower) = sym; \ +*(upper) = sym; \ +if (sym >> 8 == 0) { \ + 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); \ +} +#endif + +void initialize_keyboard_and_pointer(void) { + if (use_modifier_tweak) { + initialize_modtweak(); } - str[8] = '\0'; - mask = 1; - for (i=7; i>=0; i--) { - if (st & mask) { - str[i] = '1'; - } - mask = mask << 1; + if (remap_file != NULL) { + initialize_remap(remap_file); + } + + initialize_pointer_map(pointer_remap); + + clear_modifiers(1); + if (clear_mods == 1) { + clear_modifiers(0); } - return str; } void initialize_modtweak(void) { @@ -3278,7 +4006,7 @@ static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, if (view_only) { return; } - if (client->viewOnly) { + if (client && client->viewOnly) { return; } @@ -3348,7 +4076,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { if (view_only) { return; } - if (client->viewOnly) { + if (client && client->viewOnly) { return; } @@ -3641,7 +4369,7 @@ void initialize_pointer_map(char *pointer_remap) { pointer_map[i][0].up = 0; } - if (pointer_remap) { + if (pointer_remap && *pointer_remap != '\0') { /* -buttonmap, format is like: 12-21=2 */ char *p, *q, *remap = pointer_remap; int n; @@ -3843,7 +4571,7 @@ void pointer(int mask, int x, int y, rfbClientPtr client) { * See check_user_input() for the more complicated things we do * in the non-threaded case. */ - if (use_threads && old_pointer != 1) { + if (use_threads && pointer_mode != 1) { # define NEV 32 /* storage for the event queue */ static int mutex_init = 0; @@ -3940,22 +4668,24 @@ void initialize_xkb(void) { int ir, reason; int op, ev, er, maj, min; - if (! use_xkb) { - return; - } if (! XkbQueryExtension(dpy, &op, &ev, &er, &maj, &min)) { - if (! quiet) { - fprintf(stderr, "warning: XKEYBOARD" - " extension not present.\n"); + if (! quiet && use_xkb) { + rfbLog("warning: XKEYBOARD extension not present.\n"); } + xkb_present = 0; use_xkb = 0; return; + } else { + xkb_present = 1; + } + if (! use_xkb) { + return; } if (! XkbOpenDisplay(DisplayString(dpy), &xkb_base_event_type, &ir, NULL, NULL, &reason) ) { if (! quiet) { - fprintf(stderr, "warning: disabling XKEYBOARD." - " XkbOpenDisplay failed.\n"); + rfbLog("warning: disabling XKEYBOARD. XkbOpenDisplay" + " failed.\n"); } use_xkb = 0; } @@ -3964,8 +4694,8 @@ void initialize_xkb(void) { void initialize_watch_bell(void) { if (! use_xkb) { if (! quiet) { - fprintf(stderr, "warning: disabling bell." - " XKEYBOARD ext. not present.\n"); + rfbLog("warning: disabling bell. XKEYBOARD ext. " + "not present.\n"); } watch_bell = 0; return; @@ -3973,8 +4703,8 @@ void initialize_watch_bell(void) { if (! XkbSelectEvents(dpy, XkbUseCoreKbd, XkbBellNotifyMask, XkbBellNotifyMask) ) { if (! quiet) { - fprintf(stderr, "warning: disabling bell." - " XkbSelectEvents failed.\n"); + rfbLog("warning: disabling bell. XkbSelectEvents" + " failed.\n"); } watch_bell = 0; } @@ -3994,7 +4724,7 @@ void check_bell_event(void) { } X_LOCK; - if (! XCheckTypedEvent(dpy, xkb_base_event_type , &xev)) { + if (! XCheckTypedEvent(dpy, xkb_base_event_type, &xev)) { X_UNLOCK; return; } @@ -4004,7 +4734,7 @@ void check_bell_event(void) { } X_UNLOCK; - if (got_bell) { + if (got_bell && sound_bell) { if (! all_clients_initialized()) { rfbLog("check_bell_event: not sending bell: " "uninitialized clients\n"); @@ -4017,6 +4747,260 @@ void check_bell_event(void) { void check_bell_event(void) {} #endif +/* -- xrandr.h -- */ + +time_t last_subwin_trap = 0; +int subwin_trap_count = 0; + +XErrorHandler old_getimage_handler; +#define XRANDR_SET_TRAP_RET(x,y) \ + if (subwin || xrandr) { \ + if (0) fprintf(stderr, " SET_TRAP: '%d' '%s'\n", x, y); \ + trapped_getimage_xerror = 0; \ + old_getimage_handler = XSetErrorHandler(trap_getimage_xerror); \ + if (check_xrandr_event(y)) { \ + trapped_getimage_xerror = 0; \ + XSetErrorHandler(old_getimage_handler); \ + return(x); \ + } \ + } +#define XRANDR_CHK_TRAP_RET(x,y) \ + if (subwin || xrandr) { \ + if (0) fprintf(stderr, " CHK_TRAP: '%d' '%s'\n", x, y); \ + if (trapped_getimage_xerror) { \ + if (subwin) { \ + static int last = 0; \ + subwin_trap_count++; \ + if (time(0) > last_subwin_trap + 60) { \ + rfbLog("trapped GetImage xerror" \ + " in SUBWIN mode. [%d]\n", \ + subwin_trap_count); \ + last_subwin_trap = time(0); \ + last = subwin_trap_count; \ + } \ + if (subwin_trap_count - last > 30) { \ + /* window probably iconified */ \ + usleep(1000*1000); \ + } \ + } else { \ + rfbLog("trapped GetImage xerror" \ + " in XRANDR mode.\n"); \ + } \ + trapped_getimage_xerror = 0; \ + XSetErrorHandler(old_getimage_handler); \ + check_xrandr_event(y); \ + X_UNLOCK; \ + return(x); \ + } \ + } + +/* -- xrandr.c -- */ + +void initialize_xrandr(void) { +#ifdef LIBVNCSERVER_HAVE_LIBXRANDR + if (xrandr_present) { + Rotation rot; + + xrandr_width = XDisplayWidth(dpy, scr); + xrandr_height = XDisplayHeight(dpy, scr); + XRRRotations(dpy, scr, &rot); + xrandr_rotation = (int) rot; + if (xrandr) { + XRRSelectInput(dpy, rootwin, RRScreenChangeNotifyMask); + } else { + XRRSelectInput(dpy, rootwin, 0); + } + } +#endif +} + +void handle_xrandr_change(int, int); + +int handle_subwin_resize(char *msg) { + int new_x, new_y; + int i, check = 10, ms = 250; /* 2.5 secs... */ + + if (0) fprintf(stderr, "IN handle_subwin_resize('%s')\n", msg); + if (! subwin) { + return 0; /* hmmm... */ + } + if (! valid_window(subwin)) { + rfbLog("subwin 0x%lx went away!\n", subwin); + X_UNLOCK; + clean_up_exit(1); + } + if (! get_window_size(subwin, &new_x, &new_y)) { + rfbLog("could not get size of subwin 0x%lx\n", subwin); + X_UNLOCK; + clean_up_exit(1); + } + if (dpy_x == new_x && dpy_y == new_y) { + /* no change */ + return 0; + } + + /* window may still be changing (e.g. drag resize) */ + for (i=0; i < check; i++) { + int newer_x, newer_y; + usleep(ms * 1000); + + if (! get_window_size(subwin, &newer_x, &newer_y)) { + rfbLog("could not get size of subwin 0x%lx\n", subwin); + clean_up_exit(1); + } + if (new_x == newer_x && new_y == newer_y) { + /* go for it... */ + break; + } else { + rfbLog("subwin 0x%lx still changing size...\n", subwin); + new_x = newer_x; + new_y = newer_y; + } + } + + rfbLog("subwin 0x%lx new size: x: %d -> %d, y: %d -> %d\n", + subwin, dpy_x, new_x, dpy_y, new_y); + rfbLog("calling handle_xrandr_change() for resizing\n"); + + X_UNLOCK; + handle_xrandr_change(new_x, new_y); + return 1; +} + +int known_xrandr_mode(char *); + +void handle_xrandr_change(int new_x, int new_y) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + + /* sanity check xrandr_mode */ + if (! xrandr_mode) { + xrandr_mode = strdup("default"); + } else if (! known_xrandr_mode(xrandr_mode)) { + free(xrandr_mode); + xrandr_mode = strdup("default"); + } + rfbLog("xrandr_mode: %s\n", xrandr_mode); + if (!strcmp(xrandr_mode, "exit")) { + close_all_clients(); + rfbLog(" shutting down due to XRANDR event.\n"); + clean_up_exit(0); + } + if (!strcmp(xrandr_mode, "newfbsize")) { + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + if (cl->useNewFBSize) { + continue; + } + rfbLog(" closing client %s (no useNewFBSize" + " support).\n", cl->host); + rfbCloseClient(cl); + rfbClientConnectionGone(cl); + } + rfbReleaseClientIterator(iter); + } + + /* default, resize, and newfbsize create a new fb: */ + rfbLog("check_xrandr_event: trying to create new framebuffer...\n"); + if (new_x < dpy_x || new_y < dpy_y) { + check_black_fb(); + } + do_new_fb(1); + rfbLog("check_xrandr_event: fb WxH: %dx%d\n", dpy_x, dpy_y); +} + +int check_xrandr_event(char *msg) { + XEvent xev; + if (subwin) { + return handle_subwin_resize(msg); + } +#ifdef LIBVNCSERVER_HAVE_LIBXRANDR + if (! xrandr || ! xrandr_present) { + return 0; + } + if (0) fprintf(stderr, "IN check_xrandr_event('%s')\n", msg); + if (XCheckTypedEvent(dpy, xrandr_base_event_type + + RRScreenChangeNotify, &xev)) { + int do_change; + XRRScreenChangeNotifyEvent *rev; + + rev = (XRRScreenChangeNotifyEvent *) &xev; + rfbLog("check_xrandr_event():\n"); + rfbLog("Detected XRANDR event at location '%s':\n", msg); + rfbLog(" serial: %d\n", (int) rev->serial); + rfbLog(" timestamp: %d\n", (int) rev->timestamp); + rfbLog(" cfg_timestamp: %d\n", (int) rev->config_timestamp); + rfbLog(" size_id: %d\n", (int) rev->size_index); + rfbLog(" sub_pixel: %d\n", (int) rev->subpixel_order); + rfbLog(" rotation: %d\n", (int) rev->rotation); + rfbLog(" width: %d\n", (int) rev->width); + rfbLog(" height: %d\n", (int) rev->height); + rfbLog(" mwidth: %d mm\n", (int) rev->mwidth); + rfbLog(" mheight: %d mm\n", (int) rev->mheight); + rfbLog("\n"); + rfbLog("check_xrandr_event: previous WxH: %dx%d\n", + dpy_x, dpy_y); + if (dpy_x == rev->width && dpy_y == rev->height && + xrandr_rotation == (int) rev->rotation) { + rfbLog("check_xrandr_event: no change detected.\n"); + do_change = 0; + } else { + do_change = 1; + } + + xrandr_width = rev->width; + xrandr_height = rev->height; + xrandr_timestamp = rev->timestamp; + xrandr_cfg_time = rev->config_timestamp; + xrandr_rotation = (int) rev->rotation; + + rfbLog("check_xrandr_event: updating config...\n"); + XRRUpdateConfiguration(&xev); + + if (do_change) { + X_UNLOCK; + handle_xrandr_change(rev->width, rev->height); + } + rfbLog("check_xrandr_event: current WxH: %dx%d\n", + XDisplayWidth(dpy, scr), XDisplayHeight(dpy, scr)); + rfbLog("check_xrandr_event(): returning control to" + " caller...\n"); + if (0) fprintf(stderr, "OUT-%d check_xrandr_event('%s')\n", + do_change, msg); + return do_change; + } +#endif + if (0) fprintf(stderr, "OUT-0 check_xrandr_event('%s')\n", msg); + return 0; +} + +int known_xrandr_mode(char *s) { +/* + * default: + * resize: the default + * exit: shutdown clients and exit. + * newfbsize: shutdown clients that do not support NewFBSize encoding. + */ + if (strcmp(s, "default") && strcmp(s, "resize") && + strcmp(s, "exit") && strcmp(s, "newfbsize")) { + return 0; + } else { + return 1; + } +} + +int known_sigpipe_mode(char *s) { +/* + * skip, ignore, exit + */ + if (strcmp(s, "skip") && strcmp(s, "ignore") && + strcmp(s, "exit")) { + return 0; + } else { + return 1; + } +} + /* -- selection.c -- */ /* * Selection/Cutbuffer/Clipboard handlers. @@ -4176,6 +5160,8 @@ static void cutbuffer_send(void) { * * TODO: if we were willing to use libXt, we could perhaps get selection * timestamps to speed up the checking... XtGetSelectionValue(). + * + * Also: XFIXES has XFixesSelectSelectionInput(). */ #define CHKSZ 32 static void selection_send(XEvent *ev) { @@ -4258,36 +5244,70 @@ static void selection_send(XEvent *ev) { rfbSendServerCutText(screen, selection_str, newlen); } -/* - * This routine is periodically called to check for selection related - * and other X11 events and respond to them as needed. - */ -void check_xevents(void) { - XEvent xev; - static int first = 1, sent_some_sel = 0; - static time_t last_request = 0; - time_t now = time(0); - int have_clients = screen->clientHead ? 1 : 0; +/* -- xevents.c -- */ + +void initialize_vnc_connect_prop() { + vnc_connect_str[0] = '\0'; + vnc_connect_prop = XInternAtom(dpy, "VNC_CONNECT", False); +} + +void initialize_xevents(void) { + static int did_xselect_input = 0; + static int did_xcreate_simple_window = 0; + static int did_vnc_connect_prop = 0; + static int did_xfixes = 0; + static int did_xrandr = 0; X_LOCK; - if (first && (watch_selection || vnc_connect)) { + if ((watch_selection || vnc_connect) && !did_xselect_input) { /* * register desired event(s) for notification. * PropertyChangeMask is for CUT_BUFFER0 changes. - * TODO: does this cause a flood of other stuff? + * XXX: does this cause a flood of other stuff? */ XSelectInput(dpy, rootwin, PropertyChangeMask); + did_xselect_input = 1; } - if (first && watch_selection) { + if (watch_selection && !did_xcreate_simple_window) { /* create fake window for our selection ownership, etc */ + selwin = XCreateSimpleWindow(dpy, rootwin, 0, 0, 1, 1, 0, 0, 0); + did_xcreate_simple_window = 1; } - if (first && vnc_connect) { - vnc_connect_str[0] = '\0'; - vnc_connect_prop = XInternAtom(dpy, "VNC_CONNECT", False); + X_UNLOCK; + + if (xrandr && !did_xrandr) { + initialize_xrandr(); + did_xrandr = 1; + } + if (vnc_connect && !did_vnc_connect_prop) { + initialize_vnc_connect_prop(); + did_vnc_connect_prop = 1; + } + if (xfixes_present && use_xfixes && !did_xfixes) { + initialize_xfixes(); + did_xfixes = 1; + } +} + +/* + * This routine is periodically called to check for selection related + * and other X11 events and respond to them as needed. + */ +void check_xevents(void) { + XEvent xev; + static int first = 1, sent_some_sel = 0; + static time_t last_request = 0; + time_t now = time(0); + int have_clients = screen->clientHead ? 1 : 0; + + + if (first) { + initialize_xevents(); } first = 0; + X_LOCK; /* * There is a bug where we have to wait before sending text to * the client... so instead of sending right away we wait a @@ -4337,6 +5357,18 @@ void check_xevents(void) { } } +#ifdef LIBVNCSERVER_HAVE_LIBXRANDR + if (xrandr) { + check_xrandr_event("check_xevents"); + } +#endif +#ifdef LIBVNCSERVER_HAVE_LIBXFIXES + if (XCheckTypedEvent(dpy, xfixes_base_event_type + + XFixesCursorNotify, &xev)) { + got_xfixes_cursor_notify++; + } +#endif + /* check for our PRIMARY request notification: */ if (watch_primary) { if (XCheckTypedEvent(dpy, SelectionNotify, &xev)) { @@ -4400,7 +5432,7 @@ void check_xevents(void) { */ void xcut_receive(char *text, int len, rfbClientPtr cl) { - if (cl->viewOnly) { + if (cl && cl->viewOnly) { return; } if (text == NULL || len == 0) { @@ -4436,6 +5468,1806 @@ void xcut_receive(char *text, int len, rfbClientPtr cl) { set_cutbuffer = 1; } +/* -- remote.c -- */ + +/* + * for the wild-n-crazy -remote/-R interface. + */ +int send_remote_cmd(char *cmd, int query) { + char *str; + FILE *in = NULL; + + if (client_connect_file) { + in = fopen(client_connect_file, "w"); + if (in == NULL) { + fprintf(stderr, "send_remote_cmd: could not open " + "connect file \"%s\" for writing\n", + client_connect_file); + perror("fopen"); + return 1; + } + } else if (vnc_connect_prop == None) { + initialize_vnc_connect_prop(); + if (vnc_connect_prop == None) { + fprintf(stderr, "send_remote_cmd: could not obtain " + "VNC_CONNECT X property\n"); + return 1; + } + } + str = (char *) malloc((strlen(cmd)+5)); + if (query) { + strcpy(str, "qry="); + } else { + strcpy(str, "cmd="); + } + strcat(str, cmd); + + if (in != NULL) { + fprintf(stderr, "sending remote command: \"%s\"\nvia connect" + " file: %s\n", cmd, client_connect_file); + fprintf(in, "%s\n", str); + fclose(in); + } else { + fprintf(stderr, "sending remote command: \"%s\" via VNC_CONNECT" + " X property.\n", cmd); + set_vnc_connect_prop(str); + XFlush(dpy); + } + + if (strstr(str, "qry=") == str) { + char line[VNC_CONNECT_MAX]; + int rc=1, i=0, max=20, ms_sl=100; + for (i=0; i<max; i++) { + usleep(ms_sl * 1000); + if (client_connect_file) { + char *q; + in = fopen(client_connect_file, "r"); + if (in == NULL) { + fprintf(stderr, "send_remote_cmd: could" + " not open connect file \"%s\" for" + " writing\n", client_connect_file); + perror("fopen"); + return 1; + } + fgets(line, VNC_CONNECT_MAX, in); + fclose(in); + q = line; + while (*q != '\0') { + if (*q == '\n') *q = '\0'; + q++; + } + } else { + read_vnc_connect_prop(); + strncpy(line, vnc_connect_str, VNC_CONNECT_MAX); + } + if (strcmp(str, line)){ + fprintf(stdout, "%s\n", line); + fflush(stdout); + rc = 0; + break; + } + } + free(str); + if (rc) { + fprintf(stderr, "error: could not connect to " + "an x11vnc server at %s (rc=%d)\n", + client_connect_file ? client_connect_file + : DisplayString(dpy), rc); + } + return rc; + } + free(str); + return 0; +} + +char *add_item(char *instr, char *item) { + char *p, *str; + int len, saw_item = 0; + + if (! instr) { + str = strdup(item); + return str; + } + len = strlen(instr) + strlen(item) + 2; + str = (char *)malloc(len); + str[0] = '\0'; + + p = strtok(instr, ","); + while (p) { + if (!strcmp(p, item)) { + if (saw_item) { + p = strtok(NULL, ","); + continue; + } + saw_item = 1; + } else if (*p == '\0') { + p = strtok(NULL, ","); + continue; + } + if (str[0]) { + strcat(str, ","); + } + strcat(str, p); + p = strtok(NULL, ","); + } + if (! saw_item) { + if (str[0]) { + strcat(str, ","); + } + strcat(str, item); + + } + return str; +} + +char *delete_item(char *instr, char *item) { + char *p, *str; + int len; + + if (! instr) { + str = strdup(""); + return str; + } + len = strlen(instr) + 1; + str = (char *)malloc(len); + str[0] = '\0'; + + p = strtok(instr, ","); + while (p) { + if (!strcmp(p, item) || *p == '\0') { + p = strtok(NULL, ","); + continue; + } + if (str[0]) { + strcat(str, ","); + } + strcat(str, p); + p = strtok(NULL, ","); + } + return str; +} + +void if_8bpp_do_new_fb(void) { + if (bpp == 8) { + do_new_fb(0); + } else { + rfbLog(" bpp(%d) is not 8bpp, not resetting fb\n", bpp); + } +} + +void check_black_fb(void) { + if (new_fb_size_clients(screen) != client_count) { + rfbLog("trying to send a black fb for non-newfbsize" + " clients %d != %d\n", client_count, + new_fb_size_clients(screen)); + push_black_screen(4); + } +} + +/* + * Huge, ugly switch to handle all remote commands and queries + * -remote/-R and -query/-Q. + */ +char *process_remote_cmd(char *cmd, int stringonly) { + char *p = cmd; + char *co = ""; + char buf[VNC_CONNECT_MAX]; + int do_vnc_connect = 0; + int query = 0; + static char *prev_cursors_mode = NULL; + + if (! accept_remote_cmds) { + rfbLog("remote commands disabled: %s\n", cmd); + return NULL; + } + + if (strstr(cmd, "cmd=") == cmd) { + p += strlen("cmd="); + } else if (strstr(cmd, "qry=") == cmd) { + query = 1; + if (strchr(cmd, ',')) { + /* comma separated batch mode */ + char *s, *q, *res; + char tmp[512]; + strcpy(buf, ""); + s = strdup(cmd + strlen("qry=")); + q = strtok(s, ","); + while (q) { + sprintf(tmp, "qry=%s", q); + res = process_remote_cmd(tmp, 1); + if (res) { + strcat(buf, res); + free(res); + } + q = strtok(NULL, ","); + if (q) { + strcat(buf, ","); + } + } + free(s); + goto qry; + } + p += strlen("qry="); + } else { + rfbLog("ignoring malformed command: %s\n", cmd); + return NULL; + } + + if (!query && strstr(p, "file:") == p) { + /* process command from a script file */ + FILE *in; + char line[VNC_CONNECT_MAX], cmd[VNC_CONNECT_MAX]; + int sz = VNC_CONNECT_MAX - 6; + p += strlen("file:"); + in = fopen(p, "r"); + if (in == NULL) { + rfbLog("process_remote_cmd: could not open cmd file" + " '%s'\n", p); + return NULL; + } + rfbLog("process_remote_cmd: running command from '%s'\n", p); + while (fgets(line, sz, in) != NULL) { + char *q; + if (strlen(line)) { + q = line + strlen(line) - 1; + if (*q == '\n') { + *q = '\0'; + } + while (q > line && isspace(*q)) { + *q = '\0'; + q--; + } + } + q = line; + while (isspace(*q)) { + q++; + } + if ( *q == '#' || *q == '\0') { + continue; + } + strcpy(cmd, "cmd="); + strcat(cmd, q); + process_remote_cmd(cmd, 0); + } + fclose(in); + return NULL; + } + + /* always call like: COLON_CHECK("foobar:") */ +#define COLON_CHECK(str) \ + if (strstr(p, str) != p) { \ + co = ":"; \ + if (! query) { \ + goto done; \ + } \ + } else { \ + char *q = strchr(p, ':'); \ + if (query && q != NULL) { \ + *(q+1) = '\0'; \ + } \ + } + +#define NOTAPP \ + if (query) { \ + if (strchr(p, ':')) { \ + sprintf(buf, "ans=%sN/A", p); \ + } else { \ + sprintf(buf, "ans=%s:N/A", p); \ + } \ + goto qry; \ + } + +#define NOTAPPRO \ + if (query) { \ + if (strchr(p, ':')) { \ + sprintf(buf, "aro=%sN/A", p); \ + } else { \ + sprintf(buf, "aro=%s:N/A", p); \ + } \ + goto qry; \ + } + +/* + * Add: passwdfile logfile bg nofb rfbauth passwd noshm... + */ + if (!strcmp(p, "stop") || !strcmp(p, "quit") || + !strcmp(p, "exit") || !strcmp(p, "shutdown")) { + NOTAPP + close_all_clients(); + rfbLog("process_remote_cmd: setting shut_down flag\n"); + shut_down = 1; + + } else if (!strcmp(p, "ping")) { + query = 1; + if (rfb_desktop_name) { + sprintf(buf, "ans=%s:%s", p, rfb_desktop_name); + } else { + sprintf(buf, "ans=%s:%s", p, "unknown"); + } + goto qry; + + } else if (!strcmp(p, "blacken") || !strcmp(p, "zero")) { + NOTAPP + push_black_screen(4); + } else if (!strcmp(p, "refresh")) { + NOTAPP + refresh_screen(); + } else if (!strcmp(p, "reset")) { + NOTAPP + do_new_fb(1); + } else if (strstr(p, "zero:") == p) { /* skip-cmd-list */ + int x1, y1, x2, y2; + NOTAPP + p += strlen("zero:"); + if (sscanf(p, "%d,%d,%d,%d", &x1, &y1, &x2, &y2) == 4) { + int mark = 1; + rfbLog("zeroing rect: %s\n", p); + if (x1 < 0 || x2 < 0) { + x1 = nabs(x1); + x2 = nabs(x2); + mark = 0; /* hack for testing */ + } + + zero_fb(x1, y1, x2, y2); + if (mark) { + mark_rect_as_modified(x1, y1, x2, y2, 1); + } + push_sleep(4); + } + } else if (strstr(p, "damagefb:") == p) { /* skip-cmd-list */ + int delay; + NOTAPP + p += strlen("damagefb:"); + if (sscanf(p, "%d", &delay) == 1) { + rfbLog("damaging client fb's for %d secs " + "(by not marking rects.)\n", delay); + damage_time = time(0); + damage_delay = delay; + } + + } else if (strstr(p, "close") == p) { + NOTAPP + COLON_CHECK("close:") + p += strlen("close:"); + close_clients(p); + } else if (strstr(p, "disconnect") == p) { + NOTAPP + COLON_CHECK("disconnect:") + p += strlen("disconnect:"); + close_clients(p); + + } else if (strstr(p, "id") == p) { + int ok = 0; + Window twin; + COLON_CHECK("id:") + if (query) { + sprintf(buf, "ans=%s%s0x%lx", p, co, subwin); + goto qry; + } + p += strlen("id:"); + if (*p == '\0' || !strcmp("root", p)) { + /* back to root win */ + twin = 0x0; + ok = 1; + } else if (!strcmp("pick", p)) { + twin = 0x0; + if (safe_remote_only) { + rfbLog("unsafe: '-id pick'\n"); + } else if (pick_windowid(&twin)) { + ok = 1; + } + } else if (! scan_hexdec(p, &twin)) { + rfbLog("-id: incorrect hex/dec number: %s\n", p); + } else { + ok = 1; + } + if (ok) { + if (twin && ! valid_window(twin)) { + rfbLog("skipping invalid sub-window: 0x%lx\n", + twin); + } else { + subwin = twin; + rootshift = 0; + check_black_fb(); + do_new_fb(1); + } + } + } else if (strstr(p, "sid") == p) { + int ok = 0; + Window twin; + COLON_CHECK("sid:") + if (query) { + sprintf(buf, "ans=%s%s0x%lx", p, co, subwin); + goto qry; + } + p += strlen("sid:"); + if (*p == '\0' || !strcmp("root", p)) { + /* back to root win */ + twin = 0x0; + ok = 1; + } else if (!strcmp("pick", p)) { + twin = 0x0; + if (safe_remote_only) { + rfbLog("unsafe: '-sid pick'\n"); + } else if (pick_windowid(&twin)) { + ok = 1; + } + } else if (! scan_hexdec(p, &twin)) { + rfbLog("-sid: incorrect hex/dec number: %s\n", p); + } else { + ok = 1; + } + if (ok) { + if (twin && ! valid_window(twin)) { + rfbLog("skipping invalid sub-window: 0x%lx\n", + twin); + } else { + subwin = twin; + rootshift = 1; + check_black_fb(); + do_new_fb(1); + } + } + + } else if (!strcmp(p, "flashcmap")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, flash_cmap); + goto qry; + } + rfbLog("process_remote_cmd: turning on flashcmap mode.\n"); + flash_cmap = 1; + } else if (!strcmp(p, "noflashcmap")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !flash_cmap); + goto qry; + } + rfbLog("process_remote_cmd: turning off flashcmap mode.\n"); + flash_cmap = 0; + + } else if (!strcmp(p, "truecolor")) { + int orig = force_indexed_color; + if (query) { + sprintf(buf, "ans=%s:%d", p, !force_indexed_color); + goto qry; + } + rfbLog("process_remote_cmd: turning off notruecolor mode.\n"); + force_indexed_color = 0; + if (orig != force_indexed_color) { + if_8bpp_do_new_fb(); + } + } else if (!strcmp(p, "notruecolor")) { + int orig = force_indexed_color; + if (query) { + sprintf(buf, "ans=%s:%d", p, force_indexed_color); + goto qry; + } + rfbLog("process_remote_cmd: turning on notruecolor mode.\n"); + force_indexed_color = 1; + if (orig != force_indexed_color) { + if_8bpp_do_new_fb(); + } + + } else if (!strcmp(p, "overlay")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, overlay); + goto qry; + } + rfbLog("process_remote_cmd: turning on -overlay mode.\n"); + if (!overlay_present) { + rfbLog("skipping: overlay extension not present.\n"); + } else if (overlay) { + rfbLog("skipping: already in -overlay mode.\n"); + } else { + int reset_mem = 0; + /* here we go... */ + if (using_shm) { + rfbLog("setting -noshm mode.\n"); + using_shm = 0; + reset_mem = 1; + } + overlay = 1; + do_new_fb(reset_mem); + } + } else if (!strcmp(p, "nooverlay")) { + int orig = overlay; + if (query) { + sprintf(buf, "ans=%s:%d", p, !overlay); + goto qry; + } + rfbLog("process_remote_cmd: turning off overlay mode\n"); + overlay = 0; + if (!overlay_present) { + rfbLog("warning: overlay extension not present.\n"); + } else if (!orig) { + rfbLog("skipping: already not in -overlay mode.\n"); + } else { + /* here we go... */ + do_new_fb(0); + } + + } else if (!strcmp(p, "overlay_cursor") || + !strcmp(p, "overlay_yescursor")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, overlay_cursor); + goto qry; + } + rfbLog("process_remote_cmd: turning on overlay_cursor mode.\n"); + overlay_cursor = 1; + if (!overlay_present) { + rfbLog("warning: overlay extension not present.\n"); + } else if (!overlay) { + rfbLog("warning: not in -overlay mode.\n"); + } else { + rfbLog("You may want to run -R noshow_cursor or\n"); + rfbLog(" -R cursor:none to disable any extra " + "cursors.\n"); + } + } else if (!strcmp(p, "nooverlay_cursor") || + !strcmp(p, "overlay_nocursor")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !overlay_cursor); + goto qry; + } + rfbLog("process_remote_cmd: turning off overlay_cursor mode\n"); + overlay_cursor = 0; + if (!overlay_present) { + rfbLog("warning: overlay extension not present.\n"); + } else if (!overlay) { + rfbLog("warning: not in -overlay mode.\n"); + } else { + rfbLog("You may want to run -R show_cursor or\n"); + rfbLog(" -R cursor:... to re-enable any cursors.\n"); + } + + } else if (strstr(p, "visual") == p) { + COLON_CHECK("visual:") + if (query) { + sprintf(buf, "ans=%s%s%s", p, co, NONUL(visual_str)); + goto qry; + } + p += strlen("visual:"); + if (visual_str) free(visual_str); + visual_str = strdup(p); + + /* OK, this requires a new fb... */ + do_new_fb(0); + + } else if (strstr(p, "scale") == p) { + COLON_CHECK("scale:") + if (query) { + sprintf(buf, "ans=%s%s%s", p, co, NONUL(scale_str)); + goto qry; + } + p += strlen("scale:"); + if (scale_str) free(scale_str); + scale_str = strdup(p); + + /* OK, this requires a new fb... */ + check_black_fb(); + do_new_fb(0); + + } else if (!strcmp(p, "viewonly")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, view_only); goto qry; + } + rfbLog("process_remote_cmd: enable viewonly mode.\n"); + view_only = 1; + } else if (!strcmp(p, "noviewonly")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !view_only); goto qry; + } + rfbLog("process_remote_cmd: disable viewonly mode.\n"); + view_only = 0; + + } else if (!strcmp(p, "shared")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, shared); goto qry; + } + rfbLog("process_remote_cmd: enable sharing.\n"); + shared = 1; + screen->alwaysShared = TRUE; + screen->neverShared = FALSE; + } else if (!strcmp(p, "noshared")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !shared); goto qry; + } + rfbLog("process_remote_cmd: disable sharing.\n"); + shared = 0; + screen->alwaysShared = FALSE; + screen->neverShared = TRUE; + + } else if (!strcmp(p, "forever")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, 1-connect_once); goto qry; + } + rfbLog("process_remote_cmd: enable -forever mode.\n"); + connect_once = 0; + } else if (!strcmp(p, "noforever") || !strcmp(p, "once")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, connect_once); goto qry; + } + rfbLog("process_remote_cmd: disable -forever mode.\n"); + connect_once = 1; + + } else if (!strcmp(p, "deny") || !strcmp(p, "lock")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, deny_all); goto qry; + } + rfbLog("process_remote_cmd: denying new connections.\n"); + deny_all = 1; + } else if (!strcmp(p, "nodeny") || !strcmp(p, "unlock")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !deny_all); goto qry; + } + rfbLog("process_remote_cmd: allowing new connections.\n"); + deny_all = 0; + + } else if (strstr(p, "connect") == p) { + NOTAPP + COLON_CHECK("connect:") + p += strlen("connect:"); /* handled at end */ + do_vnc_connect = 1; + + } else if (strstr(p, "allowonce") == p) { + NOTAPP + COLON_CHECK("allowonce:") + p += strlen("allowonce:"); + allow_once = strdup(p); + rfbLog("process_remote_cmd: set allow_once %s\n", allow_once); + + } else if (strstr(p, "allow") == p) { + char *before, *old; + COLON_CHECK("allow:") + if (query) { + sprintf(buf, "ans=%s%s%s", p, co, NONUL(allow_list)); + goto qry; + } + p += strlen("allow:"); + if (allow_list && strchr(allow_list, '/')) { + rfbLog("process_remote_cmd: cannot use allow:host\n"); + rfbLog("in '-allow %s' mode.\n", allow_list); + return NULL; + } + if (allow_list) { + before = strdup(allow_list); + } else { + before = strdup(""); + } + + old = allow_list; + if (*p == '+') { + p++; + allow_list = add_item(allow_list, p); + } else if (*p == '-') { + p++; + allow_list = delete_item(allow_list, p); + } else { + allow_list = strdup(p); + } + + if (strcmp(before, allow_list)) { + rfbLog("process_remote_cmd: modified allow_list:\n"); + rfbLog(" from: \"%s\"\n", before); + rfbLog(" to: \"%s\"\n", allow_list); + } + if (old) free(old); + free(before); + + } else if (!strcmp(p, "localhost")) { + char *before, *old; + if (query) { + int state = 0; + if (allow_list && !strcmp(allow_list, "127.0.0.1")) { + state = 1; + } + sprintf(buf, "ans=%s:%d", p, state); + goto qry; + } + if (allow_list) { + before = strdup(allow_list); + } else { + before = strdup(""); + } + old = allow_list; + + allow_list = strdup("127.0.0.1"); + + if (strcmp(before, allow_list)) { + rfbLog("process_remote_cmd: modified allow_list:\n"); + rfbLog(" from: \"%s\"\n", before); + rfbLog(" to: \"%s\"\n", allow_list); + } + if (old) free(old); + free(before); + } else if (!strcmp(p, "nolocalhost")) { + char *before, *old; + if (query) { + int state = 0; + if (allow_list && !strcmp(allow_list, "127.0.0.1")) { + state = 1; + } + sprintf(buf, "ans=%s:%d", p, !state); + goto qry; + } + if (allow_list) { + before = strdup(allow_list); + } else { + before = strdup(""); + } + old = allow_list; + + allow_list = strdup(""); + + if (strcmp(before, allow_list)) { + rfbLog("process_remote_cmd: modified allow_list:\n"); + rfbLog(" from: \"%s\"\n", before); + rfbLog(" to: \"%s\"\n", allow_list); + } + if (old) free(old); + free(before); + + } else if (strstr(p, "accept") == p) { + COLON_CHECK("accept:") + if (query) { + sprintf(buf, "ans=%s%s%s", p, co, NONUL(accept_cmd)); + goto qry; + } + if (safe_remote_only) { + rfbLog("unsafe: %s\n", p); + } else { + p += strlen("accept:"); + if (accept_cmd) free(accept_cmd); + accept_cmd = strdup(p); + } + + } else if (strstr(p, "gone") == p) { + COLON_CHECK("gone:") + if (query) { + sprintf(buf, "ans=%s%s%s", p, co, NONUL(gone_cmd)); + goto qry; + } + if (safe_remote_only) { + rfbLog("unsafe: %s\n", p); + } else { + p += strlen("gone:"); + if (gone_cmd) free(gone_cmd); + gone_cmd = strdup(p); + } + + } else if (!strcmp(p, "shm")) { + int orig = using_shm; + if (query) { + sprintf(buf, "ans=%s:%d", p, using_shm); + goto qry; + } + rfbLog("process_remote_cmd: turning off noshm mode.\n"); + using_shm = 1; + if (orig != using_shm) { + do_new_fb(1); + } else { + rfbLog(" already in shm mode.\n"); + } + } else if (!strcmp(p, "noshm")) { + int orig = using_shm; + if (query) { + sprintf(buf, "ans=%s:%d", p, !using_shm); + goto qry; + } + rfbLog("process_remote_cmd: turning on noshm mode.\n"); + using_shm = 0; + if (orig != using_shm) { + do_new_fb(1); + } else { + rfbLog(" already in noshm mode.\n"); + } + + } else if (!strcmp(p, "flipbyteorder")) { + int orig = flip_byte_order; + if (query) { + sprintf(buf, "ans=%s:%d", p, flip_byte_order); + goto qry; + } + rfbLog("process_remote_cmd: turning on flipbyteorder mode.\n"); + flip_byte_order = 1; + if (orig != flip_byte_order) { + if (! using_shm) { + do_new_fb(1); + } else { + rfbLog(" using shm, not resetting fb\n"); + } + } + } else if (!strcmp(p, "noflipbyteorder")) { + int orig = flip_byte_order; + if (query) { + sprintf(buf, "ans=%s:%d", p, !flip_byte_order); + goto qry; + } + rfbLog("process_remote_cmd: turning off flipbyteorder mode.\n"); + flip_byte_order = 0; + if (orig != flip_byte_order) { + if (! using_shm) { + do_new_fb(1); + } else { + rfbLog(" using shm, not resetting fb\n"); + } + } + + } else if (!strcmp(p, "onetile")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, single_copytile); goto qry; + } + rfbLog("process_remote_cmd: enable -onetile mode.\n"); + single_copytile = 1; + } else if (!strcmp(p, "noonetile")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !single_copytile); + goto qry; + } + rfbLog("process_remote_cmd: disable -onetile mode.\n"); + if (tile_shm_count < ntiles_x) { + rfbLog(" this has no effect: tile_shm_count=%d" + " ntiles_x=%d\n", tile_shm_count, ntiles_x); + + } + single_copytile = 0; + + } else if (strstr(p, "blackout") == p) { + char *before, *old; + COLON_CHECK("blackout:") + if (query) { + sprintf(buf, "ans=%s%s%s", p, co, + NONUL(blackout_string)); + goto qry; + } + p += strlen("blackout:"); + if (blackout_string) { + before = strdup(blackout_string); + } else { + before = strdup(""); + } + old = blackout_string; + if (*p == '+') { + p++; + blackout_string = add_item(blackout_string, p); + } else if (*p == '-') { + p++; + blackout_string = delete_item(blackout_string, p); + } else { + blackout_string = strdup(p); + } + if (strcmp(before, blackout_string)) { + rfbLog("process_remote_cmd: changing -blackout\n"); + rfbLog(" from: %s\n", before); + rfbLog(" to: %s\n", blackout_string); + if (0 && !strcmp(blackout_string, "") && + single_copytile_orig != single_copytile) { + rfbLog("resetting single_copytile to: %d\n", + single_copytile_orig); + single_copytile = single_copytile_orig; + } + initialize_blackouts_and_xinerama(); + } + if (old) free(old); + free(before); + + } else if (!strcmp(p, "xinerama")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, xinerama); goto qry; + } + rfbLog("process_remote_cmd: enable xinerama mode." + "(if applicable).\n"); + xinerama = 1; + initialize_blackouts_and_xinerama(); + } else if (!strcmp(p, "noxinerama")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !xinerama); goto qry; + } + rfbLog("process_remote_cmd: disable xinerama mode." + "(if applicable).\n"); + xinerama = 0; + initialize_blackouts_and_xinerama(); + + } else if (!strcmp(p, "xrandr")) { + int orig = xrandr; + if (query) { + sprintf(buf, "ans=%s:%d", p, xrandr); goto qry; + } + if (xrandr_present) { + rfbLog("process_remote_cmd: enable xrandr mode.\n"); + xrandr = 1; + if (! xrandr_mode) { + xrandr_mode = strdup("default"); + } + if (orig != xrandr) { + initialize_xrandr(); + } + } else { + rfbLog("process_remote_cmd: XRANDR ext. not " + "present.\n"); + } + } else if (!strcmp(p, "noxrandr")) { + int orig = xrandr; + if (query) { + sprintf(buf, "ans=%s:%d", p, !xrandr); goto qry; + } + xrandr = 0; + if (xrandr_present) { + rfbLog("process_remote_cmd: disable xrandr mode.\n"); + if (orig != xrandr) { + initialize_xrandr(); + } + } else { + rfbLog("process_remote_cmd: XRANDR ext. not " + "present.\n"); + } + } else if (strstr(p, "xrandr_mode") == p) { + int orig = xrandr; + COLON_CHECK("xrandr_mode:") + if (query) { + sprintf(buf, "ans=%s%s%s", p, co, NONUL(xrandr_mode)); + goto qry; + } + p += strlen("xrandr_mode:"); + if (!strcmp("none", p)) { + xrandr = 0; + } else { + if (known_xrandr_mode(p)) { + if (xrandr_mode) free(xrandr_mode); + xrandr_mode = strdup(p); + } else { + rfbLog("skipping unknown xrandr mode: %s\n", p); + return NULL; + } + xrandr = 1; + } + if (xrandr_present) { + if (xrandr) { + rfbLog("process_remote_cmd: enable xrandr" + " mode.\n"); + } else { + rfbLog("process_remote_cmd: disable xrandr" + " mode.\n"); + } + if (! xrandr_mode) { + xrandr_mode = strdup("default"); + } + if (orig != xrandr) { + initialize_xrandr(); + } + } else { + rfbLog("process_remote_cmd: XRANDR ext. not " + "present.\n"); + } + + } else if (strstr(p, "padgeom") == p) { + COLON_CHECK("padgeom:") + if (query) { + sprintf(buf, "ans=%s%s%s", p, co, NONUL(pad_geometry)); + goto qry; + } + p += strlen("padgeom:"); + if (!strcmp("force", p) || !strcmp("do",p) || !strcmp("go",p)) { + rfbLog("process_remote_cmd: invoking " + "install_padded_fb()\n"); + install_padded_fb(pad_geometry); + } else { + if (pad_geometry) free(pad_geometry); + pad_geometry = strdup(p); + rfbLog("process_remote_cmd: set padgeom to: %s\n", + pad_geometry); + } + + } else if (!strcmp(p, "quiet") || !strcmp(p, "q")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, quiet); goto qry; + } + rfbLog("process_remote_cmd: turning on quiet mode.\n"); + quiet = 1; + } else if (!strcmp(p, "noquiet")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !quiet); goto qry; + } + rfbLog("process_remote_cmd: turning off quiet mode.\n"); + quiet = 0; + + } else if (!strcmp(p, "modtweak")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, use_modifier_tweak); + goto qry; + } + rfbLog("process_remote_cmd: enabling -modtweak mode.\n"); + if (! use_modifier_tweak) { + use_modifier_tweak = 1; + initialize_modtweak(); + } + use_modifier_tweak = 1; + + } else if (!strcmp(p, "nomodtweak")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !use_modifier_tweak); + goto qry; + } + rfbLog("process_remote_cmd: enabling -nomodtweak mode.\n"); + use_modifier_tweak = 0; + + } else if (!strcmp(p, "xkb")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, use_xkb_modtweak); + goto qry; + } + if (! xkb_present) { + rfbLog("process_remote_cmd: cannot enable -xkb " + "modtweak mode (not supported on X display)\n"); + return NULL; + } + rfbLog("process_remote_cmd: enabling -xkb modtweak mode" + " (if supported).\n"); + if (! use_modifier_tweak || ! use_xkb_modtweak) { + use_modifier_tweak = 1; + use_xkb_modtweak = 1; + initialize_modtweak(); + } + use_modifier_tweak = 1; + use_xkb_modtweak = 1; + + } else if (!strcmp(p, "noxkb")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !use_xkb_modtweak); + goto qry; + } + if (! xkb_present) { + rfbLog("process_remote_cmd: cannot disable -xkb " + "modtweak mode (not supported on X display)\n"); + return NULL; + } + rfbLog("process_remote_cmd: disabling -xkb modtweak mode.\n"); + use_xkb_modtweak = 0; + + } else if (strstr(p, "skip_keycodes") == p) { + COLON_CHECK("skip_keycodes:") + if (query) { + sprintf(buf, "ans=%s%s%s", p, co, NONUL(skip_keycodes)); + goto qry; + } + p += strlen("skip_keycodes:"); + rfbLog("process_remote_cmd: setting xkb -skip_keycodes" + " to:\n\t'%s'\n", p); + if (! xkb_present) { + rfbLog("process_remote_cmd: warning xkb not present\n"); + } else if (! use_xkb_modtweak) { + rfbLog("process_remote_cmd: turning on xkb.\n"); + use_xkb_modtweak = 1; + if (! use_modifier_tweak) { + rfbLog("process_remote_cmd: turning on " + "modtweak.\n"); + use_modifier_tweak = 1; + } + } + if (skip_keycodes) free(skip_keycodes); + skip_keycodes = strdup(p); + initialize_modtweak(); + + } else if (!strcmp(p, "add_keysyms")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, add_keysyms); + goto qry; + } + rfbLog("process_remote_cmd: enabling -add_keysyms mode.\n"); + add_keysyms = 1; + + } else if (!strcmp(p, "noadd_keysyms")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !add_keysyms); + goto qry; + } + rfbLog("process_remote_cmd: disabling -add_keysyms mode.\n"); + add_keysyms = 0; + + } else if (!strcmp(p, "clear_mods")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, clear_mods == 1); + goto qry; + } + rfbLog("process_remote_cmd: enabling -clear_mods mode.\n"); + clear_mods = 1; + clear_modifiers(0); + + } else if (!strcmp(p, "noclear_mods")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !(clear_mods == 1)); + goto qry; + } + rfbLog("process_remote_cmd: disabling -clear_mods mode.\n"); + clear_mods = 0; + + } else if (!strcmp(p, "clear_keys")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, clear_mods == 2); + goto qry; + } + rfbLog("process_remote_cmd: enabling -clear_keys mode.\n"); + clear_mods = 2; + clear_keys(); + + } else if (!strcmp(p, "noclear_keys")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !(clear_mods == 2)); + goto qry; + } + rfbLog("process_remote_cmd: disabling -clear_keys mode.\n"); + clear_mods = 0; + + } else if (strstr(p, "remap") == p) { + char *before, *old; + COLON_CHECK("remap:") + if (query) { + sprintf(buf, "ans=%s%s%s", p, co, NONUL(remap_file)); + goto qry; + } + p += strlen("remap:"); + if ((*p == '+' || *p == '-') && remap_file && + strchr(remap_file, '/')) { + rfbLog("process_remote_cmd: cannot use remap:+/-\n"); + rfbLog("in '-remap %s' mode.\n", remap_file); + return NULL; + } + if (remap_file) { + before = strdup(remap_file); + } else { + before = strdup(""); + } + old = remap_file; + if (*p == '+') { + p++; + remap_file = add_item(remap_file, p); + } else if (*p == '-') { + p++; + remap_file = delete_item(remap_file, p); + if (! strchr(remap_file, '-')) { + *remap_file = '\0'; + } + } else { + remap_file = strdup(p); + } + if (strcmp(before, remap_file)) { + rfbLog("process_remote_cmd: changed -remap\n"); + rfbLog(" from: %s\n", before); + rfbLog(" to: %s\n", remap_file); + initialize_remap(remap_file); + } + if (old) free(old); + free(before); + + } else if (!strcmp(p, "repeat")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !no_autorepeat); + goto qry; + } + rfbLog("process_remote_cmd: enabling -repeat mode.\n"); + if (no_autorepeat) { + autorepeat(1); + } + no_autorepeat = 0; + + } else if (!strcmp(p, "norepeat")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, no_autorepeat); + goto qry; + } + rfbLog("process_remote_cmd: enabling -norepeat mode.\n"); + if (! no_autorepeat && client_count) { + no_autorepeat = 1; + autorepeat(0); + } + no_autorepeat = 1; + + } else if (!strcmp(p, "bell")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, sound_bell); + goto qry; + } + rfbLog("process_remote_cmd: enabling bell (if supported).\n"); + sound_bell = 1; + + } else if (!strcmp(p, "nobell")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !sound_bell); + goto qry; + } + rfbLog("process_remote_cmd: disabling bell.\n"); + sound_bell = 0; + + } else if (!strcmp(p, "sel")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, watch_selection); + goto qry; + } + rfbLog("process_remote_cmd: enabling watch_selection.\n"); + watch_selection = 1; + + } else if (!strcmp(p, "nosel")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !watch_selection); + goto qry; + } + rfbLog("process_remote_cmd: disabling watch_selection.\n"); + watch_selection = 0; + + } else if (!strcmp(p, "primary")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, watch_primary); + goto qry; + } + rfbLog("process_remote_cmd: enabling watch_primary.\n"); + watch_primary = 1; + + } else if (!strcmp(p, "noprimary")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !watch_primary); + goto qry; + } + rfbLog("process_remote_cmd: disabling watch_primary.\n"); + watch_primary = 0; + + } else if (!strcmp(p, "set_no_cursor")) { /* skip-cmd-list */ + rfbLog("process_remote_cmd: calling set_no_cursor()\n"); + set_no_cursor(); + + } else if (!strcmp(p, "cursorshape")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, cursor_shape_updates); + goto qry; + } + rfbLog("process_remote_cmd: turning on cursorshape mode.\n"); + + rfbUndrawCursor(screen); + set_no_cursor(); + cursor_shape_updates = 1; + restore_cursor_shape_updates(screen); + } else if (!strcmp(p, "nocursorshape")) { + int i, max = 5; + if (query) { + sprintf(buf, "ans=%s:%d", p, !cursor_shape_updates); + goto qry; + } + rfbLog("process_remote_cmd: turning off cursorshape mode.\n"); + + rfbUndrawCursor(screen); + set_no_cursor(); + for (i=0; i<max; i++) { + /* XXX: try to force empty cursor back to client */ + rfbPE(screen, -1); + } + cursor_shape_updates = 0; + disable_cursor_shape_updates(screen); + + } else if (!strcmp(p, "cursorpos")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, cursor_pos_updates); + goto qry; + } + rfbLog("process_remote_cmd: turning on cursorpos mode.\n"); + cursor_pos_updates = 1; + } else if (!strcmp(p, "nocursorpos")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !cursor_pos_updates); + goto qry; + } + rfbLog("process_remote_cmd: turning off cursorpos mode.\n"); + cursor_pos_updates = 0; + + } else if (strstr(p, "cursor") == p) { + COLON_CHECK("cursor:") + if (query) { + sprintf(buf, "ans=%s%s%s", p, co, + NONUL(multiple_cursors_mode)); + goto qry; + } + p += strlen("cursor:"); + if (multiple_cursors_mode) { + if (prev_cursors_mode) free(prev_cursors_mode); + prev_cursors_mode = strdup(multiple_cursors_mode); + free(multiple_cursors_mode); + } + multiple_cursors_mode = strdup(p); + + rfbLog("process_remote_cmd: changed -cursor mode " + "to: %s\n", multiple_cursors_mode); + + if (strcmp(multiple_cursors_mode, "none") && !show_cursor) { + show_cursor = 1; + rfbLog("process_remote_cmd: changed show_cursor " + "to: %d\n", show_cursor); + } + initialize_cursors_mode(); + + } else if (!strcmp(p, "show_cursor")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, show_cursor); + goto qry; + } + rfbLog("process_remote_cmd: enabling show_cursor.\n"); + show_cursor = 1; + if (multiple_cursors_mode && !strcmp(multiple_cursors_mode, + "none")) { + free(multiple_cursors_mode); + if (prev_cursors_mode) { + multiple_cursors_mode = + strdup(prev_cursors_mode); + } else { + multiple_cursors_mode = strdup("default"); + } + rfbLog("process_remote_cmd: changed -cursor mode " + "to: %s\n", multiple_cursors_mode); + } + initialize_cursors_mode(); + } else if (!strcmp(p, "noshow_cursor") || !strcmp(p, "nocursor")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !show_cursor); + goto qry; + } + if (prev_cursors_mode) free(prev_cursors_mode); + prev_cursors_mode = strdup(multiple_cursors_mode); + + rfbLog("process_remote_cmd: disabling show_cursor.\n"); + show_cursor = 0; + initialize_cursors_mode(); + + } else if (!strcmp(p, "xfixes")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, use_xfixes); + goto qry; + } + if (! xfixes_present) { + rfbLog("process_remote_cmd: cannot enable xfixes " + "(not supported on X display)\n"); + return NULL; + } + rfbLog("process_remote_cmd: enabling -xfixes" + " (if supported).\n"); + use_xfixes = 1; + initialize_xfixes(); + } else if (!strcmp(p, "noxfixes")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !use_xfixes); + goto qry; + } + if (! xfixes_present) { + rfbLog("process_remote_cmd: disabling xfixes " + "(but not supported on X display)\n"); + return NULL; + } + rfbLog("process_remote_cmd: disabling -xfixes.\n"); + use_xfixes = 0; + initialize_xfixes(); + + } else if (strstr(p, "xwarp") == p || strstr(p, "xwarppointer")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, use_xwarppointer); + goto qry; + } + rfbLog("process_remote_cmd: turning on xwarppointer mode.\n"); + use_xwarppointer = 1; + } else if (strstr(p, "noxwarp") == p || strstr(p, "noxwarppointer")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !use_xwarppointer); + goto qry; + } + rfbLog("process_remote_cmd: turning off xwarppointer mode.\n"); + use_xwarppointer = 0; + + } else if (strstr(p, "buttonmap") == p) { + COLON_CHECK("buttonmap:") + if (query) { + sprintf(buf, "ans=%s%s%s", p, co, NONUL(pointer_remap)); + goto qry; + } + p += strlen("buttonmap:"); + if (pointer_remap) free(pointer_remap); + pointer_remap = strdup(p); + + rfbLog("process_remote_cmd: setting -buttonmap to:\n" + "\t'%s'\n", p); + initialize_pointer_map(p); + + } else if (!strcmp(p, "dragging")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, show_dragging); + goto qry; + } + rfbLog("process_remote_cmd: enabling mouse dragging mode.\n"); + show_dragging = 1; + } else if (!strcmp(p, "nodragging")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !show_dragging); + goto qry; + } + rfbLog("process_remote_cmd: enabling mouse nodragging mode.\n"); + show_dragging = 0; + + } else if (strstr(p, "pointer_mode") == p) { + int pm; + COLON_CHECK("pointer_mode:") + if (query) { + sprintf(buf, "ans=%s%s%d", p, co, pointer_mode); + goto qry; + } + p += strlen("pointer_mode:"); + pm = atoi(p); + if (pm < 1 || pm > pointer_mode_max) { + rfbLog("process_remote_cmd: pointer_mode out of range:" + " 1-%d: %d\n", pointer_mode_max, pm); + } else { + rfbLog("process_remote_cmd: setting pointer_mode %d\n", + pm); + pointer_mode = pm; + } + } else if (strstr(p, "input_skip") == p) { + int is; + COLON_CHECK("input_skip:") + if (query) { + sprintf(buf, "ans=%s%s%d", p, co, ui_skip); + goto qry; + } + p += strlen("input_skip:"); + is = atoi(p); + rfbLog("process_remote_cmd: setting input_skip %d\n", is); + ui_skip = is; + + } else if (!strcmp(p, "debug_pointer") || !strcmp(p, "dp")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, debug_pointer); + goto qry; + } + rfbLog("process_remote_cmd: turning on debug_pointer.\n"); + debug_pointer = 1; + } else if (!strcmp(p, "nodebug_pointer") || !strcmp(p, "nodp")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !debug_pointer); + goto qry; + } + rfbLog("process_remote_cmd: turning off debug_pointer.\n"); + debug_pointer = 0; + + } else if (!strcmp(p, "debug_keyboard") || !strcmp(p, "dk")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, debug_keyboard); + goto qry; + } + rfbLog("process_remote_cmd: turning on debug_keyboard.\n"); + debug_keyboard = 1; + } else if (!strcmp(p, "nodebug_keyboard") || !strcmp(p, "nodk")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !debug_keyboard); + goto qry; + } + rfbLog("process_remote_cmd: turning off debug_keyboard.\n"); + debug_keyboard = 0; + + } else if (strstr(p, "deferupdate") == p) { + int d; + COLON_CHECK("deferupdate:") + if (query) { + sprintf(buf, "ans=%s%s%d", p, co, + screen->deferUpdateTime); + goto qry; + } + p += strlen("deferupdate:"); + d = atoi(p); + if (d < 0) d = 0; + rfbLog("process_remote_cmd: setting defer to %d ms.\n", d); + screen->deferUpdateTime = d; + + } else if (strstr(p, "defer") == p) { + int d; + COLON_CHECK("defer:") + if (query) { + sprintf(buf, "ans=%s%s%d", p, co, + screen->deferUpdateTime); + goto qry; + } + p += strlen("defer:"); + d = atoi(p); + if (d < 0) d = 0; + rfbLog("process_remote_cmd: setting defer to %d ms.\n", d); + /* XXX not part of API? */ + screen->deferUpdateTime = d; + + } else if (strstr(p, "wait") == p) { + int w; + COLON_CHECK("wait:") + if (query) { + sprintf(buf, "ans=%s%s%d", p, co, waitms); + goto qry; + } + p += strlen("wait:"); + w = atoi(p); + if (w < 0) w = 0; + rfbLog("process_remote_cmd: setting wait %d -> %d ms.\n", + waitms, w); + waitms = w; + + } else if (!strcmp(p, "nap")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, take_naps); goto qry; + } + rfbLog("process_remote_cmd: turning on nap mode.\n"); + take_naps = 1; + } else if (!strcmp(p, "nonap")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !take_naps); goto qry; + } + rfbLog("process_remote_cmd: turning off nap mode.\n"); + take_naps = 0; + + } else if (strstr(p, "sb") == p) { + int w; + COLON_CHECK("sb:") + if (query) { + sprintf(buf, "ans=%s%s%d", p, co, screen_blank); + goto qry; + } + p += strlen("sb:"); + w = atoi(p); + if (w < 0) w = 0; + rfbLog("process_remote_cmd: setting screen_blank %d -> %d" + " sec.\n", screen_blank, w); + screen_blank = w; + } else if (strstr(p, "screen_blank") == p) { + int w; + COLON_CHECK("screen_blank:") + if (query) { + sprintf(buf, "ans=%s%s%d", p, co, screen_blank); + goto qry; + } + p += strlen("screen_blank:"); + w = atoi(p); + if (w < 0) w = 0; + rfbLog("process_remote_cmd: setting screen_blank %d -> %d" + " sec.\n", screen_blank, w); + screen_blank = w; + + } else if (strstr(p, "fs") == p) { + COLON_CHECK("fs:") + if (query) { + sprintf(buf, "ans=%s%s%f", p, co, fs_frac); + goto qry; + } + p += strlen("fs:"); + fs_frac = atof(p); + rfbLog("process_remote_cmd: setting -fs frac to %f\n", fs_frac); + + } else if (strstr(p, "gaps") == p) { + int g; + COLON_CHECK("gaps:") + if (query) { + sprintf(buf, "ans=%s%s%d", p, co, gaps_fill); + goto qry; + } + p += strlen("gaps:"); + g = atoi(p); + if (g < 0) g = 0; + rfbLog("process_remote_cmd: setting gaps_fill %d -> %d.\n", + gaps_fill, g); + gaps_fill = g; + } else if (strstr(p, "grow") == p) { + int g; + COLON_CHECK("grow:") + if (query) { + sprintf(buf, "ans=%s%s%d", p, co, grow_fill); + goto qry; + } + p += strlen("grow:"); + g = atoi(p); + if (g < 0) g = 0; + rfbLog("process_remote_cmd: setting grow_fill %d -> %d.\n", + grow_fill, g); + grow_fill = g; + } else if (strstr(p, "fuzz") == p) { + int f; + COLON_CHECK("fuzz:") + if (query) { + sprintf(buf, "ans=%s%s%d", p, co, tile_fuzz); + goto qry; + } + p += strlen("fuzz:"); + f = atoi(p); + if (f < 0) f = 0; + rfbLog("process_remote_cmd: setting tile_fuzz %d -> %d.\n", + tile_fuzz, f); + grow_fill = f; + + } else if (strstr(p, "progressive") == p) { + int f; + COLON_CHECK("progressive:") + if (query) { + sprintf(buf, "ans=%s%s%d", p, co, + screen->progressiveSliceHeight); + goto qry; + } + p += strlen("progressive:"); + f = atoi(p); + if (f < 0) f = 0; + rfbLog("process_remote_cmd: setting progressive %d -> %d.\n", + screen->progressiveSliceHeight, f); + screen->progressiveSliceHeight = f; + + } else if (!strcmp(p, "noremote")) { + if (query) { + sprintf(buf, "ans=%s:%d", p, !accept_remote_cmds); + goto qry; + } + rfbLog("process_remote_cmd: disabling remote commands.\n"); + accept_remote_cmds = 0; /* cannot be turned back on. */ + + } else if (query) { + /* read-only variables that can only be queried: */ + + if (!strcmp(p, "display")) { + char *d = DisplayString(dpy); + if (! d) d = "unknown"; + if (*d == ':') { + sprintf(buf, "aro=%s:%s%s", p, this_host(), d); + } else { + sprintf(buf, "aro=%s:%s", p, d); + } + } else if (!strcmp(p, "vncdisplay")) { + sprintf(buf, "aro=%s:%s", p, NONUL(vnc_desktop_name)); + } else if (!strcmp(p, "desktopname")) { + sprintf(buf, "aro=%s:%s", p, NONUL(rfb_desktop_name)); + } else if (!strcmp(p, "desktop")) { + sprintf(buf, "aro=%s:%s", p, NONUL(rfb_desktop_name)); + } else if (!strcmp(p, "auth")) { + sprintf(buf, "aro=%s:%s", p, NONUL(auth_file)); + } else if (!strcmp(p, "rootshift")) { + sprintf(buf, "aro=%s:%d", p, rootshift); + } else if (!strcmp(p, "scale_str")) { + sprintf(buf, "aro=%s:%s", p, NONUL(scale_str)); + } else if (!strcmp(p, "scaled_x")) { + sprintf(buf, "aro=%s:%d", p, scaled_x); + } else if (!strcmp(p, "scaled_y")) { + sprintf(buf, "aro=%s:%d", p, scaled_y); + } else if (!strcmp(p, "scale_numer")) { + sprintf(buf, "aro=%s:%d", p, scale_numer); + } else if (!strcmp(p, "scale_denom")) { + sprintf(buf, "aro=%s:%d", p, scale_denom); + } else if (!strcmp(p, "scale_fac")) { + sprintf(buf, "aro=%s:%f", p, scale_fac); + } else if (!strcmp(p, "scaling_noblend")) { + sprintf(buf, "aro=%s:%d", p, scaling_noblend); + } else if (!strcmp(p, "scaling_nomult4")) { + sprintf(buf, "aro=%s:%d", p, scaling_nomult4); + } else if (!strcmp(p, "scaling_pad")) { + sprintf(buf, "aro=%s:%d", p, scaling_pad); + } else if (!strcmp(p, "scaling_interpolate")) { + sprintf(buf, "aro=%s:%d", p, scaling_interpolate); + } else if (!strcmp(p, "inetd")) { + sprintf(buf, "aro=%s:%d", p, inetd); + } else if (!strcmp(p, "safer")) { + sprintf(buf, "aro=%s:%d", p, safe_remote_only); + } else if (!strcmp(p, "unsafe")) { + sprintf(buf, "aro=%s:%d", p, !safe_remote_only); + } else if (!strcmp(p, "passwdfile")) { + sprintf(buf, "aro=%s:%s", p, NONUL(passwdfile)); + } else if (!strcmp(p, "using_shm")) { + sprintf(buf, "aro=%s:%d", p, !using_shm); + } else if (!strcmp(p, "logfile") || !strcmp(p, "o")) { + sprintf(buf, "aro=%s:%s", p, NONUL(logfile)); + } else if (!strcmp(p, "rc")) { + sprintf(buf, "aro=%s:%s", p, NONUL(rc_rcfile)); + } else if (!strcmp(p, "norc")) { + sprintf(buf, "aro=%s:%d", p, rc_norc); + } else if (!strcmp(p, "h") || !strcmp(p, "help") || + !strcmp(p, "V") || !strcmp(p, "version") || + !strcmp(p, "lastmod")) { + sprintf(buf, "aro=%s:%s", p, NONUL(lastmod)); + } else if (!strcmp(p, "bg")) { + sprintf(buf, "aro=%s:%d", p, opts_bg); + } else if (!strcmp(p, "nofb")) { + sprintf(buf, "aro=%s:%d", p, nofb); + } else if (!strcmp(p, "sigpipe")) { + sprintf(buf, "aro=%s:%s", p, NONUL(sigpipe)); + } else if (!strcmp(p, "threads")) { + sprintf(buf, "aro=%s:%d", p, use_threads); + } else if (!strcmp(p, "clients")) { + char *str = list_clients(); + sprintf(buf, "aro=%s:%s", p, str); + free(str); + } else if (!strcmp(p, "client_count")) { + sprintf(buf, "aro=%s:%d", p, client_count); + } else if (!strcmp(p, "pid")) { + sprintf(buf, "aro=%s:%d", p, (int) getpid()); + } else if (!strcmp(p, "ext_xtest")) { + sprintf(buf, "aro=%s:%d", p, xtest_present); + } else if (!strcmp(p, "ext_xkb")) { + sprintf(buf, "aro=%s:%d", p, xkb_present); + } else if (!strcmp(p, "ext_xshm")) { + sprintf(buf, "aro=%s:%d", p, xshm_present); + } else if (!strcmp(p, "ext_xinerama")) { + sprintf(buf, "aro=%s:%d", p, xinerama_present); + } else if (!strcmp(p, "ext_overlay")) { + sprintf(buf, "aro=%s:%d", p, overlay_present); + } else if (!strcmp(p, "ext_xfixes")) { + sprintf(buf, "aro=%s:%d", p, xfixes_present); + } else if (!strcmp(p, "ext_xdamage")) { + sprintf(buf, "aro=%s:%d", p, xdamage_present); + } else if (!strcmp(p, "ext_xrandr")) { + sprintf(buf, "aro=%s:%d", p, xrandr_present); + } else if (!strcmp(p, "rootwin")) { + sprintf(buf, "aro=%s:0x%x", p, (unsigned int) rootwin); + } else if (!strcmp(p, "num_buttons")) { + sprintf(buf, "aro=%s:%d", p, num_buttons); + } else if (!strcmp(p, "button_mask")) { + sprintf(buf, "aro=%s:%d", p, button_mask); + } else if (!strcmp(p, "mouse_x")) { + sprintf(buf, "aro=%s:%d", p, cursor_x); + } else if (!strcmp(p, "mouse_y")) { + sprintf(buf, "aro=%s:%d", p, cursor_y); + } else if (!strcmp(p, "bpp")) { + sprintf(buf, "aro=%s:%d", p, bpp); + } else if (!strcmp(p, "depth")) { + sprintf(buf, "aro=%s:%d", p, depth); + } else if (!strcmp(p, "indexed_color")) { + sprintf(buf, "aro=%s:%d", p, indexed_color); + } else if (!strcmp(p, "dpy_x")) { + sprintf(buf, "aro=%s:%d", p, dpy_x); + } else if (!strcmp(p, "dpy_y")) { + sprintf(buf, "aro=%s:%d", p, dpy_y); + } else if (!strcmp(p, "rfbport")) { + sprintf(buf, "aro=%s:%d", p, screen->port); + } else if (!strcmp(p, "rfbwait")) { + NOTAPPRO + } else if (!strcmp(p, "rfbauth")) { + NOTAPPRO + } else if (!strcmp(p, "passwd")) { + NOTAPPRO + } else if (!strcmp(p, "alwaysshared")) { + sprintf(buf, "aro=%s:%d", p, screen->alwaysShared); + } else if (!strcmp(p, "dontdisconnect")) { + sprintf(buf, "aro=%s:%d", p, screen->dontDisconnect); + } else if (!strcmp(p, "httpdir")) { + sprintf(buf, "aro=%s:%s", p, NONUL(screen->httpDir)); + } else if (!strcmp(p, "enablehttpproxy")) { + sprintf(buf, "aro=%s:%d", p, + screen->httpEnableProxyConnect); + } else { + NOTAPP + } + goto qry; + } else { + NOTAPP + rfbLog("process_remote_cmd: warning unknown\n"); + rfbLog("command \"%s\"\n", p); + return NULL; + } + + if (*p != '\0' && do_vnc_connect) { + /* this is a reverse connection */ + strncpy(vnc_connect_str, p, VNC_CONNECT_MAX); + vnc_connect_str[VNC_CONNECT_MAX] = '\0'; + } + + done: + return NULL; + + qry: + + if (stringonly) { + return strdup(buf); + } else if (client_connect_file) { + FILE *out = fopen(client_connect_file, "w"); + if (out != NULL) { + fprintf(out, "%s\n", buf); + fclose(out); + usleep(200*1000); + } + } else { + set_vnc_connect_prop(buf); + XFlush(dpy); + } + return NULL; +} + /* -- cursor.c -- */ /* * Here begins a bit of a mess to experiment with multiple cursors @@ -4450,6 +7282,26 @@ typedef struct cursor_info { rfbCursorPtr rfb; } cursor_info_t; +/* empty cursor */ +static char* curs_empty_data = +" " +" "; + +static char* curs_empty_mask = +" " +" "; +static cursor_info_t cur_empty = {NULL, NULL, 2, 2, 0, 0, 0, NULL}; + +/* dot cursor */ +static char* curs_dot_data = +" " +" x"; + +static char* curs_dot_mask = +" " +" x"; +static cursor_info_t cur_dot = {NULL, NULL, 2, 2, 0, 0, 0, NULL}; + /* main cursor */ static char* curs_arrow_data = " " @@ -4642,18 +7494,107 @@ static char* curs_xterm_mask = static cursor_info_t cur_xterm = {NULL, NULL, 16, 16, 8, 8, 1, NULL}; enum cursor_names { - CURS_ARROW = 0, + CURS_EMPTY = 0, + CURS_DOT, + + CURS_ARROW, CURS_ROOT, CURS_WM, - CURS_TERM + CURS_TERM, + + CURS_DYN1, + CURS_DYN2, + CURS_DYN3, + CURS_DYN4, + CURS_DYN5, + CURS_DYN6, + CURS_DYN7, + CURS_DYN8, + CURS_DYN9, + CURS_DYN10, + CURS_DYN11, + CURS_DYN12, + CURS_DYN13, + CURS_DYN14, + CURS_DYN15, + CURS_DYN16 }; -static cursor_info_t *cursors[10]; +#define CURS_DYN_MIN CURS_DYN1 +#define CURS_DYN_MAX CURS_DYN16 +#define CURS_DYN_NUM (CURS_DYN_MAX - CURS_DYN_MIN + 1) + +#define CURS_MAX 32 +static cursor_info_t *cursors[CURS_MAX]; static void setup_cursors(void) { - /* TODO clean this up if we ever do more cursors... */ rfbCursorPtr rfb_curs; int i, n = 0; + static int first = 1; + + rfbLog("setting up %d cursors...\n", CURS_MAX); + + if (first) { + for (i=0; i<CURS_MAX; i++) { + cursors[i] = NULL; + } + } + first = 0; + + if (screen) { + rfbUndrawCursor(screen); + screen->cursor = NULL; + LOCK(screen->cursorMutex); + } + + for (i=0; i<CURS_MAX; i++) { + cursor_info_t *ci; + if (cursors[i]) { + /* clear out any existing ones: */ + ci = cursors[i]; + if (ci->rfb) { + /* this is the rfbCursor part: */ + if (ci->rfb->richSource) { + free(ci->rfb->richSource); + } + if (ci->rfb->source) { + free(ci->rfb->source); + } + if (ci->rfb->mask) { + free(ci->rfb->mask); + } + free(ci->rfb); + } + if (ci->data) { + free(ci->data); + } + if (ci->mask) { + free(ci->mask); + } + } + + /* create new struct: */ + ci = (cursor_info_t *) malloc(sizeof(cursor_info_t)); + ci->data = NULL; + ci->mask = NULL; + ci->wx = 0; + ci->wy = 0; + ci->sx = 0; + ci->sy = 0; + ci->reverse = 0; + ci->rfb = NULL; + cursors[i] = ci; + } + + /* clear any xfixes cursor cache (no freeing is done) */ + get_xfixes_cursor(1); + + /* manually fill in the data+masks: */ + cur_empty.data = curs_empty_data; + cur_empty.mask = curs_empty_mask; + + cur_dot.data = curs_dot_data; + cur_dot.mask = curs_dot_mask; cur_arrow.data = curs_arrow_data; cur_arrow.mask = curs_arrow_mask; @@ -4670,12 +7611,16 @@ static void setup_cursors(void) { cur_xterm.data = curs_xterm_data; cur_xterm.mask = curs_xterm_mask; + cursors[CURS_EMPTY] = &cur_empty; n++; + cursors[CURS_DOT] = &cur_dot; n++; cursors[CURS_ARROW] = &cur_arrow; n++; cursors[CURS_ROOT] = &cur_root; n++; cursors[CURS_WM] = &cur_fleur; n++; cursors[CURS_TERM] = &cur_xterm; n++; for (i=0; i<n; i++) { + /* create rfbCursors for the special cursors: */ + cursor_info_t *ci = cursors[i]; ci->data = strdup(ci->data); @@ -4698,8 +7643,49 @@ static void setup_cursors(void) { rfb_curs->cleanupMask = FALSE; rfb_curs->cleanupRichSource = FALSE; + if (bpp == 8 && indexed_color) { + /* + * use richsource in PseudoColor for better + * looking cursors (i.e. two-color). + */ + int x, y, k = 0, bw; + char d, m; + int black = BlackPixel(dpy, scr); + int white = WhitePixel(dpy, scr); + + rfb_curs->richSource + = (char *)calloc(ci->wx * ci->wy, 1); + + for (y = 0; y < ci->wy; y++) { + for (x = 0; x < ci->wx; x++) { + d = *(ci->data + k); + m = *(ci->mask + k); + if (d == ' ' && m == ' ') { + k++; + continue; + } else if (m != ' ' && d == ' ') { + bw = black; + } else { + bw = white; + } + if (ci->reverse) { + if (bw == black) { + bw = white; + } else { + bw = black; + } + } + *(rfb_curs->richSource+k) = (unsigned char) bw; + k++; + } + } + } ci->rfb = rfb_curs; } + if (screen) { + UNLOCK(screen->cursorMutex); + } + rfbLog(" done.\n"); } typedef struct win_str_info { @@ -4722,15 +7708,14 @@ void tree_descend_cursor(int *depth, Window *w, win_str_info_t *winfo) { unsigned int mask; Window wins[10]; int descend, maxtries = 10; - char *name, a; + char *name, *s = multiple_cursors_mode; static XClassHint *classhint = NULL; int nm_info = 1; XErrorHandler old_handler; X_LOCK; - a = multiple_cursors_mode[0]; - if (a == 'd' || a == 'X') { + if (!strcmp(s, "default") || !strcmp(s, "X") || !strcmp(s, "arrow")) { nm_info = 0; } @@ -4814,6 +7799,304 @@ void tree_descend_cursor(int *depth, Window *w, win_str_info_t *winfo) { *w = wins[descend]; } +void initialize_xfixes(void) { +#ifdef LIBVNCSERVER_HAVE_LIBXFIXES + if (xfixes_present) { + if (use_xfixes) { + XFixesSelectCursorInput(dpy, rootwin, + XFixesDisplayCursorNotifyMask); + } else { + XFixesSelectCursorInput(dpy, rootwin, 0); + } + } +#endif +} + +int get_xfixes_cursor(int init) { + static unsigned long last_cursor = 0; + static int last_index = 0; + static time_t curs_times[CURS_MAX]; + static unsigned long curs_index[CURS_MAX]; + int which = CURS_ARROW; + + if (init) { + /* zero out our cache (cursors are not freed) */ + int i; + for (i=0; i<CURS_MAX; i++) { + curs_times[i] = 0; + curs_index[i] = 0; + } + last_cursor = 0; + last_index = 0; + return -1; + } + + if (xfixes_present) { +#ifdef LIBVNCSERVER_HAVE_LIBXFIXES + int use, oldest, i, x, y, w, h, len; + int Bpp = bpp/8; + time_t oldtime, now; + char *bitmap, *rich; + unsigned long black, white; + rfbCursorPtr c; + XFixesCursorImage *xfc; + + if (! got_xfixes_cursor_notify) { + /* try again for XFixesCursorNotify event */ + XEvent xev; + X_LOCK; + if (XCheckTypedEvent(dpy, xfixes_base_event_type + + XFixesCursorNotify, &xev)) { + got_xfixes_cursor_notify++; + } + X_UNLOCK; + } + if (! got_xfixes_cursor_notify) { + /* evidently no cursor change, just return last one */ + if (last_index) { + return last_index; + } else { + return CURS_ARROW; + } + } + got_xfixes_cursor_notify = 0; + + /* retrieve the cursor info + pixels from server: */ + X_LOCK; + xfc = XFixesGetCursorImage(dpy); + X_UNLOCK; + if (! xfc) { + /* failure. */ + return(which); + } + + if (xfc->cursor_serial == last_cursor) { + /* same serial index: no change */ + X_LOCK; + XFree(xfc); + X_UNLOCK; + if (last_index) { + return last_index; + } else { + return CURS_ARROW; + } + } + + oldest = CURS_DYN_MIN; + oldtime = curs_times[oldest]; + now = time(0); + for (i = CURS_DYN_MIN; i <= CURS_DYN_MAX; i++) { + if (curs_times[i] < oldtime) { + /* watch for oldest one to overwrite */ + oldest = i; + oldtime = curs_times[i]; + } + if (xfc->cursor_serial == curs_index[i]) { + /* + * got a hit with an existing cursor, + * use that one. + */ + last_cursor = curs_index[i]; + curs_times[i] = now; + last_index = i; + X_LOCK; + XFree(xfc); + X_UNLOCK; + return last_index; + } + } + + if (screen) { + rfbUndrawCursor(screen); + } + /* we need to create the cursor and overwrite oldest */ + use = oldest; + if (cursors[use]->rfb) { + /* clean up oldest if it exists */ + if (cursors[use]->rfb->richSource) { + free(cursors[use]->rfb->richSource); + } + if (cursors[use]->rfb->source) { + free(cursors[use]->rfb->source); + } + if (cursors[use]->rfb->mask) { + free(cursors[use]->rfb->mask); + } + free(cursors[use]->rfb); + } + + X_LOCK; + black = BlackPixel(dpy, scr); + white = WhitePixel(dpy, scr); + X_UNLOCK; + + w = xfc->width; + h = xfc->height; + len = w * h; + + /* for bitmap data */ + bitmap = (char *)malloc(len+1); + bitmap[len] = '\0'; + + /* for rich cursor pixel data */ + rich = (char *)calloc(Bpp*len, 1); + + i = 0; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + unsigned long r, g, b, a; + unsigned int ui; + char *p; + + a = 0xff000000 & (*(xfc->pixels+i)); + a = a >> 24; /* alpha channel */ + + if (a == 0) { + bitmap[i] = ' '; + i++; + continue; + } else { + bitmap[i] = 'x'; + } + + r = 0x00ff0000 & (*(xfc->pixels+i)); + g = 0x0000ff00 & (*(xfc->pixels+i)); + b = 0x000000ff & (*(xfc->pixels+i)); + r = r >> 16; /* red */ + g = g >> 8; /* green */ + b = b >> 0; /* blue */ + + if (indexed_color) { + /* + * Choose black or white for + * PseudoColor case. + */ + int value = (r+g+b)/3; + if (value > 127) { + ui = white; + } else { + ui = black; + } + } else { + /* + * Otherwise map the RGB data onto + * the framebuffer format: + */ + r = (main_red_max * r)/255; + g = (main_green_max * g)/255; + b = (main_blue_max * b)/255; + ui = 0; + ui |= (r << main_red_shift); + ui |= (g << main_green_shift); + ui |= (b << main_blue_shift); + } + + /* insert value into rich source: */ + p = rich + Bpp*i; + if (Bpp == 1) { + *((unsigned char *)p) + = (unsigned char) ui; + } else if (Bpp == 2) { + *((unsigned short *)p) + = (unsigned short) ui; + } else if (Bpp == 4) { + *((unsigned int *)p) + = (unsigned int) ui; + } + i++; + } + } + + /* create the cursor with the bitmap: */ + c = rfbMakeXCursor(w, h, bitmap, bitmap); + free(bitmap); + + /* set up the cursor parameters: */ + c->xhot = xfc->xhot; + c->yhot = xfc->yhot; + c->cleanup = FALSE; + c->cleanupSource = FALSE; + c->cleanupMask = FALSE; + c->cleanupRichSource = FALSE; + c->richSource = rich; + + /* place cursor into our collection */ + cursors[use]->rfb = c; + + /* update time and serial index: */ + curs_times[use] = now; + curs_index[use] = xfc->cursor_serial; + last_index = use; + last_cursor = xfc->cursor_serial; + + which = last_index; + + X_LOCK; + XFree(xfc); + X_UNLOCK; +#endif + } + return(which); +} + +int known_cursors_mode(char *s) { +/* + * default: see initialize_cursors_mode() for default behavior. + * arrow: unchanging white arrow. + * Xn*: show X on root background. Optional n sets treedepth. + * some: do the heuristics for root, wm, term detection. + * most: if display have overlay or xfixes, show all cursors, + * otherwise do the same as "some" + * none: show no cursor. + */ + if (strcmp(s, "default") && strcmp(s, "arrow") && *s != 'X' && + strcmp(s, "some") && strcmp(s, "most") && strcmp(s, "none")) { + return 0; + } else { + return 1; + } +} + +void initialize_cursors_mode(void) { + char *s = multiple_cursors_mode; + if (!s || !known_cursors_mode(s)) { + rfbLog("unknown cursors mode: %s\n", s); + rfbLog("resetting cursors mode to \"default\"\n"); + if (multiple_cursors_mode) free(multiple_cursors_mode); + multiple_cursors_mode = strdup("default"); + s = multiple_cursors_mode; + } + if (!strcmp(s, "none")) { + show_cursor = 0; + } else { + /* we do NOT set show_cursor = 1, let the caller do that */ + } + + show_multiple_cursors = 0; + if (show_cursor) { + if (!strcmp(s, "default")) { + if(multiple_cursors_mode) free(multiple_cursors_mode); + multiple_cursors_mode = strdup("X"); + s = multiple_cursors_mode; + } + if (*s == 'X' || !strcmp(s, "some") || !strcmp(s, "most")) { + show_multiple_cursors = 1; + } else { + show_multiple_cursors = 0; + /* hmmm, some bug going back to arrow mode.. */ + set_rfb_cursor(CURS_ARROW); + } + if (screen) { + set_cursor_was_changed(screen); + } + } else { + if (screen) { + screen->cursor = NULL; /* dangerous? */ + set_cursor_was_changed(screen); + } + } +} + int get_which_cursor(void) { int which = CURS_ARROW; @@ -4824,14 +8107,26 @@ int get_which_cursor(void) { Window win; XErrorHandler old_handler; int mode = 0; - char a = multiple_cursors_mode[0]; - if (a == 'd') { + if (drag_in_progress) { + return -1; + } + + if (!strcmp(multiple_cursors_mode, "arrow")) { + /* should not happen... */ + return CURS_ARROW; + } else if (!strcmp(multiple_cursors_mode, "default")) { mode = 0; - } else if (a == 'X') { + } else if (!strcmp(multiple_cursors_mode, "X")) { mode = 1; - } else { + } else if (!strcmp(multiple_cursors_mode, "some")) { mode = 2; + } else if (!strcmp(multiple_cursors_mode, "most")) { + mode = 3; + } + + if (mode == 3 && xfixes_present && use_xfixes) { + return get_xfixes_cursor(0); } if (depth_cutoff < 0) { @@ -4852,11 +8147,13 @@ int get_which_cursor(void) { tree_descend_cursor(&depth, &win, &winfo); - if (depth <= depth_cutoff) { + if (depth <= depth_cutoff && !subwin) { which = CURS_ROOT; - } else if (mode >= 2) { + + } else if (mode == 2 || mode == 3) { int which0 = which; + /* apply crude heuristics to choose a cursor... */ if (win) { int ratio = 10, x, y; unsigned int w, h, bw, d; @@ -4864,6 +8161,8 @@ int get_which_cursor(void) { trapped_xerror = 0; old_handler = XSetErrorHandler(trap_xerror); + + /* "narrow" windows are WM */ if (XGetGeometry(dpy, win, &r, &x, &y, &w, &h, &bw, &d)) { if (w > ratio * h || h > ratio * w) { @@ -4874,12 +8173,17 @@ int get_which_cursor(void) { trapped_xerror = 0; } if (which == which0) { + /* the string "term" mean I-beam. */ lowercase(winfo.res_name); lowercase(winfo.res_class); if (strstr(winfo.res_name, "term")) { which = CURS_TERM; } else if (strstr(winfo.res_class, "term")) { which = CURS_TERM; + } else if (strstr(winfo.res_name, "text")) { + which = CURS_TERM; + } else if (strstr(winfo.res_class, "text")) { + which = CURS_TERM; } } } @@ -4948,12 +8252,52 @@ void set_cursor_was_moved(rfbScreenInfoPtr s) { rfbReleaseClientIterator(iter); } -void unset_cursor_shape_updates(rfbScreenInfoPtr s) { +void restore_cursor_shape_updates(rfbScreenInfoPtr s) { rfbClientIteratorPtr iter; rfbClientPtr cl; + int count = 0; iter = rfbGetClientIterator(s); while( (cl = rfbClientIteratorNext(iter)) ) { + int changed = 0; + ClientData *cd = (ClientData *) cl->clientData; + + if (cd->had_cursor_shape_updates) { + rfbLog("restoring enableCursorShapeUpdates for client" + " 0x%x\n", cl); + cl->enableCursorShapeUpdates = TRUE; + changed = 1; + } + if (cd->had_cursor_pos_updates) { + rfbLog("restoring enableCursorPosUpdates for client" + " 0x%x\n", cl); + cl->enableCursorPosUpdates = TRUE; + changed = 1; + } + if (changed) { + cl->cursorWasChanged = TRUE; + count++; + } + } + rfbReleaseClientIterator(iter); +} + +void disable_cursor_shape_updates(rfbScreenInfoPtr s) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + + iter = rfbGetClientIterator(s); + while( (cl = rfbClientIteratorNext(iter)) ) { + ClientData *cd; + cd = (ClientData *) cl->clientData; + + if (cl->enableCursorShapeUpdates) { + cd->had_cursor_shape_updates = 1; + } + if (cl->enableCursorPosUpdates) { + cd->had_cursor_pos_updates = 1; + } + cl->enableCursorShapeUpdates = FALSE; cl->enableCursorPosUpdates = FALSE; cl->cursorWasChanged = FALSE; @@ -4961,14 +8305,14 @@ void unset_cursor_shape_updates(rfbScreenInfoPtr s) { rfbReleaseClientIterator(iter); } -int cursor_pos_updates_clients(rfbScreenInfoPtr s) { +int cursor_shape_updates_clients(rfbScreenInfoPtr s) { rfbClientIteratorPtr iter; rfbClientPtr cl; int count = 0; iter = rfbGetClientIterator(s); while( (cl = rfbClientIteratorNext(iter)) ) { - if (cl->enableCursorPosUpdates) { + if (cl->enableCursorShapeUpdates) { count++; } } @@ -4976,14 +8320,14 @@ int cursor_pos_updates_clients(rfbScreenInfoPtr s) { return count; } -int cursor_shape_updates_clients(rfbScreenInfoPtr s) { +int cursor_pos_updates_clients(rfbScreenInfoPtr s) { rfbClientIteratorPtr iter; rfbClientPtr cl; int count = 0; iter = rfbGetClientIterator(s); while( (cl = rfbClientIteratorNext(iter)) ) { - if (cl->enableCursorShapeUpdates) { + if (cl->enableCursorPosUpdates) { count++; } } @@ -5074,7 +8418,7 @@ void cursor_position(int x, int y) { } void set_rfb_cursor(int which) { - int workaround = 1; /* if rfbSetCursor does not mark modified */ + int workaround = 2; /* if rfbSetCursor does not mark modified */ if (! show_cursor) { return; @@ -5101,12 +8445,21 @@ void set_rfb_cursor(int which) { } } - rfbSetCursor(screen, cursors[which]->rfb, FALSE); + if (!cursors[which] || !cursors[which]->rfb) { + rfbLog("non-existent cursor: which=%d\n", which); + return; + } else { + rfbSetCursor(screen, cursors[which]->rfb, FALSE); + } /* this is a 2nd workaround for rfbSetCursor() */ - if (screen->underCursorBuffer == NULL && - screen->underCursorBufferLen != 0) { - screen->underCursorBufferLen = 0; + if (workaround > 1) { + if (screen->underCursorBuffer == NULL && + screen->underCursorBufferLen != 0) { + LOCK(screen->cursorMutex); + screen->underCursorBufferLen = 0; + UNLOCK(screen->cursorMutex); + } } if (workaround) { @@ -5114,8 +8467,15 @@ void set_rfb_cursor(int which) { } } +void set_no_cursor(void) { + set_rfb_cursor(CURS_EMPTY); +} + void set_cursor(int x, int y, int which) { static int last = -1; + if (which < 0) { + which = last; + } if (last < 0 || which != last) { set_rfb_cursor(which); } @@ -5172,13 +8532,20 @@ void check_x11_pointer(void) { * the clients and dynamically if -flashcmap is specified. */ #define NCOLOR 256 -void set_colormap(void) { +void set_colormap(int reset) { static int first = 1; static XColor color[NCOLOR], prev[NCOLOR]; Colormap cmap; Visual *vis; int i, ncells, diffs = 0; + if (reset) { + first = 1; + if (screen->colourMap.data.shorts) { + free(screen->colourMap.data.shorts); + } + } + if (first) { screen->colourMap.count = NCOLOR; screen->serverFormat.trueColour = FALSE; @@ -5300,8 +8667,13 @@ void set_visual(char *str) { XVisualInfo vinfo; char *p, *vstring = strdup(str); - if (! quiet) { - fprintf(stderr, "set_visual: %s\n", vstring); + visual_id = (VisualID) 0; + visual_depth = 0; + + if (!strcmp(vstring, "ignore") || !strcmp(vstring, "default") + || !strcmp(vstring, "")) { + free(vstring); + return; } /* set visual depth */ @@ -5312,6 +8684,11 @@ void set_visual(char *str) { } else { vdepth = defdepth; } + if (! quiet) { + fprintf(stderr, "\nVisual Info:\n"); + fprintf(stderr, " set_visual(\"%s\")\n", str); + fprintf(stderr, " visual_depth: %d\n", vdepth); + } /* set visual id number */ if (strcmp(vstring, "StaticGray") == 0) { @@ -5333,21 +8710,22 @@ void set_visual(char *str) { visual_id = (VisualID) v_in; return; } - fprintf(stderr, "bad -visual arg: %s\n", vstring); - exit(1); + rfbLog("bad -visual arg: %s\n", vstring); + clean_up_exit(1); } visual_id = (VisualID) v_in; free(vstring); return; } + if (! quiet) fprintf(stderr, " visual: %d\n", vis); if (XMatchVisualInfo(dpy, scr, visual_depth, vis, &vinfo)) { ; } else if (XMatchVisualInfo(dpy, scr, defdepth, vis, &vinfo)) { ; } else { - fprintf(stderr, "could not find visual: %s\n", vstring); - exit(1); + rfbLog("could not find visual: %s\n", vstring); + clean_up_exit(1); } free(vstring); @@ -5369,6 +8747,7 @@ void nofb_hook(rfbClientPtr cl) { return; } rfbLog("framebuffer requested in -nofb mode by client %s\n", cl->host); + /* ignore xrandr */ fb = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap); main_fb = fb->data; rfb_fb = main_fb; @@ -5377,19 +8756,437 @@ void nofb_hook(rfbClientPtr cl) { screen->displayHook = NULL; } +void do_new_fb(int reset_mem) { + XImage *fb; + char *old_main = main_fb; + char *old_rfb = rfb_fb; + + /* for threaded we really should lock libvncserver out. */ + if (use_threads) { + rfbLog("warning: changing framebuffers while threaded may\n"); + rfbLog(" not work, do not use -threads if problems arise.\n"); + } + + if (reset_mem) { + clean_shm(0); + free_tiles(); + } + + fb = initialize_xdisplay_fb(); + + initialize_screen(NULL, NULL, fb); + + if (reset_mem) { + initialize_tiles(); + initialize_blackouts_and_xinerama(); + initialize_polling_images(); + } + + if (old_main != old_rfb && old_main) { + free(old_main); + } + if (old_rfb) { + free(old_rfb); + } + fb0 = fb; +} + +void remove_fake_fb(void) { + if (! screen) { + return; + } + rfbLog("removing fake fb: 0x%x\n", fake_fb); + + do_new_fb(1); + + /* + * fake_fb is freed in do_new_fb(), but we set to NULL here to + * indicate it is gone. + */ + fake_fb = NULL; +} + +void install_fake_fb(int w, int h, int bpp) { + int bpc; + if (! screen) { + return; + } + if (fake_fb) { + free(fake_fb); + } + fake_fb = (char *) calloc(w*h*bpp/8, 1); + if (! fake_fb) { + rfbLog("could not create fake fb: %dx%d %d\n", w, h, bpp); + return; + } + bpc = guess_bits_per_color(bpp); + rfbLog("installing fake fb: %dx%d %d\n", w, h, bpp); + rfbLog("rfbNewFramebuffer(0x%x, 0x%x, %d, %d, %d, %d, %d)\n", + screen, fake_fb, w, h, bpc, 1, bpp/8); + + rfbNewFramebuffer(screen, fake_fb, w, h, bpc, 1, bpp/8); +} + +void check_padded_fb(void) { + if (! fake_fb) { + return; + } + if (time(0) > pad_geometry_time+1 && all_clients_initialized()) { + remove_fake_fb(); + } +} + +void install_padded_fb(char *geom) { + int w, h; + int ok = 1; + if (! geom || *geom == '\0') { + ok = 0; + } else if (sscanf(geom, "%dx%d", &w, &h) != 2) { + ok = 0; + } + w = nabs(w); + h = nabs(h); + + if (w < 5) w = 5; + if (h < 5) h = 5; + + if (!ok) { + rfbLog("skipping invalid pad geometry: '%s'\n", NONUL(geom)); + return; + } + install_fake_fb(w, h, bpp); + pad_geometry_time = time(0); +} + /* - * initialize the rfb framebuffer/screen + * initialize a fb for the X display */ -void initialize_screen(int *argc, char **argv, XImage *fb) { - int have_masks = 0; - int width = fb->width; - int height = fb->height; +XImage *initialize_xdisplay_fb(void) { + XImage *fb; + char *vis_str = visual_str; + int try = 0, subwin_tries = 3; + XErrorHandler old_handler; + int subwin_bs; + + X_LOCK; + if (subwin && !valid_window((Window) subwin)) { + rfbLog("invalid sub-window: 0x%lx\n", subwin); + X_UNLOCK; + clean_up_exit(1); + } - main_bytes_per_line = fb->bytes_per_line; + if (overlay) { + /* + * ideally we'd like to not have to cook up the + * visual variables but rather let it all come out + * of XReadScreen(), however there is no way to get + * a default visual out of it, so we pretend -visual + * TrueColor:NN was supplied with NN usually 24. + */ + char str[32]; + Window twin = subwin ? subwin : rootwin; + XImage *xi; + + xi = xreadscreen(dpy, twin, 0, 0, 8, 8, False); + sprintf(str, "TrueColor:%d", xi->depth); + if (xi->depth != 24 && ! quiet) { + rfbLog("warning: overlay image has depth %d " + "instead of 24.\n", xi->depth); + } + XDestroyImage(xi); + if (visual_str != NULL && ! quiet) { + rfbLog("warning: replacing '-visual %s' by '%s' " + "for use with -overlay\n", visual_str, str); + } + vis_str = strdup(str); + } + + if (vis_str != NULL) { + set_visual(vis_str); + if (vis_str != visual_str) { + free(vis_str); + } + } + + /* set up parameters for subwin or non-subwin cases: */ + + if (! subwin) { + /* full screen */ + window = rootwin; + dpy_x = DisplayWidth(dpy, scr); + dpy_y = DisplayHeight(dpy, scr); + off_x = 0; + off_y = 0; + /* this may be overridden via visual_id below */ + default_visual = DefaultVisual(dpy, scr); + } else { + /* single window */ + XWindowAttributes attr; + + window = (Window) subwin; + if (! XGetWindowAttributes(dpy, window, &attr)) { + rfbLog("invalid window: 0x%lx\n", window); + X_UNLOCK; + clean_up_exit(1); + } + dpy_x = attr.width; + dpy_y = attr.height; + + subwin_bs = attr.backing_store; + + /* this may be overridden via visual_id below */ + default_visual = attr.visual; + + X_UNLOCK; + set_offset(); + X_LOCK; + } + + /* initialize depth to reasonable value, visual_id may override */ + depth = DefaultDepth(dpy, scr); + + if (visual_id) { + int n; + XVisualInfo vinfo_tmpl, *vinfo; + + /* + * we are in here from -visual or -overlay options + * visual_id and visual_depth were set in set_visual(). + */ + + vinfo_tmpl.visualid = visual_id; + vinfo = XGetVisualInfo(dpy, VisualIDMask, &vinfo_tmpl, &n); + if (vinfo == NULL || n == 0) { + rfbLog("could not match visual_id: 0x%x\n", + (int) visual_id); + X_UNLOCK; + clean_up_exit(1); + } + default_visual = vinfo->visual; + depth = vinfo->depth; + if (visual_depth) { + /* force it from -visual MooColor:NN */ + depth = visual_depth; + } + if (! quiet) { + fprintf(stderr, " initialize_xdisplay_fb()\n"); + fprintf(stderr, " Visual*: 0x%x\n", + (int) vinfo->visual); + fprintf(stderr, " visualid: 0x%x\n", + (int) vinfo->visualid); + fprintf(stderr, " screen: %d\n", vinfo->screen); + fprintf(stderr, " depth: %d\n", vinfo->depth); + fprintf(stderr, " class: %d\n", vinfo->class); + fprintf(stderr, " red_mask: 0x%08lx %s\n", + vinfo->red_mask, bitprint(vinfo->red_mask, 32)); + fprintf(stderr, " green_mask: 0x%08lx %s\n", + vinfo->green_mask, bitprint(vinfo->green_mask, 32)); + fprintf(stderr, " blue_mask: 0x%08lx %s\n", + vinfo->blue_mask, bitprint(vinfo->blue_mask, 32)); + fprintf(stderr, " cmap_size: %d\n", + vinfo->colormap_size); + fprintf(stderr, " bits b/rgb: %d\n", + vinfo->bits_per_rgb); + fprintf(stderr, "\n"); + } + XFree(vinfo); + } + + if (! quiet) { + rfbLog("default visual ID: 0x%x\n", + (int) XVisualIDFromVisual(default_visual)); + } + + again: + if (subwin) { + int shift = 0; + int subwin_x, subwin_y; + int disp_x = DisplayWidth(dpy, scr); + int disp_y = DisplayHeight(dpy, scr); + Window twin; + /* subwins can be a dicey if they are changing size... */ + XTranslateCoordinates(dpy, window, rootwin, 0, 0, &subwin_x, + &subwin_y, &twin); + if (subwin_x + dpy_x > disp_x) { + shift = 1; + subwin_x = disp_x - dpy_x - 3; + } + if (subwin_y + dpy_y > disp_y) { + shift = 1; + subwin_y = disp_y - dpy_y - 3; + } + if (subwin_x < 0) { + shift = 1; + subwin_x = 1; + } + if (subwin_y < 0) { + shift = 1; + subwin_y = 1; + } + + trapped_xerror = 0; + old_handler = XSetErrorHandler(trap_xerror); + if (shift) { + XMoveWindow(dpy, window, subwin_x, subwin_y); + } + XMapRaised(dpy, window); + XRaiseWindow(dpy, window); + XFlush(dpy); + } + try++; + + if (nofb) { + /* + * For -nofb we do not allocate the framebuffer, so we + * can save a few MB of memory. + */ + fb = XCreateImage_wr(dpy, default_visual, depth, ZPixmap, + 0, NULL, dpy_x, dpy_y, BitmapPad(dpy), 0); + + } else if (visual_id) { + /* + * we need to call XCreateImage to supply the visual + */ + fb = XCreateImage_wr(dpy, default_visual, depth, ZPixmap, + 0, NULL, dpy_x, dpy_y, BitmapPad(dpy), 0); + fb->data = (char *) malloc(fb->bytes_per_line * fb->height); - main_red_mask = fb->red_mask; - main_green_mask = fb->green_mask; - main_blue_mask = fb->blue_mask; + } else { + fb = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, + ZPixmap); + if (! quiet) { + rfbLog("Read initial data from X display into" + " framebuffer.\n"); + } + } + + if (subwin) { + XSetErrorHandler(old_handler); + if (trapped_xerror) { + rfbLog("trapped GetImage at SUBWIN creation.\n"); + if (try < subwin_tries) { + usleep(250 * 1000); + if (!get_window_size(window, &dpy_x, &dpy_y)) { + rfbLog("could not get size of subwin " + "0x%lx\n", subwin); + X_UNLOCK; + clean_up_exit(1); + } + goto again; + } + } + trapped_xerror = 0; + + } else if (! fb && try == 1) { + /* try once more */ + usleep(250 * 1000); + goto again; + } + X_UNLOCK; + + if (fb->bits_per_pixel == 24 && ! quiet) { + rfbLog("warning: 24 bpp may have poor performance.\n"); + } + return fb; +} + +void parse_scale_string(char *str) { + int m, n; + char *p; + double f; + + scale_fac = 1.0; + scaling = 0; + scaling_noblend = 0; + scaling_nomult4 = 0; + scaling_pad = 0; + scaling_interpolate = 0; + scaled_x = 0, scaled_y = 0; + scale_numer = 0, scale_denom = 0; + + if (str == NULL || str[0] == '\0') { + return; + } + + if ( (p = strchr(str, ':')) != NULL) { + /* options */ + if (strstr(p+1, "nb") != NULL) { + scaling_noblend = 1; + } + if (strstr(p+1, "n4") != NULL) { + scaling_nomult4 = 1; + } + if (strstr(p+1, "in") != NULL) { + scaling_interpolate = 1; + } + if (strstr(p+1, "pad") != NULL) { + scaling_pad = 1; + } + *p = '\0'; + } + if (strchr(str, '.') != NULL) { + double test, diff, eps = 1.0e-7; + if (sscanf(str, "%lf", &f) != 1) { + rfbLog("bad -scale arg: %s\n", str); + clean_up_exit(1); + } + scale_fac = (double) f; + /* look for common fractions from small ints: */ + for (n=2; n<=10; n++) { + for (m=1; m<n; m++) { + test = ((double) m)/ n; + diff = scale_fac - test; + if (-eps < diff && diff < eps) { + scale_numer = m; + scale_denom = n; + break; + + } + } + if (scale_denom) { + break; + } + } + if (scale_fac < 0.01) { + rfbLog("-scale factor too small: %f\n", scale_fac); + clean_up_exit(1); + } + } else { + if (sscanf(str, "%d/%d", &m, &n) != 2) { + if (sscanf(str, "%d", &m) != 1) { + rfbLog("bad -scale arg: %s\n", str); + clean_up_exit(1); + } else { + /* e.g. -scale 1 or -scale 2 */ + n = 1; + } + } + if (n <= 0 || m <=0) { + rfbLog("bad -scale arg: %s\n", str); + clean_up_exit(1); + } + scale_fac = ((double) m)/ n; + if (scale_fac < 0.01) { + rfbLog("-scale factor too small: %f\n", scale_fac); + clean_up_exit(1); + } + scale_numer = m; + scale_denom = n; + } + if (scale_fac == 1.0) { + if (! quiet) { + rfbLog("scaling disabled for factor %f\n", scale_fac); + } + } else { + scaling = 1; + } +} + +void setup_scaling(int *width_in, int *height_in) { + int width = *width_in; + int height = *height_in; + + parse_scale_string(scale_str); if (scaling) { double eps = 0.000001; @@ -5439,20 +9236,60 @@ void initialize_screen(int *argc, char **argv, XImage *fb) { } scaled_x = width; scaled_y = height; - rfb_bytes_per_line = (main_bytes_per_line / fb->width) * width; + + *width_in = width; + *height_in = height; + } +} + +/* + * initialize the rfb framebuffer/screen + */ +void initialize_screen(int *argc, char **argv, XImage *fb) { + int have_masks = 0; + int width = fb->width; + int height = fb->height; + int create_screen = screen ? 0 : 1; + int bits_per_color; + + main_bytes_per_line = fb->bytes_per_line; + + setup_scaling(&width, &height); + + if (scaling) { rfbLog("scaling screen: %dx%d -> %dx%d scale_fac=%.5f\n", fb->width, fb->height, scaled_x, scaled_y, scale_fac); + + rfb_bytes_per_line = (main_bytes_per_line / fb->width) * width; } else { rfb_bytes_per_line = main_bytes_per_line; } - screen = rfbGetScreen(argc, argv, width, height, fb->bits_per_pixel, - 8, fb->bits_per_pixel/8); + /* + * These are just hints wrt pixel format just to let + * rfbGetScreen/rfbNewFramebuffer proceed with reasonable + * defaults. We manually set them in painful detail + * below. + */ + bits_per_color = guess_bits_per_color(fb->bits_per_pixel); - if (! quiet) { - fprintf(stderr, "\n"); - } + /* n.b. samplesPerPixel (set = 1 here) seems to be unused. */ + if (create_screen) { + screen = rfbGetScreen(argc, argv, width, height, + bits_per_color, 1, (int) fb->bits_per_pixel/8); + } else { + /* set set frameBuffer member below. */ + rfbLog("rfbNewFramebuffer(0x%x, 0x%x, %d, %d, %d, %d, %d)\n", + screen, NULL, width, height, + bits_per_color, 1, fb->bits_per_pixel/8); + /* these are probably overwritten, but just to be safe: */ + screen->bitsPerPixel = fb->bits_per_pixel; + screen->depth = fb->depth; + + rfbNewFramebuffer(screen, NULL, width, height, + bits_per_color, 1, (int) fb->bits_per_pixel/8); + } if (! screen) { int i; rfbLog("\n"); @@ -5468,11 +9305,11 @@ void initialize_screen(int *argc, char **argv, XImage *fb) { * the x11vnc.c file by itself and plop it down into their libvncserver tree. * Remove at some point. BTW, this assumes no usage of earlier "0.7pre". */ -#if NON_CVS && defined(LIBVNCSERVER_VERSION) +#if OLD_TREE && defined(LIBVNCSERVER_VERSION) if (strcmp(LIBVNCSERVER_VERSION, "0.6")) #endif { - if (*argc != 1) { + if (create_screen && *argc != 1) { int i; rfbLog("*** unrecognized option(s) ***\n"); for (i=1; i< *argc; i++) { @@ -5491,23 +9328,45 @@ void initialize_screen(int *argc, char **argv, XImage *fb) { rfbLog("renamed: -mouseX, use -cursor X\n"); rfbLog("renamed: -X, use -cursor X\n"); rfbLog("renamed: -nomouse, use -nocursor\n"); + rfbLog("renamed: -old_pointer, use -pointer_mode 1\n"); clean_up_exit(1); } } + /* set up format from scratch: */ screen->paddedWidthInBytes = rfb_bytes_per_line; screen->serverFormat.bitsPerPixel = fb->bits_per_pixel; screen->serverFormat.depth = fb->depth; - screen->serverFormat.trueColour = (uint8_t) TRUE; + screen->serverFormat.trueColour = TRUE; + + screen->serverFormat.redShift = 0; + screen->serverFormat.greenShift = 0; + screen->serverFormat.blueShift = 0; + screen->serverFormat.redMax = 0; + screen->serverFormat.greenMax = 0; + screen->serverFormat.blueMax = 0; + + /* these main_* formats are use elsewhere by us. */ + main_red_shift = 0; + main_green_shift = 0; + main_blue_shift = 0; + main_red_max = 0; + main_green_max = 0; + main_blue_max = 0; + main_red_mask = fb->red_mask; + main_green_mask = fb->green_mask; + main_blue_mask = fb->blue_mask; + + have_masks = ((fb->red_mask|fb->green_mask|fb->blue_mask) != 0); if (force_indexed_color) { have_masks = 0; } - if ( ! have_masks && screen->serverFormat.bitsPerPixel == 8 - && CellsOfScreen(ScreenOfDisplay(dpy,scr)) ) { - /* indexed colour */ + if (! have_masks && screen->serverFormat.bitsPerPixel == 8 + && CellsOfScreen(ScreenOfDisplay(dpy, scr))) { + /* indexed color */ if (!quiet) { rfbLog("X display %s is 8bpp indexed color\n", DisplayString(dpy)); @@ -5522,53 +9381,115 @@ void initialize_screen(int *argc, char **argv, XImage *fb) { rfbLog("\n"); } } - indexed_colour = 1; - set_colormap(); + screen->serverFormat.trueColour = FALSE; + indexed_color = 1; + set_colormap(1); } else { - /* general case ... */ + /* + * general case, we call it truecolor, but could be direct + * color, static color, etc.... + */ if (! quiet) { rfbLog("X display %s is %dbpp depth=%d true " "color\n", DisplayString(dpy), fb->bits_per_pixel, fb->depth); } + indexed_color = 0; + /* convert masks to bit shifts and max # colors */ - screen->serverFormat.redShift = 0; - if ( fb->red_mask ) { - while ( ! (fb->red_mask - & (1 << screen->serverFormat.redShift) ) ) { + if (fb->red_mask) { + while (! (fb->red_mask + & (1 << screen->serverFormat.redShift))) { screen->serverFormat.redShift++; } } - screen->serverFormat.greenShift = 0; - if ( fb->green_mask ) { - while ( ! (fb->green_mask - & (1 << screen->serverFormat.greenShift) ) ) { + if (fb->green_mask) { + while (! (fb->green_mask + & (1 << screen->serverFormat.greenShift))) { screen->serverFormat.greenShift++; } } - screen->serverFormat.blueShift = 0; - if ( fb->blue_mask ) { - while ( ! (fb->blue_mask - & (1 << screen->serverFormat.blueShift) ) ) { + if (fb->blue_mask) { + while (! (fb->blue_mask + & (1 << screen->serverFormat.blueShift))) { screen->serverFormat.blueShift++; } } screen->serverFormat.redMax - = fb->red_mask >> screen->serverFormat.redShift; + = fb->red_mask >> screen->serverFormat.redShift; screen->serverFormat.greenMax = fb->green_mask >> screen->serverFormat.greenShift; screen->serverFormat.blueMax - = fb->blue_mask >> screen->serverFormat.blueShift; + = fb->blue_mask >> screen->serverFormat.blueShift; - main_red_max = screen->serverFormat.redMax; - main_green_max = screen->serverFormat.greenMax; - main_blue_max = screen->serverFormat.blueMax; + main_red_max = screen->serverFormat.redMax; + main_green_max = screen->serverFormat.greenMax; + main_blue_max = screen->serverFormat.blueMax; main_red_shift = screen->serverFormat.redShift; main_green_shift = screen->serverFormat.greenShift; main_blue_shift = screen->serverFormat.blueShift; } + + if (!quiet) { + fprintf(stderr, "\n"); + fprintf(stderr, "FrameBuffer Info:\n"); + fprintf(stderr, " width: %d\n", fb->width); + fprintf(stderr, " height: %d\n", fb->height); + fprintf(stderr, " scaled_width: %d\n", width); + fprintf(stderr, " scaled_height: %d\n", height); + fprintf(stderr, " indexed_color: %d\n", indexed_color); + fprintf(stderr, " bits_per_pixel: %d\n", fb->bits_per_pixel); + fprintf(stderr, " depth: %d\n", fb->depth); + fprintf(stderr, " red_mask: 0x%08lx %s\n", fb->red_mask, + bitprint(fb->red_mask, 32)); + fprintf(stderr, " green_mask: 0x%08lx %s\n", fb->green_mask, + bitprint(fb->green_mask, 32)); + fprintf(stderr, " blue_mask: 0x%08lx %s\n", fb->blue_mask, + bitprint(fb->blue_mask, 32)); + fprintf(stderr, " red: max: %3d shift: %2d\n", + main_red_max, main_red_shift); + fprintf(stderr, " green: max: %3d shift: %2d\n", + main_green_max, main_green_shift); + fprintf(stderr, " blue: max: %3d shift: %2d\n", + main_blue_max, main_blue_shift); + fprintf(stderr, " mainfb_bytes_per_line: %d\n", + main_bytes_per_line); + fprintf(stderr, " rfb_fb_bytes_per_line: %d\n", + rfb_bytes_per_line); + switch(fb->format) { + case XYBitmap: + fprintf(stderr, " format: XYBitmap\n"); break; + case XYPixmap: + fprintf(stderr, " format: XYPixmap\n"); break; + case ZPixmap: + fprintf(stderr, " format: ZPixmap\n"); break; + default: + fprintf(stderr, " format: %d\n", fb->format); break; + } + switch(fb->byte_order) { + case LSBFirst: + fprintf(stderr, " byte_order: LSBFirst\n"); break; + case MSBFirst: + fprintf(stderr, " byte_order: MSBFirst\n"); break; + default: + fprintf(stderr, " byte_order: %d\n", fb->byte_order); + break; + } + fprintf(stderr, " bitmap_pad: %d\n", fb->bitmap_pad); + fprintf(stderr, " bitmap_unit: %d\n", fb->bitmap_unit); + switch(fb->bitmap_bit_order) { + case LSBFirst: + fprintf(stderr, " bitmap_bit_order: LSBFirst\n"); break; + case MSBFirst: + fprintf(stderr, " bitmap_bit_order: MSBFirst\n"); break; + default: + fprintf(stderr, " bitmap_bit_order: %d\n", + fb->bitmap_bit_order); break; + } + fprintf(stderr, "\n"); + } if (overlay && ! quiet) { rfbLog("\n"); rfbLog("Overlay mode enabled: If you experience color\n"); @@ -5595,6 +9516,50 @@ void initialize_screen(int *argc, char **argv, XImage *fb) { } screen->frameBuffer = rfb_fb; + bpp = screen->serverFormat.bitsPerPixel; + depth = screen->serverFormat.depth; + + setup_cursors(); + if (! show_cursor) { + screen->cursor = NULL; + } else { + /* just set it to the arrow for now. */ + if (0 && !quiet) rfbLog("calling set_rfb_cursor...\n"); + set_rfb_cursor(CURS_ARROW); + if (0 && !quiet) rfbLog(" done.\n"); + } + + if (scaling) { + mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); + } + + if (! create_screen) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + + /* + * since bits_per_color above may have been approximate, + * try to reset the individual translation tables... + * we do not seem to need this with rfbGetScreen()... + */ + if (!quiet) rfbLog("calling setTranslateFunction()...\n"); + iter = rfbGetClientIterator(screen); + while ((cl = rfbClientIteratorNext(iter)) != NULL) { + screen->setTranslateFunction(cl); + } + rfbReleaseClientIterator(iter); + if (!quiet) rfbLog(" done.\n"); + do_copy_screen = 1; + + /* done for framebuffer change case */ + return; + } + + /* + * the rest is screen server initialization, etc, only needed + * at screen creation time. + */ + /* called from inetd, we need to treat stdio as our socket */ if (inetd) { int fd = dup(0); @@ -5635,23 +9600,8 @@ void initialize_screen(int *argc, char **argv, XImage *fb) { screen->setXCutText = xcut_receive; } - setup_cursors(); - if (! show_cursor) { - screen->cursor = NULL; - } else { - screen->cursor = cursors[0]->rfb; - } - rfbInitServer(screen); - - bpp = screen->serverFormat.bitsPerPixel; - depth = screen->serverFormat.depth; - - if (scaling) { - mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); - } - if (viewonly_passwd) { /* append the view only passwd after the normal passwd */ char **passwds_new = malloc(3*sizeof(char**)); @@ -5688,9 +9638,11 @@ int blackouts = 0; * Take a comma separated list of geometries: WxH+X+Y and register them as * rectangles to black out from the screen. */ -void initialize_blackout (char *list) { +void initialize_blackouts(char *list) { char *p, *blist = strdup(list); - int x, y, X, Y, h, w; + int x, y, X, Y, h, w, t; + + blackouts = 0; p = strtok(blist, ", \t"); while (p) { @@ -5698,10 +9650,14 @@ void initialize_blackout (char *list) { if (sscanf(p, "%dx%d+%d+%d", &w, &h, &x, &y) == 4) { ; } else if (sscanf(p, "%dx%d-%d+%d", &w, &h, &x, &y) == 4) { + w = nabs(w); x = dpy_x - x - w; } else if (sscanf(p, "%dx%d+%d-%d", &w, &h, &x, &y) == 4) { + h = nabs(h); y = dpy_y - y - h; } else if (sscanf(p, "%dx%d-%d-%d", &w, &h, &x, &y) == 4) { + w = nabs(w); + h = nabs(h); x = dpy_x - x - w; y = dpy_y - y - h; } else { @@ -5711,10 +9667,23 @@ void initialize_blackout (char *list) { p = strtok(NULL, ", \t"); continue; } + w = nabs(w); + h = nabs(h); + x = nfix(x, dpy_x); + y = nfix(y, dpy_y); X = x + w; Y = y + h; + X = nfix(X, dpy_x); + Y = nfix(Y, dpy_y); + if (x > X) { + t = X; X = x; x = t; + } + if (y > Y) { + t = Y; Y = y; y = t; + } if (x < 0 || x > dpy_x || y < 0 || y > dpy_y || - X < 0 || X > dpy_x || Y < 0 || Y > dpy_y) { + X < 0 || X > dpy_x || Y < 0 || Y > dpy_y || + x == X || y == Y) { rfbLog("skipping invalid blackout geometry: %s x=" "%d-%d,y=%d-%d,w=%d,h=%d\n", p, x, X, y, Y, w, h); } else { @@ -5748,9 +9717,13 @@ void initialize_blackout (char *list) { */ void blackout_tiles(void) { int tx, ty; + int debug_bo = 0; if (! blackouts) { return; } + if (getenv("DEBUG_BLACKOUT") != NULL) { + debug_bo = 1; + } /* * to simplify things drop down to single copy mode, no vcr, etc... @@ -5836,8 +9809,18 @@ void blackout_tiles(void) { if (rect.x1 == x1 && rect.y1 == y1 && rect.x2 == x2 && rect.y2 == y2) { tile_blackout[n].cover = 2; + if (debug_bo) { + fprintf(stderr, "full: %d=%d,%d" + " (%d-%d) (%d-%d)\n", + n, tx, ty, x1, x2, y1, y2); + } } else { tile_blackout[n].cover = 1; + if (debug_bo) { + fprintf(stderr, "part: %d=%d,%d" + " (%d-%d) (%d-%d)\n", + n, tx, ty, x1, x2, y1, y2); + } } if (++cnt >= BO_MAX) { @@ -5852,6 +9835,10 @@ void blackout_tiles(void) { sraRgnDestroy(tile_reg); tile_blackout[n].count = cnt; + if (debug_bo && cnt > 1) { + rfbLog("warning: multiple region overlaps[%d] " + "for tile %d=%d,%d.\n", cnt, n, tx, ty); + } } } } @@ -5872,14 +9859,17 @@ void initialize_xinerama (void) { if (! XineramaQueryExtension(dpy, &ev, &er)) { rfbLog("Xinerama: disabling: display does not support it.\n"); xinerama = 0; + xinerama_present = 0; return; } if (! XineramaIsActive(dpy)) { /* n.b. change to XineramaActive(dpy, window) someday */ rfbLog("Xinerama: disabling: not active on display.\n"); xinerama = 0; + xinerama_present = 0; return; - } + } + xinerama_present = 1; /* n.b. change to XineramaGetData() someday */ xineramas = XineramaQueryScreens(dpy, &n); @@ -5946,13 +9936,51 @@ void initialize_xinerama (void) { sprintf(tstr, "%dx%d+%d+%d,", w, h, x, y); strcat(bstr, tstr); } - initialize_blackout(bstr); + initialize_blackouts(bstr); free(bstr); free(tstr); #endif } +void initialize_blackouts_and_xinerama(void) { + if (blackout_string != NULL) { + initialize_blackouts(blackout_string); + } + if (xinerama) { + initialize_xinerama(); + } + if (blackouts) { + blackout_tiles(); + /* schedule a copy_screen(), now is too early. */ + do_copy_screen = 1; + } +} + +void push_sleep(n) { + int i; + for (i=0; i<n; i++) { + rfbPE(screen, -1); + if (i != n-1 && defer_update) { + usleep(defer_update * 1000); + } + } +} + +/* + * try to forcefully push a black screen to all connected clients + */ +void push_black_screen(int n) { + zero_fb(0, 0, dpy_x, dpy_y); + mark_rect_as_modified(0, 0, dpy_x, dpy_y, 1); + push_sleep(n); +} + +void refresh_screen(void) { + mark_rect_as_modified(0, 0, dpy_x, dpy_y, 1); + rfbPE(screen, -1); +} + /* * Fill the framebuffer with zero for the prescribed rectangle */ @@ -6038,6 +10066,37 @@ void initialize_tiles(void) { hint_list = (hint_t *) malloc((size_t) (ntiles * sizeof(hint_t))); } +void free_tiles(void) { + if (tile_has_diff) { + free(tile_has_diff); + tile_has_diff = NULL; + } + if (tile_tried) { + free(tile_tried); + tile_tried = NULL; + } + if (tile_blackout) { + free(tile_blackout); + tile_blackout = NULL; + } + if (tile_region) { + free(tile_region); + tile_region = NULL; + } + if (tile_row) { + free(tile_row); + tile_row = NULL; + } + if (tile_row_shm) { + free(tile_row_shm); + tile_row_shm = NULL; + } + if (hint_list) { + free(hint_list); + hint_list = NULL; + } +} + /* * silly function to factor dpy_y until fullscreen shm is not bigger than max. * should always work unless dpy_y is a large prime or something... under @@ -6191,38 +10250,32 @@ static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h, } void shm_delete(XShmSegmentInfo *shm) { - if (! using_shm) { - return; - } #ifdef LIBVNCSERVER_HAVE_XSHM - if (shm->shmaddr != (char *) -1) { + if (shm != NULL && shm->shmaddr != (char *) -1) { shmdt(shm->shmaddr); } - if (shm->shmid != -1) { + if (shm != NULL && shm->shmid != -1) { shmctl(shm->shmid, IPC_RMID, 0); } #endif } void shm_clean(XShmSegmentInfo *shm, XImage *xim) { - if (! using_shm) { - return; - } -#ifdef LIBVNCSERVER_HAVE_XSHM X_LOCK; - if (shm->shmid != -1) { +#ifdef LIBVNCSERVER_HAVE_XSHM + if (shm != NULL && shm->shmid != -1) { XShmDetach_wr(dpy, shm); } +#endif if (xim != NULL) { XDestroyImage(xim); } X_UNLOCK; shm_delete(shm); -#endif } -void initialize_shm(void) { +void initialize_polling_images(void) { int i; /* set all shm areas to "none" before trying to create any */ @@ -6243,6 +10296,13 @@ void initialize_shm(void) { if (! shm_create(&scanline_shm, &scanline, dpy_x, 1, "scanline")) { clean_up_exit(1); } + if (0 && !quiet) { + if (using_shm) { + rfbLog("created \"scanline\" shm polling image.\n"); + } else { + rfbLog("created \"scanline\" polling image.\n"); + } + } /* * the fullscreen (e.g. 1280x1024/fs_factor) shared memory area image: @@ -6261,6 +10321,13 @@ void initialize_shm(void) { dpy_y/fs_factor, "fullscreen")) { clean_up_exit(1); } + if (0 && !quiet) { + if (using_shm) { + rfbLog("created \"scanline\" shm polling image.\n"); + } else { + rfbLog("created \"scanline\" polling image.\n"); + } + } } /* @@ -6269,6 +10336,7 @@ void initialize_shm(void) { * and 40 for 1280x1024, etc. */ + tile_shm_count = 0; for (i=1; i<=ntiles_x; i++) { if (! shm_create(&tile_row_shm[i], &tile_row[i], tile_x * i, tile_y, "tile_row")) { @@ -6284,14 +10352,23 @@ void initialize_shm(void) { rfbLog("shm: shared memory usage, or run ipcrm(1)" " to manually\n"); rfbLog("shm: delete unattached shm segments.\n"); - /* n.b.: "i" not "1", a kludge for cleanup */ - single_copytile = i; + single_copytile_count = i; } + tile_shm_count++; if (single_copytile && i >= 1) { /* only need 1x1 tiles */ break; } } + if (!quiet) { + if (using_shm) { + rfbLog("created %d tile_row shm polling images.\n", + tile_shm_count); + } else { + rfbLog("created %d tile_row polling images.\n", + tile_shm_count); + } + } } /* @@ -6895,6 +10972,23 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) { void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force) { + if (damage_time != 0) { + int debug = 0; + if (time(0) > damage_time + damage_delay) { + if (! quiet) { + rfbLog("damaging turned off.\n"); + } + damage_time = 0; + damage_delay = 0; + } else { + if (debug) { + rfbLog("damaging viewer fb by not marking " + "rect: %d,%d,%d,%d\n", x1, y1, x2, y2); + } + return; + } + } + if (rfb_fb == main_fb || force) { rfbMarkRectAsModified(screen, x1, y1, x2, y2); } else if (scaling) { @@ -6926,7 +11020,7 @@ void mark_hint(hint_t hint) { static int *first_line = NULL, *last_line; static unsigned short *left_diff, *right_diff; -static void copy_tiles(int tx, int ty, int nt) { +static int copy_tiles(int tx, int ty, int nt) { int x, y, line; int size_x, size_y, width1, width2; int off, len, n, dw, dx, t; @@ -6975,12 +11069,13 @@ static void copy_tiles(int tx, int ty, int nt) { * n.b. we are int single copy_tile mode: nt=1 */ tile_has_diff[n] = 0; - return; + return(0); } X_LOCK; + XRANDR_SET_TRAP_RET(-1, "copy_tile-set"); /* read in the whole tile run at once: */ - if ( using_shm && size_x == tile_x * nt && size_y == tile_y ) { + if (using_shm && size_x == tile_x * nt && size_y == tile_y) { /* general case: */ XShmGetImage_wr(dpy, window, tile_row[nt], x, y, AllPlanes); } else { @@ -6991,6 +11086,7 @@ static void copy_tiles(int tx, int ty, int nt) { XGetSubImage_wr(dpy, window, x, y, size_x, size_y, AllPlanes, ZPixmap, tile_row[nt], 0, 0); } + XRANDR_CHK_TRAP_RET(-1, "copy_tile-chk"); X_UNLOCK; if (blackouts && tile_blackout[n].cover == 1) { @@ -7074,7 +11170,7 @@ static void copy_tiles(int tx, int ty, int nt) { for (t=1; t <= nt; t++) { tile_has_diff[n+(t-1)] = 0; } - return; + return(0); } else { /* * at least one tile has a difference. make sure info @@ -7224,6 +11320,7 @@ static void copy_tiles(int tx, int ty, int nt) { tile_region[n+s].right_diff = right_diff[t]; } + return(1); } /* @@ -7241,14 +11338,15 @@ static void copy_tiles(int tx, int ty, int nt) { */ static int copy_all_tiles(void) { int x, y, n, m; - int diffs = 0; + int diffs = 0, ct; for (y=0; y < ntiles_y; y++) { for (x=0; x < ntiles_x; x++) { n = x + y * ntiles_x; if (tile_has_diff[n]) { - copy_tiles(x, y, 1); + ct = copy_tiles(x, y, 1); + if (ct < 0) return ct; /* fatal */ } if (! tile_has_diff[n]) { /* @@ -7284,7 +11382,7 @@ static int copy_all_tiles(void) { */ static int copy_all_tile_runs(void) { int x, y, n, m, i; - int diffs = 0; + int diffs = 0, ct; int in_run = 0, run = 0; int ntave = 0, ntcnt = 0; @@ -7301,7 +11399,8 @@ static int copy_all_tile_runs(void) { run = 0; continue; } - copy_tiles(x - run, y, run); + ct = copy_tiles(x - run, y, run); + if (ct < 0) return ct; /* fatal */ ntcnt++; ntave += run; @@ -7351,7 +11450,7 @@ static int copy_all_tile_runs(void) { */ static int copy_tiles_backward_pass(void) { int x, y, n, m; - int diffs = 0; + int diffs = 0, ct; for (y = ntiles_y - 1; y >= 0; y--) { for (x = ntiles_x - 1; x >= 0; x--) { @@ -7366,7 +11465,8 @@ static int copy_tiles_backward_pass(void) { if (y >= 1 && ! tile_has_diff[m] && tile_region[n].top_diff) { if (! tile_tried[m]) { tile_has_diff[m] = 2; - copy_tiles(x, y-1, 1); + ct = copy_tiles(x, y-1, 1); + if (ct < 0) return ct; /* fatal */ } } @@ -7375,7 +11475,8 @@ static int copy_tiles_backward_pass(void) { if (x >= 1 && ! tile_has_diff[m] && tile_region[n].left_diff) { if (! tile_tried[m]) { tile_has_diff[m] = 2; - copy_tiles(x-1, y, 1); + ct = copy_tiles(x-1, y, 1); + if (ct < 0) return ct; /* fatal */ } } } @@ -7388,8 +11489,8 @@ static int copy_tiles_backward_pass(void) { return diffs; } -static void gap_try(int x, int y, int *run, int *saw, int along_x) { - int n, m, i, xt, yt; +static int gap_try(int x, int y, int *run, int *saw, int along_x) { + int n, m, i, xt, yt, ct; n = x + y * ntiles_x; @@ -7397,12 +11498,12 @@ static void gap_try(int x, int y, int *run, int *saw, int along_x) { if (*saw) { (*run)++; /* extend the gap run. */ } - return; + return 0; } if (! *saw || *run == 0 || *run > gaps_fill) { *run = 0; /* unacceptable run. */ *saw = 1; - return; + return 0; } for (i=1; i <= *run; i++) { /* iterate thru the run. */ @@ -7419,10 +11520,12 @@ static void gap_try(int x, int y, int *run, int *saw, int along_x) { continue; } - copy_tiles(xt, yt, 1); + ct = copy_tiles(xt, yt, 1); + if (ct < 0) return ct; /* fatal */ } *run = 0; *saw = 1; + return 1; } /* @@ -7435,14 +11538,15 @@ static void gap_try(int x, int y, int *run, int *saw, int along_x) { */ static int fill_tile_gaps(void) { int x, y, run, saw; - int n, diffs = 0; + int n, diffs = 0, ct; /* horizontal: */ for (y=0; y < ntiles_y; y++) { run = 0; saw = 0; for (x=0; x < ntiles_x; x++) { - gap_try(x, y, &run, &saw, 1); + ct = gap_try(x, y, &run, &saw, 1); + if (ct < 0) return ct; /* fatal */ } } @@ -7451,7 +11555,8 @@ static int fill_tile_gaps(void) { run = 0; saw = 0; for (y=0; y < ntiles_y; y++) { - gap_try(x, y, &run, &saw, 0); + ct = gap_try(x, y, &run, &saw, 0); + if (ct < 0) return ct; /* fatal */ } } @@ -7463,8 +11568,8 @@ static int fill_tile_gaps(void) { return diffs; } -static void island_try(int x, int y, int u, int v, int *run) { - int n, m; +static int island_try(int x, int y, int u, int v, int *run) { + int n, m, ct; n = x + y * ntiles_x; m = u + v * ntiles_x; @@ -7479,13 +11584,15 @@ static void island_try(int x, int y, int u, int v, int *run) { /* found a discontinuity */ if (tile_tried[m]) { - return; + return 0; } else if (*run < grow_fill) { - return; + return 0; } - copy_tiles(u, v, 1); + ct = copy_tiles(u, v, 1); + if (ct < 0) return ct; /* fatal */ } + return 1; } /* @@ -7495,7 +11602,7 @@ static void island_try(int x, int y, int u, int v, int *run) { */ static int grow_islands(void) { int x, y, n, run; - int diffs = 0; + int diffs = 0, ct; /* * n.b. the way we scan here should keep an extension going, @@ -7506,14 +11613,16 @@ static int grow_islands(void) { for (y=0; y < ntiles_y; y++) { run = 0; for (x=0; x <= ntiles_x - 2; x++) { - island_try(x, y, x+1, y, &run); + ct = island_try(x, y, x+1, y, &run); + if (ct < 0) return ct; /* fatal */ } } /* right to left: */ for (y=0; y < ntiles_y; y++) { run = 0; for (x = ntiles_x - 1; x >= 1; x--) { - island_try(x, y, x-1, y, &run); + ct = island_try(x, y, x-1, y, &run); + if (ct < 0) return ct; /* fatal */ } } for (n=0; n < ntiles; n++) { @@ -7540,11 +11649,15 @@ static void blackout_regions(void) { * the info from the X server. Bandwidth to client and compression time * are other issues... use -fs 1.0 to disable. */ -void copy_screen(void) { +int copy_screen(void) { int pixelsize = bpp >> 3; char *fbp; int i, y, block_size; + if (! fs_factor) { + return 0; + } + block_size = (dpy_x * (dpy_y/fs_factor) * pixelsize); fbp = main_fb; @@ -7554,6 +11667,7 @@ void copy_screen(void) { /* screen may be too big for 1 shm area, so broken into fs_factor */ for (i=0; i < fs_factor; i++) { + XRANDR_SET_TRAP_RET(-1, "copy_screen-set"); if (using_shm) { XShmGetImage_wr(dpy, window, fullscreen, 0, y, AllPlanes); @@ -7562,6 +11676,7 @@ void copy_screen(void) { fullscreen->height, AllPlanes, ZPixmap, fullscreen, 0, 0); } + XRANDR_CHK_TRAP_RET(-1, "copy_screen-chk"); memcpy(fbp, fullscreen->data, (size_t) block_size); y += dpy_y / fs_factor; @@ -7575,6 +11690,7 @@ void copy_screen(void) { } mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); + return 0; } @@ -7818,6 +11934,7 @@ static int scan_display(int ystart, int rescan) { /* grab the horizontal scanline from the display: */ X_LOCK; + XRANDR_SET_TRAP_RET(-1, "scan_display-set"); if (using_shm) { XShmGetImage_wr(dpy, window, scanline, 0, y, AllPlanes); } else { @@ -7825,6 +11942,7 @@ static int scan_display(int ystart, int rescan) { scanline->height, AllPlanes, ZPixmap, scanline, 0, 0); } + XRANDR_CHK_TRAP_RET(-1, "scan_display-chk"); X_UNLOCK; /* for better memory i/o try the whole line at once */ @@ -7921,14 +12039,22 @@ int scan_for_updates(void) { if (subwin) { set_offset(); /* follow the subwindow */ } - if (indexed_colour) { /* check for changed colormap */ - set_colormap(); + if (indexed_color) { /* check for changed colormap */ + set_colormap(0); } } +#define SCAN_FATAL(x) \ + if (x < 0) { \ + scan_in_progress = 0; \ + fb_copy_in_progress = 0; \ + return 0; \ + } + /* scan with the initial y to the jitter value from scanlines: */ scan_in_progress = 1; tile_count = scan_display(scanlines[scan_count], 0); + SCAN_FATAL(tile_count); nap_set(tile_count); @@ -7951,11 +12077,13 @@ int scan_for_updates(void) { cp = (NSCAN - scan_count) % NSCAN; tile_count = scan_display(scanlines[cp], 1); + SCAN_FATAL(tile_count); if (tile_count >= (1 + frac2) * tile_count_old) { /* on a roll... do a 3rd scan */ cp = (NSCAN - scan_count + 7) % NSCAN; tile_count = scan_display(scanlines[cp], 1); + SCAN_FATAL(tile_count); } } scan_in_progress = 0; @@ -7975,10 +12103,12 @@ int scan_for_updates(void) { * Use -fs 1.0 to disable on slow links. */ if (fs_factor && tile_count > fs_frac * ntiles) { + int cs; fb_copy_in_progress = 1; - copy_screen(); + cs = copy_screen(); fb_copy_in_progress = 0; - if (use_threads && old_pointer != 1) { + SCAN_FATAL(cs); + if (use_threads && pointer_mode != 1) { pointer(-1, 0, 0, NULL); } nap_check(tile_count); @@ -7990,7 +12120,7 @@ int scan_for_updates(void) { /* copy all tiles with differences from display to rfb framebuffer: */ fb_copy_in_progress = 1; - if (single_copytile) { + if (single_copytile || tile_shm_count < ntiles_x) { /* * Old way, copy I/O one tile at a time. */ @@ -8003,25 +12133,29 @@ int scan_for_updates(void) { */ tile_diffs = copy_all_tile_runs(); } + SCAN_FATAL(tile_diffs); /* * This backward pass for upward and left tiles complements what * was done in copy_all_tiles() for downward and right tiles. */ tile_diffs = copy_tiles_backward_pass(); + SCAN_FATAL(tile_diffs); /* Given enough tile diffs, try the islands: */ if (grow_fill && tile_diffs > 4) { tile_diffs = grow_islands(); } + SCAN_FATAL(tile_diffs); /* Given enough tile diffs, try the gaps: */ if (gaps_fill && tile_diffs > 4) { tile_diffs = fill_tile_gaps(); } + SCAN_FATAL(tile_diffs); fb_copy_in_progress = 0; - if (use_threads && old_pointer != 1) { + if (use_threads && pointer_mode != 1) { /* * tell the pointer handler it can process any queued * pointer events: @@ -8054,6 +12188,220 @@ int scan_for_updates(void) { return tile_diffs; } +/* -- gui.c -- */ +#if OLD_TREE +char gui_code[] = ""; +#else +#include "tkx11vnc.h" +#endif + +void run_gui(char *gui_xdisplay, int connect_to_x11vnc) { + char *x11vnc_xdisplay = NULL; + char extra_path[] = ":/usr/local/bin:/usr/sfw/bin:/usr/X11R6/bin"; + char cmd[100]; + char *wish, *orig_path, *full_path, *tpath, *p; + int try_max = 9, sleep = 300; + FILE *pipe; + + + if (getenv("DISPLAY") != NULL) { + x11vnc_xdisplay = strdup(getenv("DISPLAY")); + } + if (use_dpy) { + x11vnc_xdisplay = strdup(use_dpy); + } + if (connect_to_x11vnc) { + int rc, i; + if (! client_connect_file) { + dpy = XOpenDisplay(x11vnc_xdisplay); + if (! dpy) { + fprintf(stderr, "could not open display: %s\n", + x11vnc_xdisplay); + exit(1); + } + scr = DefaultScreen(dpy); + rootwin = RootWindow(dpy, scr); + initialize_vnc_connect_prop(); + } + for (i=0; i<try_max; i++) { + usleep(sleep*1000); + fprintf(stderr, "pinging %s ...\n", + NONUL(x11vnc_xdisplay)); + rc = send_remote_cmd("ping", 1); + if (rc == 0) { + break; + } + } + if (rc == 0) { + fprintf(stderr, "success.\n"); + set_env("X11VNC_XDISPLAY", x11vnc_xdisplay); + set_env("X11VNC_CONNECT", "1"); + } else { + fprintf(stderr, "could not connect to: '%s', try again" + " in the gui.\n", x11vnc_xdisplay); + } + } + + orig_path = getenv("PATH"); + if (! orig_path) { + orig_path = strdup("/bin:/usr/bin:/usr/bin/X11"); + } + full_path = (char *) malloc(strlen(orig_path)+strlen(extra_path)+1); + strcpy(full_path, orig_path); + strcat(full_path, extra_path); + + tpath = strdup(full_path); + p = strtok(tpath, ":"); + + while (p) { + char *try; + struct stat sbuf; + char *wishes[] = {"wish", "wish8.3", "wish8.4"}; + int nwishes = 3, i; + + try = (char *)malloc(strlen(p) + 10); + for (i=0; i<nwishes; i++) { + sprintf(try, "%s/%s", p, wishes[i]); + if (stat(try, &sbuf)) { + wish = wishes[i]; + break; + } + } + free(try); + if (wish) { + break; + } + p = strtok(NULL, ":"); + } + free(tpath); + if (!wish) { + wish = strdup("wish"); + } + set_env("PATH", full_path); + set_env("DISPLAY", gui_xdisplay); + set_env("X11VNC_PROG", program_name); + set_env("X11VNC_CMDLINE", program_cmdline); + +if (0) system("env"); + sprintf(cmd, "%s -", wish); + pipe = popen(cmd, "w"); + if (! pipe) { + fprintf(stderr, "could not run: %s\n", cmd); + perror("popen"); + } + fprintf(pipe, "%s", gui_code); + pclose(pipe); + exit(0); +} + +void do_gui(char *opts) { + char *s, *p; + char *old_xauth = NULL; + char *gui_xdisplay = NULL; + int start_x11vnc = 1; + int connect_to_x11vnc = 0; + Display *test_dpy; + + if (*gui_code == '\0') { + rfbLog("gui not available in this program.\n"); + clean_up_exit(1); + } + if (opts) { + s = strdup(opts); + } else { + s = strdup(""); + } + + if (use_dpy) { + /* worst case */ + gui_xdisplay = strdup(use_dpy); + + } + if (getenv("DISPLAY") != NULL) { + /* better */ + gui_xdisplay = strdup(getenv("DISPLAY")); + } + + p = strtok(s, ","); + + while(p) { + if (*p == '\0') { + ; + } else if (strchr(p, ':') != NULL) { + /* best */ + gui_xdisplay = strdup(p); + } else if (!strcmp(p, "wait")) { + start_x11vnc = 0; + connect_to_x11vnc = 0; + } else if (!strcmp(p, "conn") || !strcmp(p, "connect")) { + start_x11vnc = 0; + connect_to_x11vnc = 1; + } else { + fprintf(stderr, "unrecognized gui opt: %s\n", p); + } + + p = strtok(NULL, ","); + } + free(s); + if (start_x11vnc) { + connect_to_x11vnc = 1; + } + + if (! gui_xdisplay) { + fprintf(stderr, "error: cannot determine X DISPLAY for gui" + " to display on.\n"); + exit(1); + } + test_dpy = XOpenDisplay(gui_xdisplay); + if (! test_dpy && auth_file) { + if (getenv("XAUTHORITY") != NULL) { + old_xauth = strdup(getenv("XAUTHORITY")); + } + set_env("XAUTHORITY", auth_file); + test_dpy = XOpenDisplay(gui_xdisplay); + } + if (! test_dpy) { + if (! old_xauth && getenv("XAUTHORITY") != NULL) { + old_xauth = strdup(getenv("XAUTHORITY")); + } + set_env("XAUTHORITY", ""); + test_dpy = XOpenDisplay(gui_xdisplay); + } + if (! test_dpy) { + fprintf(stderr, "error: cannot connect to gui X DISPLAY: %s\n", + gui_xdisplay); + } + XCloseDisplay(test_dpy); + + if (start_x11vnc) { +#if defined(LIBVNCSERVER_HAVE_FORK) + /* fork into the background now */ + int p; + if ((p = fork()) > 0) { + ; /* parent */ + } else if (p == -1) { + fprintf(stderr, "could not fork\n"); + perror("fork"); + clean_up_exit(1); + } else { + run_gui(gui_xdisplay, connect_to_x11vnc); + exit(1); + } +#else + fprintf(stderr, "system does not support fork: start " + "x11vnc in the gui.\n"); + start_x11vnc = 0; +#endif + } + if (!start_x11vnc) { + run_gui(gui_xdisplay, connect_to_x11vnc); + exit(1); + } + if (old_xauth) { + set_env("XAUTHORITY", old_xauth); + } +} + /* -- x11vnc.c -- */ /* * main routine for the x11vnc program @@ -8077,43 +12425,143 @@ static int defer_update_nofb = 6; /* defer a shorter time under -nofb */ * * return of 1 means watch_loop should short-circuit and reloop, * return of 0 means watch_loop should proceed to scan_for_updates(). - * (this is for old_pointer == 1 mode, the others do it all internally, + * (this is for pointer_mode == 1 mode, the others do it all internally, * cnt is also only for that mode). */ -static int check_user_input_old(double dt, int *cnt); -static int check_user_input(double dt, int tile_diffs, int *cnt) { +static void check_user_input2(double dt) { - if (old_pointer == 1) { - /* every n-th drops thru to scan */ - if ((got_user_input || ui_skip < 0) && *cnt % ui_skip != 0) { - *cnt++; + + if (got_pointer_input) { + int eaten = 0, miss = 0, max_eat = 50; + int g, g_in; + double spin = 0.0, tm = 0.0; + double quick_spin_fac = 0.40; + double grind_spin_time = 0.175; + + dtime(&tm); + g = g_in = got_pointer_input; + + /* + * Try for some "quick" pointer input processing. + * + * About as fast as we can, we try to process user input + * calling rfbProcessEvents or rfbCheckFds. We do this + * for a time on order of the last scan_for_updates() time, + * dt, but if we stop getting user input we break out. + * We will also break out if we have processed max_eat + * inputs. + * + * Note that rfbCheckFds() does not send any framebuffer + * updates, so is more what we want here, although it is + * likely they have all be sent already. + */ + while (1) { + if (show_multiple_cursors) { + rfbPE(screen, 1000); + } else { + rfbCFD(screen, 1000); + } XFlush(dpy); - return 1; /* short circuit watch_loop */ - } else { - return 0; + + spin += dtime(&tm); + + if (spin > quick_spin_fac * dt) { + /* get out if spin time comparable to last scan time */ + break; + } + if (got_pointer_input > g) { + g = got_pointer_input; + if (eaten++ < max_eat) { + continue; + } + } else { + miss++; + } + if (miss > 1) { /* 1 means out on 2nd miss */ + break; + } } - } else if (old_pointer == 2) { - /* this older way is similar to the method below */ - return check_user_input_old(dt, cnt); - } - if (got_keyboard_input) { - if (*cnt % ui_skip != 0) { - *cnt++; - return 1; /* short circuit watch_loop */ + + /* + * Probably grinding with a lot of fb I/O if dt is + * this large. (need to do this more elegantly) + * + * Current idea is to spin our wheels here *not* processing + * any fb I/O, but still processing the user input. + * This user input goes to the X display and changes it, + * but we don't poll it while we "rest" here for a time + * on order of dt, the previous scan_for_updates() time. + * We also break out if we miss enough user input. + */ + if (dt > grind_spin_time) { + int i, ms, split = 30; + double shim; + + /* + * Break up our pause into 'split' steps. + * We get at most one input per step. + */ + shim = 0.75 * dt / split; + + ms = (int) (1000 * shim); + + /* cutoff how long the pause can be */ + if (split * ms > 300) { + ms = 300 / split; + } + + spin = 0.0; + tm = 0.0; + dtime(&tm); + + g = got_pointer_input; + miss = 0; + for (i=0; i<split; i++) { + usleep(ms * 1000); + if (show_multiple_cursors) { + rfbPE(screen, 1000); + } else { + rfbCFD(screen, 1000); + } + spin += dtime(&tm); + if (got_pointer_input > g) { + XFlush(dpy); + miss = 0; + } else { + miss++; + } + g = got_pointer_input; + if (miss > 2) { + break; + } + if (1000 * spin > ms * split) { + break; + } + } } - /* otherwise continue with pointer input */ } +} + +static void check_user_input3(double dt, int tile_diffs) { if (got_pointer_input) { int spun_out, missed_out, allowed_misses, g, g_in; double spin, spin_max, tm, to, dtm, rpe_last; static int rfb_wait_ms = 2; - static double grind_spin_time = 0.30, dt_min = 0.15; + static double grind_spin_time = 0.30, dt_min = 0.075; static double quick_spin_fac = 0.65, spin_max_fac = 2.0; static double rpe_wait = 0.15; int grinding, gcnt, ms, split = 200; + static int first = 1; + if (first) { + char *p = getenv("SPIN"); + if (p) { + sscanf(p, "%lf,%lf,%lf", &grind_spin_time, &dt_min, &quick_spin_fac); + } + first = 0; + } /* @@ -8150,6 +12598,7 @@ static int check_user_input(double dt, int tile_diffs, int *cnt) { rpe_last = to = tm; /* last time we did rfbPE() */ g = g_in = got_pointer_input; + while (1) { int got_input = 0; @@ -8161,6 +12610,10 @@ static int check_user_input(double dt, int tile_diffs, int *cnt) { usleep(ms * 1000); } + if (button_mask) { + drag_in_progress = 1; + } + if (show_multiple_cursors && tm > rpe_last + rpe_wait) { rfbPE(screen, rfb_wait_ms * 1000); rpe_last = tm; @@ -8195,6 +12648,7 @@ static int check_user_input(double dt, int tile_diffs, int *cnt) { wms = 1000 * (0.5 * (spin_max - spin)); } else if (button_mask) { wms = 10; + } else { } if (wms) { usleep(wms * 1000); @@ -8219,7 +12673,7 @@ static int check_user_input(double dt, int tile_diffs, int *cnt) { /* reset for second pass */ spun_out = 0; missed_out = 0; - allowed_misses = 5; + allowed_misses = 3; g = got_pointer_input; gcnt = 0; } else if (spun_out && grinding) { @@ -8231,129 +12685,39 @@ static int check_user_input(double dt, int tile_diffs, int *cnt) { } } } - return 0; + drag_in_progress = 0; } -/* this is the -old_pointer2 way */ -static int check_user_input_old(double dt, int *cnt) { +static void check_user_input4(double dt, int tile_diffs) { + return; +} - if (got_keyboard_input) { - if (*cnt % ui_skip != 0) { +static int check_user_input(double dt, int tile_diffs, int *cnt) { + if (pointer_mode == 1) { + if ((got_user_input || ui_skip < 0) && *cnt % ui_skip != 0) { + /* every ui_skip-th drops thru to scan */ *cnt++; + XFlush(dpy); return 1; /* short circuit watch_loop */ + } else { + return 0; } - /* otherwise continue with pointer input */ } - - if (got_pointer_input) { - int eaten = 0, miss = 0, max_eat = 50; - int g, g_in; - double spin = 0.0, tm = 0.0; - double quick_spin_fac = 0.40; - double grind_spin_time = 0.175; - - dtime(&tm); - g = g_in = got_pointer_input; - - /* - * Try for some "quick" pointer input processing. - * - * About as fast as we can, we try to process user input - * calling rfbProcessEvents or rfbCheckFds. We do this - * for a time on order of the last scan_for_updates() time, - * dt, but if we stop getting user input we break out. - * We will also break out if we have processed max_eat - * inputs. - * - * Note that rfbCheckFds() does not send any framebuffer - * updates, so is more what we want here, although it is - * likely they have all be sent already. - */ - while (1) { - if (show_multiple_cursors) { - rfbPE(screen, 1000); - } else { - rfbCFD(screen, 1000); - } - XFlush(dpy); - - spin += dtime(&tm); - - if (spin > quick_spin_fac * dt) { - /* get out if spin time comparable to last scan time */ - break; - } - if (got_pointer_input > g) { - g = got_pointer_input; - if (eaten++ < max_eat) { - continue; - } - } else { - miss++; - } - if (miss > 1) { /* 1 means out on 2nd miss */ - break; - } - } - - - /* - * Probably grinding with a lot of fb I/O if dt is - * this large. (need to do this more elegantly) - * - * Current idea is to spin our wheels here *not* processing - * any fb I/O, but still processing the user input. - * This user input goes to the X display and changes it, - * but we don't poll it while we "rest" here for a time - * on order of dt, the previous scan_for_updates() time. - * We also break out if we miss enough user input. - */ - if (dt > grind_spin_time) { - int i, ms, split = 30; - double shim; - - /* - * Break up our pause into 'split' steps. - * We get at most one input per step. - */ - shim = 0.75 * dt / split; - - ms = (int) (1000 * shim); - - /* cutoff how long the pause can be */ - if (split * ms > 300) { - ms = 300 / split; - } - - spin = 0.0; - tm = 0.0; - dtime(&tm); - - g = got_pointer_input; - miss = 0; - for (i=0; i<split; i++) { - usleep(ms * 1000); - if (show_multiple_cursors) { - rfbPE(screen, 1000); - } else { - rfbCFD(screen, 1000); - } - spin += dtime(&tm); - if (got_pointer_input > g) { - XFlush(dpy); - miss = 0; - } else { - miss++; - } - g = got_pointer_input; - if (miss > 2) { - break; - } - if (1000 * spin > ms * split) { - break; - } + if (pointer_mode >= 2 && pointer_mode <= 4) { + if (got_keyboard_input) { + if (*cnt % ui_skip != 0) { + *cnt++; + return 1; /* short circuit watch_loop */ } } + /* otherwise continue with pointer input */ + } + if (pointer_mode == 2) { + check_user_input2(dt); + } else if (pointer_mode == 3) { + check_user_input3(dt, tile_diffs); + } else if (pointer_mode == 4) { + check_user_input4(dt, tile_diffs); } return 0; } @@ -8418,7 +12782,7 @@ static void watch_loop(void) { rfbPE(screen, -1); if (! cursor_shape_updates) { /* undo any cursor shape requests */ - unset_cursor_shape_updates(screen); + disable_cursor_shape_updates(screen); } if (check_user_input(dt, tile_diffs, &cnt)) { /* true means loop back for more input */ @@ -8430,8 +12794,14 @@ static void watch_loop(void) { clean_up_exit(0); } + if (do_copy_screen) { + do_copy_screen = 0; + copy_screen(); + } + check_xevents(); check_connect_inputs(); + check_padded_fb(); if (! screen->clientHead) { /* waiting for a client */ usleep(200 * 1000); @@ -8497,15 +12867,17 @@ static void print_help(void) { "\n" " vncviewer far-host:0\n" "\n" -"Once x11vnc establishes connections with the X11 server and starts\n" -"listening as a VNC server it will print out a string: PORT=XXXX where\n" -"XXXX is typically 5900 (the default VNC port). One would next run something\n" -"like this on the local machine: \"vncviewer host:N\" where N is XXXX - 5900,\n" -"i.e. usually \"vncviewer host:0\"\n" +"Once x11vnc establishes connections with the X11 server and starts listening\n" +"as a VNC server it will print out a string: PORT=XXXX where XXXX is typically\n" +"5900 (the default VNC server port). One would next run something like\n" +"this on the local machine: \"vncviewer hostname:N\" where \"hostname\" is\n" +"the name of the machine running x11vnc and N is XXXX - 5900, i.e. usually\n" +"\"vncviewer hostname:0\".\n" "\n" -"By default x11vnc will not allow the screen to be shared and it will\n" -"exit as soon as a client disconnects. See -shared and -forever below\n" -"to override these protections.\n" +"By default x11vnc will not allow the screen to be shared and it will exit\n" +"as soon as a client disconnects. See -shared and -forever below to override\n" +"these protections. See the FAQ on how to tunnel the VNC connection through\n" +"an encrypted channel such as ssh(1).\n" "\n" "For additional info see: http://www.karlrunge.com/x11vnc/\n" " and http://www.karlrunge.com/x11vnc/#faq\n" @@ -8532,36 +12904,54 @@ static void print_help(void) { "\n" "-id windowid Show the window corresponding to \"windowid\" not\n" " the entire display. New windows like popup menus,\n" -" etc may not be seen, or will be clipped. x11vnc may\n" -" crash if the window changes size, is iconified, etc.\n" -" Use xwininfo(1) to get the window id. Primarily useful\n" -" for exporting very simple applications.\n" +" transient toplevels, etc, may not be seen or may be\n" +" clipped. Disabling SaveUnders or BackingStore in the\n" +" X server may help show them. x11vnc may crash if the\n" +" window is initially partially obscured, changes size,\n" +" is iconified, etc. Some steps are taken to avoid this\n" +" and the -xrandr mechanism is used to track resizes. Use\n" +" xwininfo(1) to get the window id, or use \"-id pick\"\n" +" to have x11vnc run xwininfo(1) for you and extract\n" +" the id. The -id option is useful for exporting very\n" +" simple applications (e.g. the current view on a webcam).\n" "-sid windowid As -id, but instead of using the window directly it\n" -" shifts a root view to it: this shows saveUnders menus,\n" +" shifts a root view to it: this shows SaveUnders menus,\n" " etc, although they will be clipped if they extend beyond\n" " the window.\n" "-flashcmap In 8bpp indexed color, let the installed colormap flash\n" " as the pointer moves from window to window (slow).\n" "-notruecolor For 8bpp displays, force indexed color (i.e. a colormap)\n" " even if it looks like 8bpp TrueColor. (rare problem)\n" +"-visual n Experimental option: probably does not do what you\n" +" think. It simply *forces* the visual used for the\n" +" framebuffer; this may be a bad thing... (e.g. messes\n" +" up colors or cause a crash). It is useful for testing\n" +" and for some workarounds. n may be a decimal number,\n" +" or 0x hex. Run xdpyinfo(1) for the values. One may\n" +" also use \"TrueColor\", etc. see <X11/X.h> for a list.\n" +" If the string ends in \":m\" for better or for worse\n" +" the visual depth is forced to be m.\n" "-overlay Handle multiple depth visuals on one screen, e.g. 8+24\n" " and 24+8 overlay visuals (the 32 bits per pixel are\n" " packed with 8 for PseudoColor and 24 for TrueColor).\n" "\n" -" Currently -overlay only works on Solaris (it uses\n" -" XReadScreen(3X11)). There is a problem with image\n" -" \"bleeding\" around transient popup menus (but not\n" -" for the menu itself): a workaround is to disable\n" -" SaveUnders by passing the \"-su\" argument to Xsun\n" -" (in /etc/dt/config/Xservers, say). Also note that,\n" -" the mouse cursor shape is exactly correct in this mode.\n" +" Currently -overlay only works on Solaris via\n" +" XReadScreen(3X11) and IRIX using XReadDisplay(3).\n" +" On Solaris there is a problem with image \"bleeding\"\n" +" around transient popup menus (but not for the menu\n" +" itself): a workaround is to disable SaveUnders\n" +" by passing the \"-su\" argument to Xsun (in\n" +" /etc/dt/config/Xservers). Also note that the mouse\n" +" cursor shape is exactly correct in this mode.\n" "\n" " Use -overlay as a workaround for situations like these:\n" -" Some legacy applications require the default visual\n" +" Some legacy applications require the default visual to\n" " be 8bpp (8+24), or they will use 8bpp PseudoColor even\n" " when the default visual is depth 24 TrueColor (24+8).\n" " In these cases colors in some windows will be messed\n" -" up in x11vnc unless -overlay is used.\n" +" up in x11vnc unless -overlay is used. Another use of\n" +" -overlay is to enable showing the exact mouse cursor\n" +" shape (details below).\n" "\n" " Under -overlay, performance will be somewhat degraded\n" " due to the extra image transformations required.\n" @@ -8571,22 +12961,15 @@ static void print_help(void) { " visual (some apps have -use24 or -visual options).\n" "-overlay_nocursor Sets -overlay, but does not try to draw the exact mouse\n" " cursor shape using the overlay mechanism.\n" -"-visual n Experimental option: probably does not do what you\n" -" think. It simply *forces* the visual used for the\n" -" framebuffer; this may be a bad thing... It is useful for\n" -" testing and for some workarounds. n may be a decimal\n" -" number, or 0x hex. Run xdpyinfo(1) for the values.\n" -" One may also use \"TrueColor\", etc. see <X11/X.h>\n" -" for a list. If the string ends in \":m\" for better\n" -" or for worse the visual depth is forced to be m.\n" "\n" -"-scale fraction Scale the framebuffer by factor \"fraction\".\n" -" Values less than 1 shrink the fb. Note: image may not\n" -" be sharp and response may be slower. Currently the\n" -" cursor shape is not scaled. If \"fraction\" contains\n" -" a decimal point \".\" it is taken as a floating point\n" -" number, alternatively the notation \"m/n\" may be used\n" -" to denote fractions exactly, e.g. -scale 2/3.\n" +"-scale fraction Scale the framebuffer by factor \"fraction\". Values\n" +" less than 1 shrink the fb, larger ones expand it.\n" +" Note: image may not be sharp and response may be\n" +" slower. Currently the cursor shape is not scaled.\n" +" If \"fraction\" contains a decimal point \".\" it\n" +" is taken as a floating point number, alternatively\n" +" the notation \"m/n\" may be used to denote fractions\n" +" exactly, e.g. -scale 2/3.\n" "\n" " Scaling Options: can be added after \"fraction\" via\n" " \":\", to supply multiple \":\" options use commas.\n" @@ -8605,6 +12988,10 @@ static void print_help(void) { " disconnects, opposite of -forever. This is the Default.\n" "-forever Keep listening for more connections rather than exiting\n" " as soon as the first client(s) disconnect. Same as -many\n" +"-inetd Launched by inetd(1): stdio instead of listening socket.\n" +" Note: if you are not redirecting stderr to a log file\n" +" (via shell 2> or -o option) you must also specify the\n" +" -q option, otherwise the stderr goes to the viewer.\n" "-connect string For use with \"vncviewer -listen\" reverse connections.\n" " If \"string\" has the form \"host\" or \"host:port\"\n" " the connection is made once at startup. Use commas\n" @@ -8616,21 +13003,17 @@ static void print_help(void) { "-novncconnect VNC program vncconnect(1). When the property is\n" " set to \"host\" or \"host:port\" establish a reverse\n" " connection. Using xprop(1) instead of vncconnect may\n" -" work, see the FAQ. Default: %s\n" -"-inetd Launched by inetd(1): stdio instead of listening socket.\n" -" Note: if you are not redirecting stderr to a log file\n" -" (via shell 2> or -o option) you must also specify the\n" -" -q option.\n" +" work (see the FAQ). Default: %s\n" "\n" -"-allow addr1[,addr2..] Only allow client connections from IP addresses matching\n" -" the comma separated list of numerical addresses.\n" -" Can be a prefix, e.g. \"192.168.100.\" to match a\n" -" simple subnet, for more control build libvncserver\n" -" with libwrap support. If the list contains a \"/\"\n" -" it instead is a interpreted as a file containing\n" -" addresses or prefixes that is re-read each time a new\n" -" client connects. Lines can be commented out with the\n" -" \"#\" character in the usual way.\n" +"-allow host1[,host2..] Only allow client connections from hosts matching\n" +" the comma separated list of hostnames or IP addresses.\n" +" Can also be a numerical IP prefix, e.g. \"192.168.100.\"\n" +" to match a simple subnet, for more control build\n" +" libvncserver with libwrap support (See the FAQ). If the\n" +" list contains a \"/\" it instead is a interpreted as a\n" +" file containing addresses or prefixes that is re-read\n" +" each time a new client connects. Lines can be commented\n" +" out with the \"#\" character in the usual way.\n" "-localhost Same as -allow 127.0.0.1\n" "-viewpasswd string Supply a 2nd password for view-only logins. The -passwd\n" " (full-access) password must also be supplied.\n" @@ -8639,7 +13022,7 @@ static void print_help(void) { " If a second non blank line exists in the file it is\n" " taken as a view-only password (i.e. -viewpasswd) Note:\n" " this is a simple plaintext passwd, see also -rfbauth\n" -" and -storepasswd below.\n" +" and -storepasswd below for obfuscated passwords.\n" "-storepasswd pass file Store password \"pass\" as the VNC password in the\n" " file \"file\". Once the password is stored the\n" " program exits. Use the password via \"-rfbauth file\"\n" @@ -8648,10 +13031,10 @@ static void print_help(void) { " should be allowed to connect or not. \"string\" is\n" " an external command run via system(3) or some special\n" " cases described below. Be sure to quote \"string\"\n" -" if it contains spaces, etc. If the external command\n" -" returns 0 the client is accepted, otherwise the client\n" -" is rejected. See below for an extension to accept a\n" -" client view-only.\n" +" if it contains spaces, shell characters, etc. If the\n" +" external command returns 0 the client is accepted,\n" +" otherwise the client is rejected. See below for an\n" +" extension to accept a client view-only.\n" "\n" " Environment: The RFB_CLIENT_IP environment variable will\n" " be set to the incoming client IP number and the port\n" @@ -8661,7 +13044,7 @@ static void print_help(void) { " of the tcp virtual circuit. The x11vnc process\n" " id will be in RFB_X11VNC_PID, a client id number in\n" " RFB_CLIENT_ID, and the number of other connected clients\n" -" in RFB_CLIENT_COUNT.\n" +" in RFB_CLIENT_COUNT. RFB_MODE will be \"accept\"\n" "\n" " If \"string\" is \"popup\" then a builtin popup window\n" " is used. The popup will time out after 120 seconds,\n" @@ -8669,7 +13052,8 @@ static void print_help(void) { " (use 0 for no timeout)\n" "\n" " If \"string\" is \"xmessage\" then an xmessage(1)\n" -" invocation is used for the command.\n" +" invocation is used for the command. xmessage must be\n" +" installed on the machine for this to work.\n" "\n" " Both \"popup\" and \"xmessage\" will present an option\n" " for accepting the client \"View-Only\" (the client\n" @@ -8685,19 +13069,23 @@ static void print_help(void) { " the default action (in case the command returns an\n" " unexpected value). E.g. \"no:*\" is a good choice.\n" "\n" -" Note that x11vnc blocks while the external command or\n" +" Note that x11vnc blocks while the external command\n" " or popup is running (other clients may see no updates\n" " during this period).\n" "\n" " More -accept tricks: use \"popupmouse\" to only allow\n" " mouse clicks in the builtin popup to be recognized.\n" -" Similarly use \"popupkey\" to only recognize keystroke\n" -" responses. All 3 of the popup keywords can be followed\n" +" Similarly use \"popupkey\" to only recognize\n" +" keystroke responses. These are to help avoid the\n" +" user accidentally accepting a client by typing or\n" +" clicking. All 3 of the popup keywords can be followed\n" " by +N+M to supply a position for the popup window.\n" " The default is to center the popup window.\n" "-gone string As -accept, except to run a user supplied command when\n" -" a client goes away (disconnects). Unlike -accept,\n" -" the command return code is not interpreted by x11vnc.\n" +" a client goes away (disconnects). RFB_MODE will be\n" +" set to \"gone\" and the other RFB_* variables are as\n" +" in -accept. Unlike -accept, the command return code\n" +" is not interpreted by x11vnc. Example: -gone 'xlock &'\n" "\n" "-noshm Do not use the MIT-SHM extension for the polling.\n" " Remote displays can be polled this way: be careful this\n" @@ -8720,8 +13108,44 @@ static void print_help(void) { " In general on XINERAMA displays you may need to use the\n" " -xwarppointer option if the mouse pointer misbehaves.\n" "\n" +"-xrandr [mode] If the display supports the XRANDR (X Resize, Rotate\n" +" and Reflection) extension, and you expect XRANDR events\n" +" to occur to the display while x11vnc is running, this\n" +" options indicates x11vnc should try to respond to\n" +" them (as opposed to simply crashing by assuming the\n" +" old screen size). See the xrandr(1) manpage and run\n" +" 'xrandr -q' for more info. [mode] is optional and\n" +" described below.\n" +"\n" +" Since watching for XRANDR events and errors increases\n" +" polling overhead, only use this option if XRANDR changes\n" +" are expected. For example on a rotatable screen PDA or\n" +" laptop, or using a XRANDR-aware Desktop where you resize\n" +" often. It is best to be viewing with a vncviewer that\n" +" supports the NewFBSize encoding, since it knows how to\n" +" react to screen size changes. Otherwise, libvncserver\n" +" tries to do so something reasonable for viewers that\n" +" cannot do this (portions of the screen may be clipped,\n" +" unused, etc).\n" +"\n" +" \"mode\" defaults to \"resize\", which means create a\n" +" new, resized, framebuffer and hope all viewers can cope\n" +" with the change. \"newfbsize\" means first disconnect\n" +" all viewers that do not support the NewFBSize VNC\n" +" encoding, and then resize the framebuffer. \"exit\"\n" +" means disconnect all viewer clients, and then terminate\n" +" x11vnc.\n" +"-padgeom WxH Whenever a new vncviewer connects, the framebuffer is\n" +" replaced with a fake, solid black one of geometry WxH.\n" +" Shortly afterwards the framebuffer is replaced with the\n" +" real one. This is intended for use with vncviewers\n" +" that do not support NewFBSize and one wants to make\n" +" sure the initial viewer geometry will be big enough\n" +" to handle all subsequent resizes (e.g. under -xrandr,\n" +" -remote id:windowid, rescaling, etc.\n" +"\n" "-o logfile Write stderr messages to file \"logfile\" instead of\n" -" to the terminal. Same as -logfile \"file\".\n" +" to the terminal. Same as \"-logfile file\".\n" "-rc filename Use \"filename\" instead of $HOME/.x11vncrc for rc file.\n" "-norc Do not process any .x11vncrc file for options.\n" "-h, -help Print this help text.\n" @@ -8749,16 +13173,18 @@ static void print_help(void) { "-isolevel3 When in modtweak mode, always send ISO_Level3_Shift to\n" " the X server instead of Mode_switch (AltGr).\n" #endif -"-xkb When in modtweak mode, use the XKEYBOARD extension\n" -" (if it exists) to do the modifier tweaking. This is\n" -" powerful and should be tried if there are still\n" -" keymapping problems when using the simpler -modtweak.\n" -"-skip_keycodes string Skip keycodes not on your keyboard but your X server\n" -" thinks exist. Currently only applies to -xkb mode.\n" -" \"string\" is a comma separated list of decimal\n" -" keycodes. Use this option to help x11vnc in the reverse\n" -" problem it tries to solve: Keysym -> Keycode(s) when\n" -" ambiguities exist. E.g. -skip_keycodes 94,114\n" +"-xkb When in modtweak mode, use the XKEYBOARD extension (if\n" +" the X display supports it) to do the modifier tweaking.\n" +" This is powerful and should be tried if there are still\n" +" keymapping problems when using -modtweak by itself.\n" +"-skip_keycodes string Ignore the comma separated list of decimal keycodes.\n" +" Perhaps these are keycodes not on your keyboard but\n" +" your X server thinks exist. Currently only applies\n" +" to -xkb mode. Use this option to help x11vnc in the\n" +" reverse problem it tries to solve: Keysym -> Keycode(s)\n" +" when ambiguities exist (more than one Keycode per\n" +" Keysym). Run 'xmodmap -pk' to see your keymapping.\n" +" E.g. \"-skip_keycodes 94,114\"\n" "-add_keysyms If a Keysym is received from a VNC viewer and\n" " that Keysym does not exist in the X server, then\n" " add the Keysym to the X server's keyboard mapping.\n" @@ -8785,8 +13211,8 @@ static void print_help(void) { " form: key1-key2,key3-key4,... See <X11/keysymdef.h>\n" " header file for a list of Keysym names, or use\n" " xev(1). To map a key to a button click, use the\n" -" fake Keysyms \"Button1\", ..., etc.\n" -" E.g. -remap Super_R-Button2\n" +" fake Keysyms \"Button1\", ..., etc. E.g. \"-remap\n" +" Super_R-Button2\" (useful for pasting on a laptop)\n" "-norepeat Option -norepeat disables X server key auto repeat\n" "-repeat when VNC clients are connected. This works around a\n" " repeating keystrokes bug (triggered by long processing\n" @@ -8819,26 +13245,35 @@ static void print_help(void) { " network traffic by not having to send the cursor image\n" " every time the pointer is moved), in which case these\n" " extensions are used (see -nocursorshape and -nocursorpos\n" -" below). For other viewers the cursor shape is written\n" -" directly to the framebuffer every time the pointer is\n" -" moved or changed and gets sent along with the other\n" -" framebuffer updates. In this case, there will be\n" -" some lag between the vnc viewer pointer and the remote\n" -" cursor position.\n" +" below to disable). For other viewers the cursor shape\n" +" is written directly to the framebuffer every time the\n" +" pointer is moved or changed and gets sent along with\n" +" the other framebuffer updates. In this case, there\n" +" will be some lag between the vnc viewer pointer and\n" +" the remote cursor position.\n" "\n" " If the X display supports retrieving the cursor shape\n" -" information from the X server, then the default\n" -" is to use that mode. On Solaris this requires\n" -" the SUN_OVL extension and the -overlay option to be\n" -" supplied. (see also the -overlay_nomouse option). (Soon)\n" -" on XFree86/Xorg the XFIXES extension is required.\n" -" Either can be disabled with -nocursor, and also some\n" -" values of the \"mode\" option below.\n" +" information from the X server, then the default is\n" +" to use that mode. On Solaris this can be done with\n" +" the SUN_OVL extension using -overlay (see also the\n" +" -overlay_nomouse option). A similar overlay scheme\n" +" is used on IRIX. Xorg (e.g. Linux) and recent Solaris\n" +" Xsun servers support the XFIXES extension to retrieve\n" +" the exact cursor shape from the X server. If XFIXES\n" +" is present it is preferred over Overlay and is used by\n" +" default (see -noxfixes below). This can be disabled\n" +" with -nocursor, and also some values of the \"mode\"\n" +" option below.\n" "\n" " The \"mode\" string can be used to fine-tune the\n" " displaying of cursor shapes. It can be used the\n" " following ways:\n" "\n" +" \"-cursor arrow\" - just show the standard arrow\n" +" nothing more or nothing less.\n" +"\n" +" \"-cursor none\" - same as \"-nocursor\"\n" +"\n" " \"-cursor X\" - when the cursor appears to be on the\n" " root window, draw the familiar X shape. Some desktops\n" " such as GNOME cover up the root window completely,\n" @@ -8855,21 +13290,24 @@ static void print_help(void) { " more feedback about the cursor shape.\n" "\n" " \"-cursor most\" - try to show as many cursors as\n" -" possible. Often this will only be the same as \"some\".\n" -" On Solaris if XFIXES is not available, -overlay mode\n" -" will be used.\n" +" possible. Often this will only be the same as \"some\"\n" +" unless the display has overlay visuals or XFIXES\n" +" extensions available. On Solaris and IRIX if XFIXES\n" +" is not available, -overlay mode will be attempted.\n" "\n" +"-noxfixes Do not use the XFIXES extension to draw the exact cursor\n" +" shape even if it is available.\n" "-nocursorshape Do not use the TightVNC CursorShapeUpdates extension\n" " even if clients support it. See -cursor above.\n" "-cursorpos Option -cursorpos enables sending the X cursor position\n" "-nocursorpos back to all vnc clients that support the TightVNC\n" " CursorPosUpdates extension. Other clients will be able\n" " to see the pointer motions. Default: %s\n" -"-xwarppointer Move the pointer with XWarpPointer(3X) instead of XTEST\n" -" extension. Use this as a workaround if the pointer\n" -" motion behaves incorrectly, e.g. on touchscreens or\n" -" other non-standard setups. Also sometimes needed on\n" -" XINERAMA displays.\n" +"-xwarppointer Move the pointer with XWarpPointer(3X) instead of\n" +" the XTEST extension. Use this as a workaround\n" +" if the pointer motion behaves incorrectly, e.g.\n" +" on touchscreens or other non-standard setups.\n" +" Also sometimes needed on XINERAMA displays.\n" "\n" "-buttonmap string String to remap mouse buttons. Format: IJK-LMN, this\n" " maps buttons I -> L, etc., e.g. -buttonmap 13-31\n" @@ -8899,14 +13337,24 @@ static void print_help(void) { " improves response on slow setups, but you lose all\n" " visual feedback for drags, text selection, and some\n" " menu traversals.\n" -"-old_pointer Use the original pointer input handling mechanism.\n" -" See check_input() and pointer() in source file for\n" -" details.\n" -"-old_pointer2 The default pointer input handling algorithm was changed\n" -" again, this option indicates to use the second one.\n" -"-input_skip n For the old pointer handling when non-threaded: try to\n" +"-pointer_mode n Various pointer update schemes. The problem is pointer\n" +" motion can cause rapid changes on the screen, e.g. a\n" +" window drag. Neither x11vnc nor the bandwidth to the\n" +" vncviewers can keep up these rapid screen changes:\n" +" everything bogs down when dragging or scrolling.\n" +" Note that most video h/w is optimized for writing, not\n" +" reading (a 50X rate difference is possible) and x11vnc\n" +" is reading all the time. So a scheme has to be used to\n" +" \"eat\" much of that pointer input before re-polling the\n" +" screen. n can be 1 to %d. n=1 was the original scheme\n" +" used to about Jan 2004. n=2 is an improved scheme.\n" +" n=3 is basically a dynamic -nodragging mode: it detects\n" +" if the mouse drag motion has paused and refreshes\n" +" the display. n=4 is TBD. The default n is %d.\n" +"-input_skip n For the pointer handling when non-threaded: try to\n" " read n user input events before scanning display. n < 0\n" " means to act as though there is always user input.\n" +" Default: %d\n" "\n" "-debug_pointer Print debugging output for every pointer event.\n" "-debug_keyboard Print debugging output for every keyboard event.\n" @@ -8919,6 +13367,9 @@ static void print_help(void) { " down on load. Default: %d\n" "-nap Monitor activity and if low take longer naps between\n" " polls to really cut down load when idle. Default: %s\n" +"-sb time Time in seconds after NO activity (e.g. screen blank)\n" +" to really throttle down the screen polls (i.e. sleep\n" +" for about 1.5 secs). Use 0 to disable. Default: %d\n" "\n" "-sigpipe string Broken pipe (SIGPIPE) handling. \"string\" can be\n" " \"ignore\" or \"exit\". For \"ignore\" libvncserver\n" @@ -8937,6 +13388,313 @@ static void print_help(void) { " by checking the tile near the boundary. Default: %d\n" "-fuzz n Tolerance in pixels to mark a tiles edges as changed.\n" " Default: %d\n" +"\n" +"-gui [gui-opts] Start up a simple tcl/tk gui based on the the remote\n" +" control options -remote/-query described below.\n" +" Requires the \"wish\" program to be installed on the\n" +" machine. \"gui-opts\" is not required: the default is\n" +" to start up both the gui and x11vnc with the gui showing\n" +" up on the X display in the environment variable DISPLAY.\n" +"\n" +" \"gui-opts\" can be a comma separated list of items.\n" +" Currently there are only two types of items: 1) a gui\n" +" mode and 2) the X display the gui should display on.\n" +" The gui mode can be \"start\", \"conn\", or \"wait\"\n" +" \"start\" is the default mode above and is not required.\n" +" \"conn\" means do not automatically start up x11vnc,\n" +" but instead just try to connect to an existing x11vnc\n" +" process. \"wait\" means just start the gui and nothing\n" +" else (you will later instruct the gui to start x11vnc\n" +" or connect to an existing one.)\n" +"\n" +" Note the possible confusion regarding the potentially\n" +" two different X displays: x11vnc polls one, but you\n" +" may want the gui to appear on another. For example, if\n" +" you ssh in and x11vnc is not running yet you may want\n" +" the gui to come back to you via your ssh redirected X\n" +" display (e.g. localhost:10).\n" +"\n" +" Examples: \"x11vnc -gui\", \"x11vnc -gui localhost:10\",\n" +" \"x11vnc -gui :10\", \"x11vnc -gui wait,:10\",\n" +" \"x11vnc -gui <x11vnc-opts...>\"\n" +"\n" +" If you do not specify a gui X display in \"gui-opts\"\n" +" then the DISPLAY environment variable and -display\n" +" option are tried (in that order). Regarding the x11vnc\n" +" X display the gui will try to connect to, it first\n" +" tries -display and then DISPLAY. For example, \"x11vnc\n" +" -display :0 -gui otherhost:0\", will remote control an\n" +" x11vnc polling :0 and display the gui on otherhost:0\n" +"\n" +" If you do not intend to start x11vnc from the gui\n" +" (i.e. just remote control an existing one), then the\n" +" gui process can run on a different machine from the\n" +" x11vnc server as long as X permissions, etc. permit\n" +" communication between the two.\n" +"\n" +"-remote command Remotely control some aspects of an already running\n" +" x11vnc server. \"-R\" and \"-r\" are aliases for\n" +" \"-remote\". After the remote control command is\n" +" sent to the running server the 'x11vnc -remote ...'\n" +" command exits. You can often use the -query command\n" +" (see below) to see if the x11vnc server processed your\n" +" -remote command.\n" +"\n" +" The default communication channel is that of X\n" +" properties (specifically VNC_CONNECT), and so this\n" +" command must be run with correct settings for DISPLAY\n" +" and possibly XAUTHORITY to connect to the X server\n" +" and set the property. Alternatively, use the -display\n" +" and -auth options to set them to the correct values.\n" +" The running server cannot use the -novncconnect option\n" +" because that disables the communication channel.\n" +" See below for alternate channels.\n" +"\n" +" For example: 'x11vnc -remote stop' (which is the same as\n" +" 'x11vnc -R stop') will close down the x11vnc server.\n" +" 'x11vnc -R shared' will enable shared connections, and\n" +" 'x11vnc -R scale:3/4' will rescale the desktop.\n" +"\n" +" Note: the more drastic the change induced by the -remote\n" +" command, the bigger the chance for bugs or crashes.\n" +" Please report reproducible bugs.\n" +"\n" +" The following -remote/-R commands are supported:\n" +"\n" +" stop terminate the server, same as \"quit\"\n" +" \"exit\" or \"shutdown\"\n" +" ping see if the x11vnc server responds.\n" +" Return is: ans=ping:<xdisplay>\n" +" blacken try to push a black fb update to all\n" +" clients (due to timings a client\n" +" could miss it). Same as \"zero\", also\n" +" \"zero:x1,y1,x2,y2\" for a rectangle.\n" +" refresh send the entire fb to all clients.\n" +" reset recreate the fb, polling memory, etc.\n" +" id:windowid set -id window to \"windowid\". empty\n" +" or \"root\" to go back to root window\n" +" sid:windowid set -sid window to \"windowid\"\n" +" flashcmap enable -flashcmap mode.\n" +" noflashcmap disable -flashcmap mode.\n" +" notruecolor enable -notruecolor mode.\n" +" truecolor disable -notruecolor mode.\n" +" overlay enable -overlay mode (if applicable).\n" +" nooverlay disable -overlay mode.\n" +" overlay_cursor in -overlay mode, enable cursor drawing.\n" +" overlay_nocursor disable cursor drawing. same as\n" +" nooverlay_cursor.\n" +" visual:vis set -visual to \"vis\"\n" +" scale:frac set -scale to \"frac\"\n" +" viewonly enable -viewonly mode.\n" +" noviewonly disable -viewonly mode.\n" +" shared enable -shared mode.\n" +" noshared disable -shared mode.\n" +" forever enable -forever mode.\n" +" noforever disable -forever mode.\n" +" deny deny any new connections, same as \"lock\"\n" +" nodeny allow new connections, same as \"unlock\"\n" +" connect:host do reverse connection to host, \"host\"\n" +" may be a comma separated list of hosts\n" +" or host:ports. See -connect.\n" +" disconnect:host disconnect any clients from \"host\"\n" +" same as \"close:host\". Use host\n" +" \"all\" to close all current clients.\n" +" If you know the client internal hex ID,\n" +" e.g. 0x3 (returned by -query clients and\n" +" RFB_CLIENT_ID), you can use that too.\n" +" allowonce:host For the next connection only, allow\n" +" connection from \"host\".\n" +" allow:hostlist set -allow list to (comma separated)\n" +" \"hostlist\". See -allow and -localhost.\n" +" Do not use with -allow /path/to/file\n" +" Use \"+host\" to add a single host, and\n" +" use \"-host\" to delete a single host\n" +" localhost enable -localhost mode\n" +" nolocalhost disable -localhost mode\n" +" accept:cmd set -accept \"cmd\" (empty to disable).\n" +" gone:cmd set -gone \"cmd\" (empty to disable).\n" +" noshm enable -noshm mode.\n" +" shm disable -noshm mode (i.e. use shm).\n" +" flipbyteorder enable -flipbyteorder mode, you may need\n" +" to set noshm for this to do something.\n" +" noflipbyteorder disable -flipbyteorder mode.\n" +" onetile enable -onetile mode. (you may need to\n" +" set shm for this to do something)\n" +" noonetile disable -onetile mode.\n" +" blackout:str set -blackout \"str\" (empty to disable).\n" +" See -blackout for the form of \"str\"\n" +" (basically: WxH+X+Y,...)\n" +" Use \"+WxH+X+Y\" to append a single\n" +" rectangle use \"-WxH+X+Y\" to delete one\n" +" xinerama enable -xinerama mode. (if applicable)\n" +" noxinerama disable -xinerama mode.\n" +" xrandr enable -xrandr mode. (if applicable)\n" +" noxrandr disable -xrandr mode.\n" +" xrandr_mode:mode set the -xrandr mode to \"mode\".\n" +" padgeom:WxH set -padgeom to WxH (empty to disable)\n" +" If WxH is \"force\" or \"do\" the padded\n" +" geometry fb is immediately applied.\n" +" quiet enable -quiet mode.\n" +" noquiet disable -quiet mode.\n" +" modtweak enable -modtweak mode.\n" +" nomodtweak enable -nomodtweak mode.\n" +" xkb enable -xkb modtweak mode.\n" +" noxkb disable -xkb modtweak mode.\n" +" skip_keycodes:str enable -xkb -skip_keycodes \"str\".\n" +" add_keysyms enable -add_keysyms mode.\n" +" noadd_keysyms stop adding keysyms. those added will\n" +" still be removed at exit.\n" +" clear_mods enable -clear_mods mode and clear them.\n" +" noclear_mods disable -clear_mods mode.\n" +" clear_keys enable -clear_keys mode and clear them.\n" +" noclear_keys disable -clear_keys mode.\n" +" remap:str set -remap \"str\" (empty to disable).\n" +" See -remap for the form of \"str\"\n" +" (basically: key1-key2,key3-key4,...)\n" +" Use \"+key1-key2\" to append a single\n" +" keymapping, use \"-key1-key2\" to delete.\n" +" norepeat enable -norepeat mode.\n" +" repeat disable -norepeat mode.\n" +" bell enable bell (if supported).\n" +" nobell disable bell.\n" +" sel disable -nosel mode.\n" +" nosel enable -nosel mode.\n" +" primary disable -noprimary mode.\n" +" noprimary enable -noprimary mode.\n" +" cursor:mode enable -cursor \"mode\".\n" +" show_cursor enable showing a cursor.\n" +" noshow_cursor disable showing a cursor. (same as\n" +" \"nocursor\")\n" +" xfixes enable xfixes cursor shape mode.\n" +" noxfixes disable xfixes cursor shape mode.\n" +" cursorshape disable -nocursorshape mode.\n" +" nocursorshape enable -nocursorshape mode.\n" +" cursorpos disable -nocursorpos mode.\n" +" nocursorpos enable -nocursorpos mode.\n" +" xwarp enable -xwarppointer mode.\n" +" noxwarp disable -xwarppointer mode.\n" +" buttonmap:str set -buttonmap \"str\", empty to disable\n" +" dragging disable -nodragging mode.\n" +" nodragging enable -nodragging mode.\n" +" pointer_mode n set -pointer_mode to n.\n" +" input_skip n set -input_skip to n.\n" +" debug_pointer enable -debug_pointer, same as \"dp\"\n" +" nodebug_pointer disable -debug_pointer, same as \"nodp\"\n" +" debug_keyboard enable -debug_keyboard, same as \"dk\"\n" +" nodebug_keyboard disable -debug_keyboard, same as \"nodk\"\n" +" defer:n set -defer to n ms,same as deferupdate:n\n" +" wait:n set -wait to n ms.\n" +" nap enable -nap mode.\n" +" nonap disable -nap mode.\n" +" sb:n set -sb to n s, same as screen_blank:n\n" +" fs:frac set -fs fraction to \"frac\", e.g. 0.5\n" +" gaps:n set -gaps to n.\n" +" grow:n set -grow to n.\n" +" fuzz:n set -fuzz to n.\n" +" progressive:n set libvncserver -progressive slice\n" +" height parameter to n.\n" +" file:name run -remote commands from file \"name\",\n" +" one command per line,blank and # skipped\n" +" noremote disable the -remote command processing,\n" +" it cannot be turned back on.\n" +"\n" +" The vncconnect(1) command from standard VNC\n" +" distributions may also be used if string is prefixed\n" +" with \"cmd=\" E.g. 'vncconnect cmd=stop'. Under some\n" +" circumstances xprop(1) can used if it supports -set\n" +" (see the FAQ).\n" +"\n" +" If \"-connect /path/to/file\" has been supplied to the\n" +" running x11vnc server then that file can be used as a\n" +" communication channel (this is the only way to remote\n" +" control one of many x11vnc's polling the same X display)\n" +" Simply run: 'x11vnc -connect /path/to/file -remote ...'\n" +" or you can directly write to the file via something\n" +" like: \"echo cmd=stop > /path/to/file\", etc.\n" +"\n" +"-query variable Like -remote, except just query the value of\n" +" \"variable\". \"-Q\" is an alias for \"-query\".\n" +" Multiple queries can be done by separating variables\n" +" by commas, e.g. -query var1,var2. The results come\n" +" back in the form ans=var1:value1,ans=var2:value2,...\n" +" to the standard output. If a variable is read-only,\n" +" it comes back with prefix \"aro=\" instead of \"ans=\".\n" +"\n" +" Some -remote commands are pure actions that do not make\n" +" sense as variables, e.g. \"stop\" or \"disconnect\",\n" +" in these cases the value returned is \"N/A\". To direct\n" +" a query straight to the VNC_CONNECT property or connect\n" +" file use \"qry=...\" instead of \"cmd=...\"\n" +"\n" +" Here is the current list of \"variables\" that can\n" +" be supplied to the -query command. This includes the\n" +" \"N/A\" ones that return no useful info. For variables\n" +" names that do not correspond to an x11vnc option or\n" +" remote command, we hope the name makes it obvious what\n" +" the returned value corresponds to (hint: the ext_*\n" +" variables correspond to the presence of X extensions):\n" +"\n" +" ans= stop quit exit shutdown ping blacken zero refresh\n" +" reset close disconnect id sid flashcmap noflashcmap\n" +" truecolor notruecolor overlay nooverlay overlay_cursor\n" +" overlay_yescursor nooverlay_cursor overlay_nocursor\n" +" visual scale viewonly noviewonly shared noshared\n" +" forever noforever once deny lock nodeny unlock connect\n" +" allowonce allow localhost nolocalhost accept gone shm\n" +" noshm flipbyteorder noflipbyteorder onetile noonetile\n" +" blackout xinerama noxinerama xrandr noxrandr xrandr_mode\n" +" padgeom quiet q noquiet modtweak nomodtweak xkb noxkb\n" +" skip_keycodes add_keysyms noadd_keysyms clear_mods\n" +" noclear_mods clear_keys noclear_keys remap repeat\n" +" norepeat bell nobell sel nosel primary noprimary\n" +" cursorshape nocursorshape cursorpos nocursorpos cursor\n" +" show_cursor noshow_cursor nocursor xfixes noxfixes xwarp\n" +" xwarppointer noxwarp noxwarppointer buttonmap dragging\n" +" nodragging pointer_mode input_skip debug_pointer dp\n" +" nodebug_pointer nodp debug_keyboard dk nodebug_keyboard\n" +" nodk deferupdate defer wait nap nonap sb screen_blank\n" +" fs gaps grow fuzz progressive noremote\n" +"\n" +" aro= display vncdisplay desktopname desktop auth\n" +" rootshift scale_str scaled_x scaled_y scale_numer\n" +" scale_denom scale_fac scaling_noblend scaling_nomult4\n" +" scaling_pad scaling_interpolate inetd safer unsafe\n" +" passwdfile using_shm logfile o rc norc h help V version\n" +" lastmod bg nofb sigpipe threads clients client_count\n" +" pid ext_xtest ext_xkb ext_xshm ext_xinerama ext_overlay\n" +" ext_xfixes ext_xdamage ext_xrandr rootwin num_buttons\n" +" button_mask mouse_x mouse_y bpp depth indexed_color\n" +" dpy_x dpy_y rfbport rfbwait rfbauth passwd alwaysshared\n" +" dontdisconnect httpdir enablehttpproxy\n" +"\n" +"-noremote Do not process any remote control commands or queries.\n" +"\n" +" A note about security wrt remote control commands.\n" +" If someone can connect to the X display and change the\n" +" property VNC_CONNECT, then they can remotely control\n" +" x11vnc. Normally access to the X display is protected.\n" +" Note that if they can modify VNC_CONNECT, they could\n" +" also run their own x11vnc and have complete control\n" +" of the desktop. If the \"-connect /path/to/file\"\n" +" channel is being used, obviously anyone who can write\n" +" to /path/to/file can remotely control x11vnc. So be\n" +" sure to protect the X display and that file's write\n" +" permissions.\n" +"\n" +"-unsafe If x11vnc is running as root (e.g. inetd or Xsetup for\n" +" a display manager) a few remote commands are disabled\n" +" (currently: id:pick, accept:<cmd>, and gone:<cmd>)\n" +" because they are associated with running external\n" +" programs. If you specify -unsafe, then these remote\n" +" control commands are allowed when running as root.\n" +" When running as non-root all commands are allowed.\n" +" See -safer below.\n" +"-safer Even if not running as root, disable the above unsafe\n" +" remote control commands.\n" +"\n" +"-deny_all For use with -remote nodeny: start out denying all\n" +" incoming clients until \"-remote nodeny\" is used to\n" +" let them in.\n" "%s\n" "\n" "These options are passed to libvncserver:\n" @@ -8951,9 +13709,12 @@ static void print_help(void) { use_modifier_tweak ? "-modtweak":"-nomodtweak", no_autorepeat ? "-norepeat":"-repeat", cursor_pos_updates ? "-cursorpos":"-nocursorpos", + pointer_mode_max, pointer_mode, + ui_skip, defer_update, waitms, take_naps ? "on":"off", + screen_blank, use_threads ? "-threads":"-nothreads", fs_frac, gaps_fill, @@ -8971,7 +13732,7 @@ static void print_help(void) { */ #define MAXN 256 -static char *this_host(void) { +char *this_host(void) { char host[MAXN]; #ifdef LIBVNCSERVER_HAVE_GETHOSTNAME if (gethostname(host, MAXN) == 0) { @@ -9001,7 +13762,7 @@ static char *choose_title(char *display) { } } strncat(title, display, MAXN - strlen(title)); - if (subwin) { + if (subwin && valid_window(subwin)) { char *name; if (XFetchName(dpy, subwin, &name)) { strncat(title, " ", MAXN - strlen(title)); @@ -9036,6 +13797,7 @@ static int limit_shm(void) { return limit; } + /* * quick-n-dirty ~/.x11vncrc: each line (except # comments) is a cmdline option. */ @@ -9043,7 +13805,7 @@ static int argc2 = 0; static char **argv2; static void check_rcfile(int argc, char **argv) { - int i, norc = 0, argmax = 1024; + int i, pwlast, norc = 0, argmax = 1024; char *infile = NULL; char rcfile[1024]; FILE *rc; @@ -9062,10 +13824,12 @@ static void check_rcfile(int argc, char **argv) { } } } + rc_norc = norc; if (norc) { ; } else if (infile != NULL) { rc = fopen(infile, "r"); + rc_rcfile = strdup(infile); if (rc == NULL) { fprintf(stderr, "could not open rcfile: %s\n", infile); perror("fopen"); @@ -9078,6 +13842,7 @@ static void check_rcfile(int argc, char **argv) { strcat(rcfile, "/.x11vncrc"); infile = rcfile; rc = fopen(rcfile, "r"); + rc_rcfile = strdup(rcfile); if (rc == NULL) { norc = 1; } @@ -9188,8 +13953,21 @@ static void check_rcfile(int argc, char **argv) { fclose(rc); free(buf); } + pwlast = 0; for (i=1; i < argc; i++) { argv2[argc2++] = strdup(argv[i]); + + if (pwlast || !strcmp("-passwd", argv[i]) + || !strcmp("-viewpasswd", argv[i])) { + char *p = argv[i]; + if (pwlast) { + pwlast = 0; + } else { + pwlast = 1; + } + while (*p != '\0') + *p++ = '\0'; + } if (argc2 >= argmax) { fprintf(stderr, "too many rcfile options\n"); exit(1); @@ -9199,28 +13977,47 @@ static void check_rcfile(int argc, char **argv) { int main(int argc, char* argv[]) { - XImage *fb; - int i; + int i, len; int ev, er, maj, min; - char *use_dpy = NULL; - char *auth_file = NULL; - char *arg, *visual_str = NULL; - char *logfile = NULL; - char *passwdfile = NULL; - char *blackout_string = NULL; - char *remap_file = NULL; - char *pointer_remap = NULL; + char *arg; + char *remote_cmd = NULL; + char *gui_string = NULL; + int remote_query = 0; int pw_loc = -1; int vpw_loc = -1; - int dt = 0; - int bg = 0; - int got_rfbwait = 0; - int got_deferupdate = 0, got_defer = 0; + int dt = 0, bg = 0; + int got_rfbwait = 0, got_deferupdate = 0, got_defer = 0; /* used to pass args we do not know about to rfbGetScreen(): */ int argc_vnc = 1; char *argv_vnc[128]; + /* if we are root limit some remote commands: */ + if (!getuid() || !geteuid()) { + safe_remote_only = 1; + } + argv_vnc[0] = strdup(argv[0]); + program_name = strdup(argv[0]); + + len = 0; + for (i=1; i < argc; i++) { + len += strlen(argv[i]) + 4 + 1; + } + program_cmdline = (char *)malloc(len+1); + program_cmdline[0] = '\0'; + for (i=1; i < argc; i++) { + char *s = argv[i]; + if (program_cmdline[0]) { + strcat(program_cmdline, " "); + } + if (*s == '-') { + strcat(program_cmdline, s); + } else { + strcat(program_cmdline, "{{"); + strcat(program_cmdline, s); + strcat(program_cmdline, "}}"); + } + } check_rcfile(argc, argv); /* kludge for the new array argv2 set in check_rcfile() */ @@ -9241,110 +14038,83 @@ int main(int argc, char* argv[]) { if (!strcmp(arg, "-display")) { CHECK_ARGC - use_dpy = argv[++i]; - } else if (!strcmp(arg, "-id")) { + use_dpy = strdup(argv[++i]); + } else if (!strcmp(arg, "-auth")) { CHECK_ARGC - if (sscanf(argv[++i], "0x%x", &subwin) != 1) { - if (sscanf(argv[i], "%d", &subwin) != 1) { - fprintf(stderr, "bad -id arg: %s\n", - argv[i]); - exit(1); - } - } - } else if (!strcmp(arg, "-sid")) { - rootshift = 1; + auth_file = strdup(argv[++i]); + } else if (!strcmp(arg, "-id") || !strcmp(arg, "-sid")) { CHECK_ARGC - if (sscanf(argv[++i], "0x%x", &subwin) != 1) { - if (sscanf(argv[i], "%d", &subwin) != 1) { - fprintf(stderr, "bad -id arg: %s\n", - argv[i]); - exit(1); - } - } - } else if (!strcmp(arg, "-scale")) { - int m, n; - char *p; - double f; - CHECK_ARGC - if ( (p = strchr(argv[++i], ':')) != NULL) { - /* options */ - if (strstr(p+1, "nb") != NULL) { - scaling_noblend = 1; - } - if (strstr(p+1, "n4") != NULL) { - scaling_nomult4 = 1; - } - if (strstr(p+1, "in") != NULL) { - scaling_interpolate = 1; - } - if (strstr(p+1, "pad") != NULL) { - scaling_pad = 1; - } - *p = '\0'; + if (!strcmp(arg, "-sid")) { + rootshift = 1; + } else { + rootshift = 0; } - if (strchr(argv[i], '.') != NULL) { - double test, diff, eps = 1.0e-7; - if (sscanf(argv[i], "%lf", &f) != 1) { - fprintf(stderr, "bad -scale arg: %s\n", - argv[i]); + i++; + if (!strcmp("pick", argv[i])) { + if (safe_remote_only) { + fprintf(stderr, "unsafe: %s pick\n", + arg); exit(1); - } - scale_fac = (double) f; - /* look for common fractions from small ints: */ - for (n=2; n<=10; n++) { - for (m=1; m<n; m++) { - test = ((double) m)/ n; - diff = scale_fac - test; - if (-eps < diff && diff < eps) { - scale_numer = m; - scale_denom = n; - break; - - } - } - if (scale_denom) { - break; - } - } - } else { - if (sscanf(argv[i], "%d/%d", &m, &n) != 2) { - fprintf(stderr, "bad -scale arg: %s\n", - argv[i]); + } else if (! pick_windowid(&subwin)) { + fprintf(stderr, "bad %s pick\n", arg); exit(1); } - scale_fac = ((double) m)/ n; - scale_numer = m; - scale_denom = n; - } - if (scale_fac == 1.0) { - if (! quiet) { - rfbLog("scaling disabled for factor " - " %f\n", scale_fac); - } - } else { - scaling = 1; + } else if (! scan_hexdec(argv[i], &subwin)) { + fprintf(stderr, "bad %s arg: %s\n", arg, + argv[i]); + exit(1); } - } else if (!strcmp(arg, "-visual")) { - CHECK_ARGC - visual_str = argv[++i]; + } else if (!strcmp(arg, "-flashcmap")) { + flash_cmap = 1; + } else if (!strcmp(arg, "-notruecolor")) { + force_indexed_color = 1; } else if (!strcmp(arg, "-overlay")) { overlay = 1; } else if (!strcmp(arg, "-overlay_nocursor")) { overlay = 1; overlay_cursor = 0; - } else if (!strcmp(arg, "-flashcmap")) { - flash_cmap = 1; - } else if (!strcmp(arg, "-notruecolor")) { - force_indexed_color = 1; + } else if (!strcmp(arg, "-overlay_yescursor")) { + overlay = 1; + overlay_cursor = 2; + } else if (!strcmp(arg, "-visual")) { + CHECK_ARGC + visual_str = strdup(argv[++i]); + } else if (!strcmp(arg, "-scale")) { + CHECK_ARGC + scale_str = strdup(argv[++i]); } else if (!strcmp(arg, "-viewonly")) { view_only = 1; + } else if (!strcmp(arg, "-shared")) { + shared = 1; + } else if (!strcmp(arg, "-once")) { + connect_once = 1; + } else if (!strcmp(arg, "-many") || !strcmp(arg, "-forever")) { + connect_once = 0; + } else if (!strcmp(arg, "-inetd")) { + inetd = 1; + } else if (!strcmp(arg, "-connect")) { + CHECK_ARGC + if (strchr(argv[++i], '/')) { + client_connect_file = strdup(argv[i]); + } else { + client_connect = strdup(argv[i]); + } + } else if (!strcmp(arg, "-vncconnect")) { + vnc_connect = 1; + } else if (!strcmp(arg, "-novncconnect")) { + vnc_connect = 0; + } else if (!strcmp(arg, "-allow")) { + CHECK_ARGC + allow_list = strdup(argv[++i]); + } else if (!strcmp(arg, "-localhost")) { + allow_list = strdup("127.0.0.1"); } else if (!strcmp(arg, "-viewpasswd")) { vpw_loc = i; CHECK_ARGC viewonly_passwd = strdup(argv[++i]); } else if (!strcmp(arg, "-passwdfile")) { CHECK_ARGC - passwdfile = argv[++i]; + passwdfile = strdup(argv[++i]); } else if (!strcmp(arg, "-storepasswd")) { if (i+2 >= argc || rfbEncryptAndStorePasswd(argv[i+1], argv[i+2]) != 0) { @@ -9355,44 +14125,58 @@ int main(int argc, char* argv[]) { argv[i+2]); exit(0); } - } else if (!strcmp(arg, "-shared")) { - shared = 1; - } else if (!strcmp(arg, "-auth")) { - CHECK_ARGC - auth_file = argv[++i]; - } else if (!strcmp(arg, "-allow")) { - CHECK_ARGC - allow_list = argv[++i]; - } else if (!strcmp(arg, "-localhost")) { - allow_list = "127.0.0.1"; } else if (!strcmp(arg, "-accept")) { CHECK_ARGC - accept_cmd = argv[++i]; + accept_cmd = strdup(argv[++i]); } else if (!strcmp(arg, "-gone")) { CHECK_ARGC - gone_cmd = argv[++i]; - } else if (!strcmp(arg, "-once")) { - connect_once = 1; - } else if (!strcmp(arg, "-many") - || !strcmp(arg, "-forever")) { - connect_once = 0; - } else if (!strcmp(arg, "-connect")) { - CHECK_ARGC - if (strchr(argv[++i], '/')) { - client_connect_file = argv[i]; - } else { - client_connect = strdup(argv[i]); - } - } else if (!strcmp(arg, "-vncconnect")) { - vnc_connect = 1; - } else if (!strcmp(arg, "-novncconnect")) { - vnc_connect = 0; - } else if (!strcmp(arg, "-inetd")) { - inetd = 1; + gone_cmd = strdup(argv[++i]); } else if (!strcmp(arg, "-noshm")) { using_shm = 0; } else if (!strcmp(arg, "-flipbyteorder")) { flip_byte_order = 1; + } else if (!strcmp(arg, "-onetile")) { + single_copytile = 1; + } else if (!strcmp(arg, "-blackout")) { + CHECK_ARGC + blackout_string = strdup(argv[++i]); + } else if (!strcmp(arg, "-xinerama")) { + xinerama = 1; + } else if (!strcmp(arg, "-xrandr")) { + xrandr = 1; + if (i < argc-1) { + char *s = argv[i+1]; + if (known_xrandr_mode(s)) { + xrandr_mode = strdup(s); + i++; + } + } + } else if (!strcmp(arg, "-padgeom") + || !strcmp(arg, "-padgeometry")) { + CHECK_ARGC + pad_geometry = strdup(argv[++i]); + } else if (!strcmp(arg, "-o") || !strcmp(arg, "-logfile")) { + CHECK_ARGC + logfile = strdup(argv[++i]); + } else if (!strcmp(arg, "-rc")) { + i++; /* done above */ + } else if (!strcmp(arg, "-norc")) { + ; /* done above */ + } else if (!strcmp(arg, "-h") || !strcmp(arg, "-help") + || !strcmp(arg, "-?")) { + print_help(); + } else if (!strcmp(arg, "-V") || !strcmp(arg, "-version")) { + fprintf(stderr, "x11vnc: %s\n", lastmod); + exit(0); + } else if (!strcmp(arg, "-q") || !strcmp(arg, "-quiet")) { + quiet = 1; + } else if (!strcmp(arg, "-bg") || !strcmp(arg, "-background")) { +#ifdef LIBVNCSERVER_HAVE_SETSID + bg = 1; + opts_bg = bg; +#else + fprintf(stderr, "warning: -bg mode not supported.\n"); +#endif } else if (!strcmp(arg, "-modtweak")) { use_modifier_tweak = 1; } else if (!strcmp(arg, "-nomodtweak")) { @@ -9401,11 +14185,11 @@ int main(int argc, char* argv[]) { use_iso_level3 = 1; } else if (!strcmp(arg, "-xkb")) { use_xkb_modtweak = 1; - } else if (!strcmp(arg, "-skip_keycodes")) { - CHECK_ARGC - skip_keycodes = argv[++i]; } else if (!strcmp(arg, "-xkbcompat")) { xkbcompat = 1; + } else if (!strcmp(arg, "-skip_keycodes")) { + CHECK_ARGC + skip_keycodes = strdup(argv[++i]); } else if (!strcmp(arg, "-add_keysyms")) { add_keysyms++; } else if (!strcmp(arg, "-clear_mods")) { @@ -9414,65 +14198,68 @@ int main(int argc, char* argv[]) { clear_mods = 2; } else if (!strcmp(arg, "-remap")) { CHECK_ARGC - remap_file = argv[++i]; - } else if (!strcmp(arg, "-blackout")) { - CHECK_ARGC - blackout_string = argv[++i]; - } else if (!strcmp(arg, "-xinerama")) { - xinerama = 1; - } else if (!strcmp(arg, "-norc")) { - ; /* done above */ - } else if (!strcmp(arg, "-rc")) { - i++; /* done above */ - } else if (!strcmp(arg, "-nobell")) { - watch_bell = 0; + remap_file = strdup(argv[++i]); + } else if (!strcmp(arg, "-norepeat")) { + no_autorepeat = 1; + } else if (!strcmp(arg, "-repeat")) { + no_autorepeat = 0; } else if (!strcmp(arg, "-nofb")) { nofb = 1; + } else if (!strcmp(arg, "-nobell")) { + watch_bell = 0; } else if (!strcmp(arg, "-nosel")) { watch_selection = 0; } else if (!strcmp(arg, "-noprimary")) { watch_primary = 0; } else if (!strcmp(arg, "-cursor")) { show_cursor = 1; - if (i >= argc-1) { - ; - } else { + if (i < argc-1) { char *s = argv[i+1]; - if (*s == 'X' || !strcmp(s, "default") || - !strcmp(s, "some") || !strcmp(s, "most")) { + if (known_cursors_mode(s)) { multiple_cursors_mode = strdup(s); i++; - } + if (!strcmp(s, "none")) { + show_cursor = 0; + } + } } } else if (!strcmp(arg, "-nocursor")) { + multiple_cursors_mode = strdup("none"); show_cursor = 0; + } else if (!strcmp(arg, "-noxfixes")) { + use_xfixes = 0; + } else if (!strcmp(arg, "-nocursorshape")) { + cursor_shape_updates = 0; } else if (!strcmp(arg, "-cursorpos")) { cursor_pos_updates = 1; } else if (!strcmp(arg, "-nocursorpos")) { cursor_pos_updates = 0; - } else if (!strcmp(arg, "-nocursorshape")) { - cursor_shape_updates = 0; } else if (!strcmp(arg, "-xwarppointer")) { use_xwarppointer = 1; } else if (!strcmp(arg, "-buttonmap")) { CHECK_ARGC - pointer_remap = argv[++i]; + pointer_remap = strdup(argv[++i]); } else if (!strcmp(arg, "-nodragging")) { show_dragging = 0; + } else if (!strcmp(arg, "-pointer_mode")) { + char *p, *s; + CHECK_ARGC + s = argv[++i]; + if ((p = strchr(s, ':')) != NULL) { + ui_skip = atoi(p+1); + if (! ui_skip) ui_skip = 1; + *p = '\0'; + } + if (atoi(s) < 1 || atoi(s) > pointer_mode_max) { + rfbLog("pointer_mode out of range 1-%d: %d\n", + pointer_mode_max, atoi(s)); + } else { + pointer_mode = atoi(s); + } } else if (!strcmp(arg, "-input_skip")) { CHECK_ARGC ui_skip = atoi(argv[++i]); if (! ui_skip) ui_skip = 1; - } else if (!strcmp(arg, "-old_pointer")) { - old_pointer = 1; - } else if (!strcmp(arg, "-old_pointer2")) { - old_pointer = 2; - } else if (!strcmp(arg, "-norepeat")) { - no_autorepeat = 1; - } else if (!strcmp(arg, "-repeat")) { - no_autorepeat = 0; - } else if (!strcmp(arg, "-onetile")) { - single_copytile = 1; } else if (!strcmp(arg, "-debug_pointer") || !strcmp(arg, "-dp")) { debug_pointer++; @@ -9488,25 +14275,24 @@ int main(int argc, char* argv[]) { waitms = atoi(argv[++i]); } else if (!strcmp(arg, "-nap")) { take_naps = 1; -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD - } else if (!strcmp(arg, "-threads")) { - use_threads = 1; - } else if (!strcmp(arg, "-nothreads")) { - use_threads = 0; -#endif + } else if (!strcmp(arg, "-sb")) { + CHECK_ARGC + screen_blank = atoi(argv[++i]); } else if (!strcmp(arg, "-sigpipe")) { CHECK_ARGC - if (!strcmp(argv[++i], "ignore")) { - sigpipe = 1; - } else if (!strcmp(argv[i], "exit")) { - sigpipe = 2; - } else if (!strcmp(argv[i], "skip")) { - sigpipe = 0; + if (known_sigpipe_mode(argv[++i])) { + sigpipe = strdup(argv[i]); } else { fprintf(stderr, "bad -sigpipe arg: %s, must " "be \"ignore\" or \"exit\"\n", argv[i]); exit(1); } +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD + } else if (!strcmp(arg, "-threads")) { + use_threads = 1; + } else if (!strcmp(arg, "-nothreads")) { + use_threads = 0; +#endif } else if (!strcmp(arg, "-fs")) { CHECK_ARGC fs_frac = atof(argv[++i]); @@ -9519,24 +14305,42 @@ int main(int argc, char* argv[]) { } else if (!strcmp(arg, "-fuzz")) { CHECK_ARGC tile_fuzz = atoi(argv[++i]); - } else if (!strcmp(arg, "-h") || !strcmp(arg, "-help") - || !strcmp(arg, "-?")) { - print_help(); - } else if (!strcmp(arg, "-V") || !strcmp(arg, "-version")) { - fprintf(stderr, "x11vnc: %s\n", lastmod); - exit(0); - } else if (!strcmp(arg, "-o") || !strcmp(arg, "-logfile")) { + } else if (!strcmp(arg, "-gui")) { + launch_gui = 1; + if (i < argc-1) { + char *s = argv[i+1]; + if (*s != '-') { + gui_string = strdup(s); + i++; + } + } + } else if (!strcmp(arg, "-remote") || !strcmp(arg, "-R") + || !strcmp(arg, "-r")) { CHECK_ARGC - logfile = argv[++i]; - } else if (!strcmp(arg, "-q") || !strcmp(arg, "-quiet")) { + remote_cmd = strdup(argv[++i]); + if (!strcmp(remote_cmd, "ping")) { + remote_query = 1; + } quiet = 1; -#ifdef LIBVNCSERVER_HAVE_SETSID - } else if (!strcmp(arg, "-bg") || !strcmp(arg, "-background")) { - bg = 1; -#endif + xkbcompat = 0; + } else if (!strcmp(arg, "-query") || !strcmp(arg, "-Q")) { + CHECK_ARGC + remote_cmd = strdup(argv[++i]); + remote_query = 1; + quiet = 1; + xkbcompat = 0; + } else if (!strcmp(arg, "-noremote")) { + accept_remote_cmds = 0; + } else if (!strcmp(arg, "-unsafe")) { + safe_remote_only = 0; + } else if (!strcmp(arg, "-safer")) { + safe_remote_only = 1; + } else if (!strcmp(arg, "-deny_all")) { + deny_all = 1; } else { - if (!strcmp(arg, "-desktop")) { + if (!strcmp(arg, "-desktop") && i < argc-1) { dt = 1; + rfb_desktop_name = strdup(argv[i+1]); } if (!strcmp(arg, "-passwd")) { pw_loc = i; @@ -9562,6 +14366,10 @@ int main(int argc, char* argv[]) { } } } + + if (launch_gui) { + do_gui(gui_string); + } if (logfile) { int n; if ((n = open(logfile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) { @@ -9588,20 +14396,27 @@ int main(int argc, char* argv[]) { } } + if (client_connect_file && remote_cmd) { + /* no need to open DISPLAY, just write it to the file now */ + int rc = send_remote_cmd(remote_cmd, remote_query); + fflush(stderr); + fflush(stdout); + exit(rc); + } + /* * If -passwd was used, clear it out of argv. This does not * work on all UNIX, have to use execvp() in general... */ if (pw_loc > 0) { - char *p = argv[pw_loc]; - while (*p != '\0') { - *p++ = '\0'; - } - if (pw_loc+1 < argc) { - p = argv[pw_loc+1]; - while (*p != '\0') { - *p++ = '\0'; + int i; + for (i=pw_loc; i <= pw_loc+1; i++) { + if (i < argc) { + char *p = argv[i]; + while (*p != '\0') { + *p++ = '\0'; + } } } } else if (passwdfile) { @@ -9611,7 +14426,7 @@ int main(int argc, char* argv[]) { in = fopen(passwdfile, "r"); if (in == NULL) { rfbLog("cannot open passwdfile: %s\n", passwdfile); - rfbLog("fopen"); + rfbLogPerror("fopen"); exit(1); } if (fgets(line, 1024, in) != NULL) { @@ -9619,7 +14434,7 @@ int main(int argc, char* argv[]) { if (len > 0 && line[len-1] == '\n') { line[len-1] = '\0'; } - argv_vnc[argc_vnc++] = "-passwd"; + argv_vnc[argc_vnc++] = strdup("-passwd"); argv_vnc[argc_vnc++] = strdup(line); pw_loc = 100; /* just for pw_loc check below */ if (fgets(line, 1024, in) != NULL) { @@ -9656,14 +14471,13 @@ int main(int argc, char* argv[]) { fclose(in); } if (vpw_loc > 0) { - char *p = argv[vpw_loc]; - while (*p != '\0') { - *p++ = '\0'; - } - if (vpw_loc+1 < argc) { - p = argv[vpw_loc+1]; - while (*p != '\0') { - *p++ = '\0'; + int i; + for (i=vpw_loc; i <= vpw_loc+1; i++) { + if (i < argc) { + char *p = argv[i]; + while (*p != '\0') { + *p++ = '\0'; + } } } } @@ -9698,77 +14512,20 @@ int main(int argc, char* argv[]) { /* increase rfbwait if threaded */ if (use_threads && ! got_rfbwait) { - argv_vnc[argc_vnc++] = "-rfbwait"; - argv_vnc[argc_vnc++] = "604800000"; /* one week... */ - } - - /* cursor shapes setup */ - -#ifdef SOLARIS - if (show_cursor && ! overlay && overlay_cursor && - !strcmp(multiple_cursors_mode, "most")) { - overlay = 1; - if (! quiet) { - rfbLog("enabling -overlay mode to achieve " - "'-cursor most'\n"); - rfbLog("disable with: -overlay_nocursor.\n"); - } - } -#endif - - if (overlay) { -#ifdef SOLARIS - using_shm = 0; - - if (flash_cmap && ! quiet) { - rfbLog("warning: -flashcmap may be incompatible " - "with -overlay\n"); - } - - if (show_cursor && overlay_cursor) { - char *s = multiple_cursors_mode; - if (*s == 'X' || !strcmp(s, "some")) { - /* - * user wants these modes, so disable fb cursor - */ - overlay_cursor = 0; - } else { - /* - * "default" and "most", we turn off - * show_cursor since it will automatically - * be in the framebuffer. - */ - show_cursor = 0; - } - } -#else - if (! quiet) { - rfbLog("disabling -overlay: only available on " - "Solaris Xsun.\n"); - } - overlay = 0; -#endif - } - - if (show_cursor) { - char *s = multiple_cursors_mode; - if (*s == 'X' || !strcmp(s, "some")) { - show_multiple_cursors = 1; - } else if (!strcmp(s, "most")) { - /* later check for XFIXES, for now "some" */ - show_multiple_cursors = 1; - } + argv_vnc[argc_vnc++] = strdup("-rfbwait"); + argv_vnc[argc_vnc++] = strdup("604800000"); /* one week... */ } /* no framebuffer (Win2VNC) mode */ if (nofb) { - /* disable things that do not make sense */ + /* disable things that do not make sense with no fb */ using_shm = 0; flash_cmap = 0; show_cursor = 0; show_multiple_cursors = 0; overlay = 0; + overlay_cursor = 0; if (! quiet) { rfbLog("disabling -cursor, fb, shm, etc. in " "-nofb mode.\n"); @@ -9780,18 +14537,11 @@ int main(int argc, char* argv[]) { } } - /* check for OS with small shm limits */ - if (using_shm && ! single_copytile) { - if (limit_shm()) { - single_copytile = 1; - } - } - if (! got_deferupdate) { char tmp[40]; /* XXX not working yet in libvncserver */ sprintf(tmp, "%d", defer_update); - argv_vnc[argc_vnc++] = "-deferupdate"; + argv_vnc[argc_vnc++] = strdup("-deferupdate"); argv_vnc[argc_vnc++] = strdup(tmp); } @@ -9809,24 +14559,26 @@ int main(int argc, char* argv[]) { fprintf(stderr, "Settings:\n"); fprintf(stderr, " display: %s\n", use_dpy ? use_dpy : "null"); - fprintf(stderr, " subwin: 0x%x\n", subwin); + fprintf(stderr, " authfile: %s\n", auth_file ? auth_file + : "null"); + fprintf(stderr, " subwin: 0x%lx\n", subwin); + fprintf(stderr, " rootshift: %d\n", rootshift); fprintf(stderr, " flashcmap: %d\n", flash_cmap); fprintf(stderr, " force_idx: %d\n", force_indexed_color); - fprintf(stderr, " scaling: %d %.5f\n", scaling, scale_fac); - fprintf(stderr, " visual: %s\n", visual_str ? visual_str - : "null"); fprintf(stderr, " overlay: %d\n", overlay); fprintf(stderr, " ovl_cursor: %d\n", overlay_cursor); + fprintf(stderr, " visual: %s\n", visual_str ? visual_str + : "null"); + fprintf(stderr, " scaling: %d %.5f\n", scaling, scale_fac); fprintf(stderr, " viewonly: %d\n", view_only); fprintf(stderr, " shared: %d\n", shared); fprintf(stderr, " conn_once: %d\n", connect_once); + fprintf(stderr, " inetd: %d\n", inetd); fprintf(stderr, " connect: %s\n", client_connect ? client_connect : "null"); fprintf(stderr, " connectfile %s\n", client_connect_file ? client_connect_file : "null"); fprintf(stderr, " vnc_conn: %d\n", vnc_connect); - fprintf(stderr, " authfile: %s\n", auth_file ? auth_file - : "null"); fprintf(stderr, " allow: %s\n", allow_list ? allow_list : "null"); fprintf(stderr, " passfile: %s\n", passwdfile ? passwdfile @@ -9835,14 +14587,20 @@ int main(int argc, char* argv[]) { : "null"); fprintf(stderr, " gone: %s\n", gone_cmd ? gone_cmd : "null"); - fprintf(stderr, " inetd: %d\n", inetd); fprintf(stderr, " using_shm: %d\n", using_shm); fprintf(stderr, " flipbytes: %d\n", flip_byte_order); + fprintf(stderr, " onetile: %d\n", single_copytile); fprintf(stderr, " blackout: %s\n", blackout_string ? blackout_string : "null"); fprintf(stderr, " xinerama: %d\n", xinerama); + fprintf(stderr, " xrandr: %d\n", xrandr); + fprintf(stderr, " xrandrmode: %s\n", xrandr_mode ? xrandr_mode + : "null"); fprintf(stderr, " logfile: %s\n", logfile ? logfile : "null"); + fprintf(stderr, " rc_file: %s\n", rc_rcfile ? rc_rcfile + : "null"); + fprintf(stderr, " norc: %d\n", rc_norc); fprintf(stderr, " bg: %d\n", bg); fprintf(stderr, " mod_tweak: %d\n", use_modifier_tweak); fprintf(stderr, " isolevel3: %d\n", use_iso_level3); @@ -9854,6 +14612,7 @@ int main(int argc, char* argv[]) { fprintf(stderr, " clearmods: %d\n", clear_mods); fprintf(stderr, " remap: %s\n", remap_file ? remap_file : "null"); + fprintf(stderr, " norepeat: %d\n", no_autorepeat); fprintf(stderr, " nofb: %d\n", nofb); fprintf(stderr, " watchbell: %d\n", watch_bell); fprintf(stderr, " watchsel: %d\n", watch_selection); @@ -9862,27 +14621,30 @@ int main(int argc, char* argv[]) { fprintf(stderr, " root_curs: %d\n", show_multiple_cursors); fprintf(stderr, " curs_mode: %s\n", multiple_cursors_mode ? multiple_cursors_mode : "null"); - fprintf(stderr, " xwarpptr: %d\n", use_xwarppointer); - fprintf(stderr, " cursorpos: %d\n", cursor_pos_updates); + fprintf(stderr, " xfixes: %d\n", use_xfixes); fprintf(stderr, " cursorshp: %d\n", cursor_shape_updates); + fprintf(stderr, " cursorpos: %d\n", cursor_pos_updates); + fprintf(stderr, " xwarpptr: %d\n", use_xwarppointer); fprintf(stderr, " buttonmap: %s\n", pointer_remap ? pointer_remap : "null"); fprintf(stderr, " dragging: %d\n", show_dragging); - fprintf(stderr, " old_ptr: %d\n", old_pointer); + fprintf(stderr, " ptr_mode: %d\n", pointer_mode); fprintf(stderr, " inputskip: %d\n", ui_skip); - fprintf(stderr, " norepeat: %d\n", no_autorepeat); fprintf(stderr, " debug_ptr: %d\n", debug_pointer); fprintf(stderr, " debug_key: %d\n", debug_keyboard); fprintf(stderr, " defer: %d\n", defer_update); fprintf(stderr, " waitms: %d\n", waitms); fprintf(stderr, " take_naps: %d\n", take_naps); - fprintf(stderr, " sigpipe: %d\n", sigpipe); + fprintf(stderr, " sb: %d\n", screen_blank); + fprintf(stderr, " sigpipe: %s\n", sigpipe + ? sigpipe : "null"); fprintf(stderr, " threads: %d\n", use_threads); fprintf(stderr, " fs_frac: %.2f\n", fs_frac); - fprintf(stderr, " onetile: %d\n", single_copytile); fprintf(stderr, " gaps_fill: %d\n", gaps_fill); fprintf(stderr, " grow_fill: %d\n", grow_fill); fprintf(stderr, " tile_fuzz: %d\n", tile_fuzz); + fprintf(stderr, " deny_all: %d\n", deny_all); + fprintf(stderr, " noremote: %d\n", !accept_remote_cmds); fprintf(stderr, "\n"); rfbLog("x11vnc version: %s\n", lastmod); } else { @@ -9946,21 +14708,140 @@ int main(int argc, char* argv[]) { scr = DefaultScreen(dpy); rootwin = RootWindow(dpy, scr); + if (remote_cmd) { + int rc = send_remote_cmd(remote_cmd, remote_query); + XFlush(dpy); + fflush(stderr); + usleep(30 * 1000); /* still needed? */ + XCloseDisplay(dpy); + exit(rc); + } + if (! dt) { static char str[] = "-desktop"; argv_vnc[argc_vnc++] = str; argv_vnc[argc_vnc++] = choose_title(use_dpy); + rfb_desktop_name = strdup(argv_vnc[argc_vnc-1]); + } + +#ifdef LIBVNCSERVER_HAVE_LIBXFIXES + if (! XFixesQueryExtension(dpy, &xfixes_base_event_type, &er)) { + if (! quiet) { + rfbLog("disabling xfixes mode: display does not " + "support it.\n"); + } + xfixes_present = 0; + } else { + xfixes_present = 1; + } +#endif + +#ifdef LIBVNCSERVER_HAVE_LIBXDAMAGE + if (! XDamageQueryExtension(dpy, &xdamage_base_event_type, &er)) { + if (0 && ! quiet) { + rfbLog("disabling xdamage mode: display does not " + "support it.\n"); + } + xdamage_present = 0; + } else { + xdamage_present = 1; + } +#endif + + overlay_present = 0; +#ifdef SOLARIS_OVERLAY + if (! XQueryExtension(dpy, "SUN_OVL", &maj, &ev, &er)) { + if (! quiet && overlay) { + rfbLog("disabling -overlay: SUN_OVL " + "extension not available.\n"); + } + } else { + overlay_present = 1; + } +#endif +#ifdef IRIX_OVERLAY + if (! XReadDisplayQueryExtension(dpy, &ev, &er)) { + if (! quiet && overlay) { + rfbLog("disabling -overlay: IRIX ReadDisplay " + "extension not available.\n"); + } + } else { + overlay_present = 1; + } +#endif + if (overlay && !overlay_present) { + overlay = 0; + overlay_cursor = 0; + } + + /* cursor shapes setup */ + if (! multiple_cursors_mode) { + multiple_cursors_mode = strdup("default"); + } + if (show_cursor) { + if(!strcmp(multiple_cursors_mode, "default") + && xfixes_present && use_xfixes) { + free(multiple_cursors_mode); + multiple_cursors_mode = strdup("most"); + + if (! quiet) { + rfbLog("XFIXES available on display, resetting" + " cursor mode\n"); + rfbLog(" to: '-cursor most'.\n"); + rfbLog(" to disable this behavior use: " + "'-cursor arrow'.\n"); + } + } + if(!strcmp(multiple_cursors_mode, "most")) { + if (xfixes_present && use_xfixes && + overlay_cursor == 1) { + if (! quiet) { + rfbLog("using XFIXES for cursor " + "drawing.\n"); + } + overlay_cursor = 0; + } + } + } + + if (overlay) { + using_shm = 0; + if (flash_cmap && ! quiet) { + rfbLog("warning: -flashcmap may be " + "incompatible with -overlay\n"); + } + if (show_cursor && overlay_cursor) { + char *s = multiple_cursors_mode; + if (*s == 'X' || !strcmp(s, "some") || + !strcmp(s, "arrow")) { + /* + * user wants these modes, so disable fb cursor + */ + overlay_cursor = 0; + } else { + /* + * "default" and "most", we turn off + * show_cursor since it will automatically + * be in the framebuffer. + */ + show_cursor = 0; + } + } } + initialize_cursors_mode(); + /* check for XTEST */ if (! XTestQueryExtension_wr(dpy, &ev, &er, &maj, &min)) { + if (! quiet) { rfbLog("warning: XTest extension not available, most user" " input\n"); rfbLog("(pointer and keyboard) will be discarded.\n"); - xtest_present = 0; rfbLog("no XTest extension, switching to -xwarppointer mode\n"); rfbLog("for pointer motion input.\n"); - use_xwarppointer = 1; + } + xtest_present = 0; + use_xwarppointer = 1; } /* * Window managers will often grab the display during resize, etc. @@ -9969,14 +14850,25 @@ int main(int argc, char* argv[]) { */ XTestGrabControl_wr(dpy, True); + /* check for OS with small shm limits */ + if (using_shm && ! single_copytile) { + if (limit_shm()) { + single_copytile = 1; + } + } + + single_copytile_orig = single_copytile; + /* check for MIT-SHM */ if (! nofb && ! XShmQueryExtension_wr(dpy)) { + xshm_present = 0; if (! using_shm) { if (! quiet) { rfbLog("info: display does not support" " XShm.\n"); } } else { + if (! quiet) { rfbLog("warning: XShm extension is not available.\n"); rfbLog("For best performance the X Display should be" " local. (i.e.\n"); @@ -9985,46 +14877,16 @@ int main(int argc, char* argv[]) { rfbLog("the same machine.)\n"); #ifdef LIBVNCSERVER_HAVE_XSHM rfbLog("Restart with -noshm to override this.\n"); - exit(1); + } + exit(1); #else rfbLog("Switching to -noshm mode.\n"); - using_shm = 0; + } + using_shm = 0; #endif } } - if (overlay) { - /* - * ideally we'd like to not have to cook up the visual variables - * but rather let it all come out of XReadScreen(), however - * there is no way to get a default visual out of it, so we - * pretend -visual TrueColor:NN was supplied with NN usually 24. - */ -#ifdef SOLARIS - char str[16]; - XImage *xi; - Window twin = subwin ? subwin : rootwin; - - xi = XReadScreen(dpy, twin, 0, 0, 8, 8, False); - - sprintf(str, "TrueColor:%d", xi->depth); - if (xi->depth != 24 && ! quiet) { - fprintf(stderr, "warning XReadScreen() image has " - "depth %d instead of 24.\n", xi->depth); - } - XDestroyImage(xi); - if (visual_str != NULL && ! quiet) { - fprintf(stderr, "warning: replacing '-visual %s' by " - "'%s' for use with -overlay\n", visual_str, str); - } - visual_str = strdup(str); -#endif - } - - if (visual_str != NULL) { - set_visual(visual_str); - } - #ifdef LIBVNCSERVER_HAVE_XKEYBOARD /* check for XKEYBOARD */ if (use_xkb) { @@ -10033,163 +14895,51 @@ int main(int argc, char* argv[]) { initialize_watch_bell(); if (!use_xkb && use_xkb_modtweak) { if (! quiet) { - fprintf(stderr, "warning: disabling xkb modtweak." + rfbLog("warning: disabling xkb modtweak." " XKEYBOARD ext. not present.\n"); } use_xkb_modtweak = 0; } #endif - /* set up parameters for subwin or non-subwin cases: */ - if (! subwin) { - window = rootwin; - dpy_x = DisplayWidth(dpy, scr); - dpy_y = DisplayHeight(dpy, scr); - off_x = 0; - off_y = 0; - /* this may be overridden via visual_id below */ - default_visual = DefaultVisual(dpy, scr); - } else { - /* experiment to share just one window */ - XWindowAttributes attr; - - window = (Window) subwin; - if ( ! XGetWindowAttributes(dpy, window, &attr) ) { - fprintf(stderr, "bad window: 0x%lx\n", window); - exit(1); +#ifdef LIBVNCSERVER_HAVE_LIBXRANDR + if (! XRRQueryExtension(dpy, &xrandr_base_event_type, &er)) { + if (xrandr && ! quiet) { + rfbLog("disabling -xrandr mode: display does not" + " support it.\n"); } - dpy_x = attr.width; - dpy_y = attr.height; - - /* this may be overridden via visual_id below */ - default_visual = attr.visual; - - if (show_multiple_cursors) { - show_multiple_cursors = 0; - if (! quiet) { - fprintf(stderr, "disabling root cursor drawing" - " for subwindow\n"); - } - } - set_offset(); - } - - /* initialize depth to reasonable value, visual_id may override */ - depth = DefaultDepth(dpy, scr); - - if (visual_id) { - int n; - XVisualInfo vinfo_tmpl, *vinfo; - - /* - * we are in here from -visual or -overlay options - * visual_id and visual_depth were set in set_visual(). - */ - - vinfo_tmpl.visualid = visual_id; - vinfo = XGetVisualInfo(dpy, VisualIDMask, &vinfo_tmpl, &n); - if (vinfo == NULL || n == 0) { - fprintf(stderr, "could not match visual_id: 0x%x\n", - (int) visual_id); - exit(1); - } - default_visual = vinfo->visual; - depth = vinfo->depth; - if (visual_depth) { - /* force it from -visual FooColor:NN */ - depth = visual_depth; - } - if (! quiet) { - fprintf(stderr, "vis id: 0x%x\n", - (int) vinfo->visualid); - fprintf(stderr, "vis scr: %d\n", vinfo->screen); - fprintf(stderr, "vis depth %d\n", vinfo->depth); - fprintf(stderr, "vis class %d\n", vinfo->class); - fprintf(stderr, "vis rmask 0x%lx\n", vinfo->red_mask); - fprintf(stderr, "vis gmask 0x%lx\n", vinfo->green_mask); - fprintf(stderr, "vis bmask 0x%lx\n", vinfo->blue_mask); - fprintf(stderr, "vis cmap_sz %d\n", vinfo->colormap_size); - fprintf(stderr, "vis b/rgb %d\n", vinfo->bits_per_rgb); - } - - XFree(vinfo); - } - - if (! quiet) { - rfbLog("default visual ID: 0x%x\n", - (int) XVisualIDFromVisual(default_visual)); + xrandr = 0; + xrandr_present = 0; + } else { + xrandr_present = 1; } +#endif - if (nofb) { - /* - * For -nofb we do not allocate the framebuffer, so we - * can save a few MB of memory. - */ - fb = XCreateImage_wr(dpy, default_visual, depth, ZPixmap, - 0, NULL, dpy_x, dpy_y, BitmapPad(dpy), 0); + /* + * Create the XImage corresponding to the display framebuffer. + */ - } else if (visual_id) { - /* - * we need to call XCreateImage to supply the visual - */ - fb = XCreateImage_wr(dpy, default_visual, depth, ZPixmap, - 0, NULL, dpy_x, dpy_y, BitmapPad(dpy), 0); - fb->data = (char *) malloc(fb->bytes_per_line * fb->height); - } else { - fb = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, - ZPixmap); - if (! quiet) { - rfbLog("Read initial data from X display into" - " framebuffer.\n"); - } - } - if (fb->bits_per_pixel == 24 && ! quiet) { - fprintf(stderr, "warning: 24 bpp may have poor" - " performance.\n"); - } + fb0 = initialize_xdisplay_fb(); /* * n.b. we do not have to X_LOCK any X11 calls until watch_loop() * is called since we are single-threaded until then. */ - initialize_screen(&argc_vnc, argv_vnc, fb); + initialize_screen(&argc_vnc, argv_vnc, fb0); initialize_tiles(); /* rectangular blackout regions */ - if (blackout_string != NULL) { - initialize_blackout(blackout_string); - } - if (xinerama) { - initialize_xinerama(); - } - if (blackouts) { - blackout_tiles(); - } + initialize_blackouts_and_xinerama(); - initialize_shm(); /* also creates XImages when using_shm = 0 */ + /* created shm or XImages when using_shm = 0 */ + initialize_polling_images(); initialize_signals(); - if (blackouts) { /* blackout fb as needed. */ - copy_screen(); - } - - if (use_modifier_tweak) { - initialize_modtweak(); - } - if (remap_file != NULL) { - initialize_remap(remap_file); - } - - initialize_pointer_map(pointer_remap); - - clear_modifiers(1); - if (clear_mods == 1) { - clear_modifiers(0); - } + initialize_keyboard_and_pointer(); if (! inetd) { if (! screen->port || screen->listenSock < 0) { @@ -10200,6 +14950,7 @@ int main(int argc, char* argv[]) { if (! quiet) { rfbLog("screen setup finished.\n"); } + sprintf(vnc_desktop_name, "unknown"); if (screen->port) { char *host = this_host(); int lport = screen->port; @@ -10209,22 +14960,31 @@ int main(int argc, char* argv[]) { ; /* should not occur (port) */ } else if (quiet) { if (lport >= 5900) { + sprintf(vnc_desktop_name, "%s:%d", + host, lport - 5900); fprintf(stderr, "The VNC desktop is " - "%s:%d\n", host, lport - 5900); + "%s\n", vnc_desktop_name); } else { + sprintf(vnc_desktop_name, "%s:%d", + host, lport); fprintf(stderr, "The VNC desktop is " - "%s:%d\n", host, lport); + "%s\n", vnc_desktop_name); } } else if (lport >= 5900) { - rfbLog("The VNC desktop is %s:%d\n", host, - lport - 5900); + sprintf(vnc_desktop_name, "%s:%d", + host, lport - 5900); + rfbLog("\n"); + rfbLog("The VNC desktop is %s\n", + vnc_desktop_name); if (lport >= 6000) { rfbLog("possible aliases: %s:%d, " "%s::%d\n", host, lport, host, lport); } } else { - rfbLog("The VNC desktop is %s:%d\n", host, - lport); + sprintf(vnc_desktop_name, "%s:%d", host, lport); + rfbLog("\n"); + rfbLog("The VNC desktop is %s\n", + vnc_desktop_name); rfbLog("possible alias: %s::%d\n", host, lport); } |