diff --git a/ChangeLog b/ChangeLog index 9cf1bc5..18de084 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2005-01-23 Karl Runge + * x11vnc: -timeout, -noalphablend. make -R norepeat work. + * sync with new draw cursor mechanism. + 2005-01-20 Karl Runge * libvncserver/{cursor.c,rfbserver.c}: fixed the "disappearing cursor" problem diff --git a/x11vnc/ChangeLog b/x11vnc/ChangeLog index 0c7b673..d6174cc 100644 --- a/x11vnc/ChangeLog +++ b/x11vnc/ChangeLog @@ -1,3 +1,9 @@ +2005-01-23 Karl Runge + * sync with new draw cursor mechanism, keep old way in OLD_TREE. + * add -timeout option, change -alphablend to be default + * -R norepeat now forces the issue (Xsession may turn it back on). + * try :0 if no other info. + 2005-01-15 Karl Runge * adjust alpha blending parameters, -alphablend, handle 24bpp. * add -snapfb snapshot fb, not clear how useful it is.. diff --git a/x11vnc/README b/x11vnc/README index 711e12b..6ba11b4 100644 --- a/x11vnc/README +++ b/x11vnc/README @@ -1,5 +1,5 @@ -x11vnc README file Date: Sat Jan 15 23:37:28 EST 2005 +x11vnc README file Date: Sun Jan 23 23:48:41 EST 2005 The following information is taken from these URLs: @@ -367,7 +367,7 @@ vncviewer -via $host localhost:0 # must be TightVNC vncviewer. # Build script for Solaris, etc, with gcc, libjpeg and libz in # non-standard locations. -PATH=/path/to/gcc/bin:/usr/ccs/bin:$PATH # set to get gcc +PATH=/path/to/gcc/bin:/usr/ccs/bin:$PATH # set to get your gcc JPEG=/path/to/jpeg # maybe "/usr/local", "/usr/sfw", or "/opt/sfw" ZLIB=/path/to/zlib # maybe "/usr/local", "/usr/sfw", or "/opt/sfw" @@ -383,7 +383,9 @@ LDFLAGS="-L $JPEG/lib -R $JPEG/lib -L $ZLIB/lib -R $ZLIB/lib" CPPFLAGS="$CPPFLAGS -I /usr/openwin/include" LDFLAGS="$LDFLAGS -L /usr/openwin/lib -R /usr/openwin/lib" -# This only applies to Solaris 10 or later (XFIXES and DAMAGE) +# This only applies to Solaris 10 or later (XFIXES, DAMAGE, and RANDR) +CPPFLAGS="$CPPFLAGS -I /usr/X11/include" +LDFLAGS="$LDFLAGS -L /usr/X11/lib -R /usr/X11/lib" LDFLAGS="$LDFLAGS -L /usr/openwin/sfw/lib -R /usr/openwin/sfw/lib" # Everything needs to built with _REENTRANT for thread safe errno: @@ -786,42 +788,45 @@ ls -l ./x11vnc/x11vnc [94]Q-51: 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? - [95]Q-52: The machine where I run x11vnc has an AltGr key, but the + [95]Q-52: The x11vnc -norepeat mode is in effect, but I still get + repeated keystrokes!! + + [96]Q-53: 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? - [96]Q-53: I have a Sun machine I run x11vnc on. Its Sun keyboard has + [97]Q-54: 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) - [97]Q-54: Can I map a keystroke to a mouse button click on the remote + [98]Q-55: Can I map a keystroke to a mouse button click on the remote machine? [Screen Related Issues and Features] - [98]Q-55: The remote display is larger (in number of pixels) than the + [99]Q-56: 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? - [99]Q-56: Does x11vnc support server-side framebuffer scaling? (E.g. + [100]Q-57: Does x11vnc support server-side framebuffer scaling? (E.g. to make the desktop smaller). - [100]Q-57: Does x11vnc work with Xinerama? (i.e. multiple monitors + [101]Q-58: Does x11vnc work with Xinerama? (i.e. multiple monitors joined together to form one big, single screen). - [101]Q-58: Can I use x11vnc on a multi-headed display that is not + [102]Q-59: Can I use x11vnc on a multi-headed display that is not Xinerama (i.e. separate screens :0.0, :0.1, ... for each monitor)? - [102]Q-59: Does x11vnc support the XRANDR (X Resize, Rotate and + [103]Q-60: Does x11vnc support the XRANDR (X Resize, Rotate and Reflection) extension? Whenever I rotate or resize the screen x11vnc just seems to crash. - [103]Q-60: Why is the view in my VNC viewer completely black? Or why + [104]Q-61: Why is the view in my VNC viewer completely black? Or why is everything flashing around randomly? - [104]Q-61: I use Linux Virtual Consoles (VC's) to implement 'Fast User + [105]Q-62: 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 @@ -829,7 +834,7 @@ ls -l ./x11vnc/x11vnc otherwise all messed up unless the X session x11vnc is attached to is in the active VC? - [105]Q-62: I am using x11vnc where my local machine has "popup/hidden + [106]Q-63: 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 @@ -837,10 +842,10 @@ ls -l ./x11vnc/x11vnc [Misc: Clipboard, Beeps, etc.] - [106]Q-63: Does the Clipboard/Selection get transferred between the + [107]Q-64: Does the Clipboard/Selection get transferred between the vncviewer and the X display? - [107]Q-64: Why don't I hear the "Beeps" in my X session (e.g. when + [108]Q-65: Why don't I hear the "Beeps" in my X session (e.g. when typing tput bel in an xterm)? _________________________________________________________________ @@ -941,7 +946,7 @@ ls -l ./x11vnc/x11vnc is a workaround for Solaris 2.5.1 (and perhaps earlier): First use the environment settings (CPPFLAGS, LDFLAGS, etc.) in the - above [108]Solaris build script to run the configure command. That + above [109]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 @@ -978,18 +983,18 @@ typedef unsigned int in_addr_t; Q-4: Where can I get a precompiled x11vnc binary for my Operating System? - Hopefully the [109]build steps above and [110]FAQ provide enough info + Hopefully the [110]build steps above and [111]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) [111]http://packages.debian.org/x11vnc + Debian: (.deb) [112]http://packages.debian.org/x11vnc - Slackware: (.tgz) [112]http://www.linuxpackages.net/ Redhat/Fedora: - (.rpm) [113]http://dag.wieers.com/packages/x11vnc/ wwexptools: (.tgz) - [114]http://www.bell-labs.com/project/wwexptools/packages.html The + Slackware: (.tgz) [113]http://www.linuxpackages.net/ Redhat/Fedora: + (.rpm) [114]http://dag.wieers.com/packages/x11vnc/ wwexptools: (.tgz) + [115]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 @@ -1001,12 +1006,12 @@ typedef unsigned int in_addr_t; 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 [115]build notes above for where to download libz and libjpeg, and + the [116]build notes above for where to download libz and libjpeg, and then build everything with gcc. If the above binaries don't work and building x11vnc on your OS fails (and all else fails!) you can try one of my motley collection of - [116]test binaries. Some may be old, some may have extra debugging + [117]test binaries. Some may be old, some may have extra debugging output, etc. One may work on your OS, but please understand they are test/experimental binaries not intended for general usage like the above precompiled ones from 3rd parties. @@ -1028,14 +1033,14 @@ typedef unsigned int in_addr_t; To obtain VNC viewers for the viewing side (Windows, Mac OS, or Unix) try here: - * [117]http://www.tightvnc.com/download.html - * [118]http://www.realvnc.com/download-free.html - * [119]http://sourceforge.net/projects/cotvnc/ + * [118]http://www.tightvnc.com/download.html + * [119]http://www.realvnc.com/download-free.html + * [120]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 [120]here as well. + Run: x11vnc -help The output is listed [121]here as well. 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? Or a GUI? @@ -1090,7 +1095,7 @@ display :0 As of Apr/2004 the above fix only works for BSD signal systems (Linux, FreeBSD, ...) For SYSV systems there is a workaround in my - [121]x11vnc.c file. It also has an option -sigpipe exit to have x11vnc + [122]x11vnc.c file. It also has an option -sigpipe exit to have x11vnc clean up and exit upon receiving SIGPIPE. [Win2VNC Related] @@ -1105,16 +1110,16 @@ 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 [122]x2vnc, however you would + This will also work X11 to X11 using [123]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: - * [123]Original Win2VNC - * [124]Enhanced Win2VNC and [125]sourceforge link - * [126]x2vnc - * [127]x2x also [128]here - * [129]zvnc (MorphOS) + * [124]Original Win2VNC + * [125]Enhanced Win2VNC and [126]sourceforge link + * [127]x2vnc + * [128]x2x also [129]here + * [130]zvnc (MorphOS) All of them (except x2x) will work with x11vnc. @@ -1169,7 +1174,7 @@ display :0 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 [130]previous question regarding 8 bpp + You may want to review the [131]previous question regarding 8 bpp PseudoColor. On some hardware (Sun/SPARC, Sgi), the -overlay option discussed a @@ -1206,24 +1211,24 @@ TrueColor defdepth 24 "-cc 4" X server command line option to get a depth 24 default visual. The -overlay mode: Another option is if the system with overlay - visuals is a Sun system running Solaris you can use the -overlay - x11vnc option (Aug/2004) to have x11vnc use the Solaris - XReadScreen(3X11) function to poll the "true view" of the whole screen - at depth 24 TrueColor.This is useful for Legacy applications (older - versions of Cadence CAD apps are mentioned by x11vnc users) that - require the default depth be 8bpp, or will use a 8bpp visual even if - depth 24 visuals are available, and so the default depth workaround - described in the previous paragraph is not sufficient for these apps. - The -overlay mode should also work on IRIX machines using - XReadDisplay(3X11). + visuals is a Sun system running Solaris or Sgi running IRIX you can + use the -overlay x11vnc option (Aug/2004) to have x11vnc use the + Solaris XReadScreen(3X11) function to poll the "true view" of the + whole screen at depth 24 TrueColor. XReadDisplay(3X11) is used on + IRIX. This is useful for Legacy applications (older versions of + Cadence CAD apps are mentioned by x11vnc users) that require the + default depth be 8bpp, or will use a 8bpp visual even if depth 24 + visuals are available, and so the default depth workaround described + in the previous paragraph is not sufficient for these apps. Misc. notes on -overlay mode: An amusing by-product of -overlay mode is that mouse cursor shape is correct. The -overlay mode may be somewhat slower than normal mode due to the extra framebuffer - manipulations that must be performed. Also, there is a bug in that for - some popup menus, the windows they overlap will have painting problems - while the popup is up (a workaround is to disable SaveUnders by - passing -su to Xsun, e.g. in your /etc/dt/config/Xservers file). + manipulations that must be performed. Also, on Solaris there is a bug + in that for some popup menus, the windows they overlap will have + painting problems while the popup is up (a workaround is to disable + SaveUnders by passing -su to Xsun, e.g. in your + /etc/dt/config/Xservers file). Still not working? Run xwininfo on the application with the messed up colors to verify that the depth of its visual is different from the @@ -1246,7 +1251,7 @@ TrueColor defdepth 24 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 - [131]problems. + [132]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 @@ -1352,7 +1357,7 @@ central-server> xauth nextract - xterm123:0 | ssh xterm123 xauth nmerge - xauth(1) manpage for more details. If the display name needs to be changed between the two hosts, see - [132]this note on the xauth add ... command. + [133]this note on the xauth add ... command. A less secure option is to run something like "xhost +127.0.0.1" to allow cookie-free local access for x11vnc. @@ -1361,7 +1366,7 @@ central-server> xauth nextract - xterm123:0 | ssh xterm123 xauth nmerge - accounts, NFS, etc. you'll need to contact your system administrator to set something up. - Not recommended, but as a last resort, you could have x11vnc [133]poll + Not recommended, but as a last resort, you could have x11vnc [134]poll the Xterminal over the network. Note: use of Display Manager (gdm, kdm, ...) auth cookie files (i.e. @@ -1429,10 +1434,10 @@ central-server> xauth nextract - xterm123:0 | ssh xterm123 xauth nmerge - -shared option to have x11vnc allow multiple clients to connect simultaneously. - Recommended additional safety measures include using ssh ([134]see + Recommended additional safety measures include using ssh ([135]see above), stunnel, or a VPN to authenticate and encrypt the viewer - connections or to at least use the -rfbauth passwd-file [135]option to - use VNC password protection (or [136]-passwdfile) It is up to you to + connections or to at least use the -rfbauth passwd-file [136]option to + use VNC password protection (or [137]-passwdfile) It is up to you to apply these security measures, they will not be done for you automatically. @@ -1468,7 +1473,7 @@ central-server> xauth nextract - xterm123:0 | ssh xterm123 xauth nmerge - is "vnc", e.g.: vnc: 192.168.100.3 .example.com - Note that if you run x11vnc out of [137]inetd you do not need to build + Note that if you run x11vnc out of [138]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. @@ -1512,7 +1517,7 @@ central-server> xauth nextract - xterm123:0 | ssh xterm123 xauth nmerge - 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 [138]ftp://ftp.x.org/ + is available at [139]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 @@ -1551,7 +1556,7 @@ elif [ $rc = 4 ]; then fi exit 1 - Stefan Radman has written a nice dtksh script [139]dtVncPopup for use + Stefan Radman has written a nice dtksh script [140]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. @@ -1587,14 +1592,14 @@ 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 [140]-passwdfile and -passwd/-viewpasswd plain + x11vnc also has the [141]-passwdfile and -passwd/-viewpasswd plain text (i.e. not obscured like the -rfbauth VNC passwords) password options. Q-26: 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 [141]how to tunnel VNC via + See the description earlier on this page on [142]how to tunnel VNC via SSH from Unix to Unix. A number of ways are described along with some issues you may encounter. @@ -1604,7 +1609,7 @@ exit 1 Q-27: How can I tunnel my connection to x11vnc via an encrypted SSH channel from Windows using an SSH client like Putty? - [142]Above we described how to tunnel VNC via SSH from Unix to Unix, + [143]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 @@ -1637,7 +1642,7 @@ 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 [143]above another option is to first start the VNC + As discussed [144]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 @@ -1658,7 +1663,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 - [144]elsewhere on this page. Of course a malicious user could allow + [145]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 @@ -1734,7 +1739,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 [145]blockdpy.c The idea behind it is simple (but + source for it is [146]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 @@ -1781,11 +1786,11 @@ exit 1 # reject it permissions. Here are some ideas: - * Use the description under "Continuously" in the [146]FAQ on x11vnc + * Use the description under "Continuously" in the [147]FAQ on x11vnc and Display Managers - * Use the description in the [147]FAQ on x11vnc and inetd(1) + * Use the description in the [148]FAQ on x11vnc and inetd(1) * Start x11vnc from $HOME/.xsession (or $HOME/.xinitrc) - * Although less reliable, see the [148]x11vnc_loop rc.local hack + * Although less reliable, see the [149]x11vnc_loop rc.local hack below. The display manager scheme will not be specific to which user has the @@ -1887,18 +1892,18 @@ x11vnc -logfile $HOME/.x11vnc.log -rfbauth $HOME/.vnc/passwd -forever -bg Then restart: /usr/sbin/gdm-restart (or reboot). The KillInitClients=false setting is important: without it x11vnc will be - killed immediately after the user logs in. Here are [149]full details + killed immediately after the user logs in. Here are [150]full details on how to configure gdm 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. [150]x11vnc_loop It will need some local + file like rc.local. [151]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. If the machine is a traditional Xterminal you may want to read - [151]this FAQ. + [152]this FAQ. Q-34: Can I run x11vnc out of inetd(1)? How about xinetd(1)? @@ -2041,7 +2046,7 @@ xprop -root -f VNC_CONNECT 8s -set VNC_CONNECT "$1" 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 [152]shm_clear to list and prompt for removal + Here is a shell script [153]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, @@ -2079,7 +2084,7 @@ 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 [153]earlier question + sockets it should be acceptable (see an [154]earlier question discussing -noshm). Q-38: How can I make x11vnc use less system resources? @@ -2127,7 +2132,7 @@ ied) worth it, but could be of use in some situations. VNC viewer parameters: - * Use a [154]TightVNC enabled viewer! + * Use a [155]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 @@ -2288,7 +2293,7 @@ ied) hide the alpha data), but it also currently requires the client and server to be of the same endianness (otherwise the hidden alpha data gets reset to zero by the translation function). As an example, for - the TightVNC 1.3dev5 Unix vncviewer [155]this patch enables the + the TightVNC 1.3dev5 Unix vncviewer [156]this patch enables the TightVNC viewer to do the blending locally. You have to set the environment variable ALPHABLEND=1 before starting your modified viewer. The patch code should give an example on how to change the @@ -2300,7 +2305,7 @@ ied) Q-45: 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 [156]tightvnc extension + This default takes advantage of a [157]tightvnc extension (CursorShapeUpdates) that allows specifying a cursor image shape for the local VNC viewer. You may disable it with the -nocursor option to x11vnc if your viewer does not have this extension. @@ -2385,12 +2390,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 [157]this FAQ + and ">" it. This key does not exist on the keyboard (see [158]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 [158]FAQ about the -xkb option for a more powerful method + Also see the [159]FAQ about the -xkb option for a more powerful method of modifier tweaking for use on X servers with the XKEYBOARD extension. @@ -2451,7 +2456,7 @@ ied) the keysym comma when it comes in from a client (so when Shift is down the comma press will yield "<"). - See also the [159]FAQ about the -xkb option as a possible workaround + See also the [160]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. @@ -2587,7 +2592,23 @@ ied) someone is also working at the physical display and misses his autorepeating. - Q-52: The machine where I run x11vnc has an AltGr key, but the local + Q-52: The x11vnc -norepeat mode is in effect, but I still get repeated + keystrokes!! + + Are you using x11vnc to log in to an X session? (as described + [161]this FAQ) If so, x11vnc is starting before your session, and then + your session startup could be resetting the autorepeat to be on. Or it + could be something inside your desktop that decides to turn it back + on. x11vnc in -norepeat mode will not battle with things turning + autorepeat back on. (It will, however, turn it off whenever it goes + from a state of zero clients to one client). + + Under these circumstances you will have to manually turn autorepeating + off by typing "xset r off" or "x11vnc -R norepeat" or a desktop + utility. If something in your desktop is automatically turning it back + on you will have to disable that somehow. + + Q-53: 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? @@ -2601,7 +2622,7 @@ ied) Super_R-Mode_switch,Menu-Multi_key" or use "-remap filename" to specify remappings from a file. - Q-53: I have a Sun machine I run x11vnc on. Its Sun keyboard has just + Q-54: 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) @@ -2618,7 +2639,7 @@ ied) send Alt_L in this case, maybe -remap Super_L-Meta_L would be a better choice. - Q-54: Can I map a keystroke to a mouse button click on the remote + Q-55: 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 @@ -2642,7 +2663,7 @@ ied) [Screen Related Issues and Features] - Q-55: The remote display is larger (in number of pixels) than the + Q-56: 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? @@ -2660,10 +2681,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 [160]this + local display. Especially for hand-held devices. See also [162]this FAQ on x11vnc scaling. - Q-56: Does x11vnc support server-side framebuffer scaling? (E.g. to + Q-57: 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 @@ -2713,7 +2734,7 @@ ied) different scalings listening on separate ports (-rfbport option, etc.). - Q-57: Does x11vnc work with Xinerama? (i.e. multiple monitors joined + Q-58: 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 @@ -2740,7 +2761,7 @@ ied) function. (This may be due to a bug in the X server for XTEST when Xinerama is enabled). - Q-58: Can I use x11vnc on a multi-headed display that is not Xinerama + Q-59: 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 @@ -2758,12 +2779,12 @@ 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 [161]FAQ to increase the limit. It + /etc/system as mentioned in another [163]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-59: Does x11vnc support the XRANDR (X Resize, Rotate and Reflection) + Q-60: Does x11vnc support the XRANDR (X Resize, Rotate and Reflection) extension? Whenever I rotate or resize the screen x11vnc just seems to crash. @@ -2783,12 +2804,12 @@ ied) specify "-xrandr exit" then all will be disconnected and x11vnc will terminate. - Q-60: Why is the view in my VNC viewer completely black? Or why is + Q-61: 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-61: I use Linux Virtual Consoles (VC's) to implement 'Fast User + Q-62: 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 @@ -2815,7 +2836,7 @@ ied) x11vnc can poll it), one can use the switchto(1) command, e.g. "switchto 7" for VC #7. - Q-62: I am using x11vnc where my local machine has "popup/hidden + Q-63: 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 @@ -2829,7 +2850,7 @@ ied) [Misc: Clipboard, Beeps, etc.] - Q-63: Does the Clipboard/Selection get transferred between the + Q-64: 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" @@ -2839,7 +2860,11 @@ ied) want the PRIMARY selection to be polled for changes use the -noprimary option. - Q-64: Why don't I hear the "Beeps" in my X session (e.g. when typing + You may need to watch out for desktop utilities such as KDE's + "Klipper" that do odd things with the selection, clipboard, and + cutbuffers. + + Q-65: 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 @@ -2960,60 +2985,62 @@ References 105. http://www.karlrunge.com/x11vnc/index.html#FAQ-62 106. http://www.karlrunge.com/x11vnc/index.html#FAQ-63 107. http://www.karlrunge.com/x11vnc/index.html#FAQ-64 - 108. http://www.karlrunge.com/x11vnc/index.html#solarisbuilding - 109. http://www.karlrunge.com/x11vnc/index.html#building - 110. http://www.karlrunge.com/x11vnc/index.html#buildfaq - 111. http://packages.debian.org/x11vnc - 112. http://www.linuxpackages.net/search_view.php?by=name&name=x11vnc - 113. http://dag.wieers.com/packages/x11vnc/ - 114. http://www.bell-labs.com/project/wwexptools/packages.html - 115. http://www.karlrunge.com/x11vnc/index.html#solarisbuilding - 116. http://www.karlrunge.com/x11vnc/bins - 117. http://www.tightvnc.com/download.html - 118. http://www.realvnc.com/download-free.html - 119. http://sourceforge.net/projects/cotvnc/ - 120. http://www.karlrunge.com/x11vnc/x11vnc_opts.html - 121. http://www.karlrunge.com/x11vnc/x11vnc.c - 122. http://fredrik.hubbe.net/x2vnc.html - 123. http://www.hubbe.net/~hubbe/win2vnc.html - 124. http://www.deboer.gmxhome.de/ - 125. http://sourceforge.net/projects/win2vnc/ - 126. http://fredrik.hubbe.net/x2vnc.html - 127. http://freshmeat.net/projects/x2x/ - 128. http://ftp.digital.com/pub/Digital/SRC/x2x/ - 129. http://zapek.com/software/zvnc/ - 130. http://www.karlrunge.com/x11vnc/index.html#8bpp - 131. http://www.karlrunge.com/x11vnc/index.html#overlays - 132. http://www.karlrunge.com/x11vnc/index.html#xauth_pain - 133. http://www.karlrunge.com/x11vnc/index.html#noshm - 134. http://www.karlrunge.com/x11vnc/index.html#tunnelling - 135. http://www.karlrunge.com/x11vnc/index.html#passwd - 136. http://www.karlrunge.com/x11vnc/index.html#passwdfile - 137. http://www.karlrunge.com/x11vnc/index.html#inetd - 138. ftp://ftp.x.org/ - 139. http://www.karlrunge.com/x11vnc/dtVncPopup - 140. http://www.karlrunge.com/x11vnc/index.html#passwdfile - 141. http://www.karlrunge.com/x11vnc/index.html#tunnelling + 108. http://www.karlrunge.com/x11vnc/index.html#FAQ-65 + 109. http://www.karlrunge.com/x11vnc/index.html#solarisbuilding + 110. http://www.karlrunge.com/x11vnc/index.html#building + 111. http://www.karlrunge.com/x11vnc/index.html#buildfaq + 112. http://packages.debian.org/x11vnc + 113. http://www.linuxpackages.net/search_view.php?by=name&name=x11vnc + 114. http://dag.wieers.com/packages/x11vnc/ + 115. http://www.bell-labs.com/project/wwexptools/packages.html + 116. http://www.karlrunge.com/x11vnc/index.html#solarisbuilding + 117. http://www.karlrunge.com/x11vnc/bins + 118. http://www.tightvnc.com/download.html + 119. http://www.realvnc.com/download-free.html + 120. http://sourceforge.net/projects/cotvnc/ + 121. http://www.karlrunge.com/x11vnc/x11vnc_opts.html + 122. http://www.karlrunge.com/x11vnc/x11vnc.c + 123. http://fredrik.hubbe.net/x2vnc.html + 124. http://www.hubbe.net/~hubbe/win2vnc.html + 125. http://www.deboer.gmxhome.de/ + 126. http://sourceforge.net/projects/win2vnc/ + 127. http://fredrik.hubbe.net/x2vnc.html + 128. http://freshmeat.net/projects/x2x/ + 129. http://ftp.digital.com/pub/Digital/SRC/x2x/ + 130. http://zapek.com/software/zvnc/ + 131. http://www.karlrunge.com/x11vnc/index.html#8bpp + 132. http://www.karlrunge.com/x11vnc/index.html#overlays + 133. http://www.karlrunge.com/x11vnc/index.html#xauth_pain + 134. http://www.karlrunge.com/x11vnc/index.html#noshm + 135. http://www.karlrunge.com/x11vnc/index.html#tunnelling + 136. http://www.karlrunge.com/x11vnc/index.html#passwd + 137. http://www.karlrunge.com/x11vnc/index.html#passwdfile + 138. http://www.karlrunge.com/x11vnc/index.html#inetd + 139. ftp://ftp.x.org/ + 140. http://www.karlrunge.com/x11vnc/dtVncPopup + 141. http://www.karlrunge.com/x11vnc/index.html#passwdfile 142. http://www.karlrunge.com/x11vnc/index.html#tunnelling 143. http://www.karlrunge.com/x11vnc/index.html#tunnelling 144. http://www.karlrunge.com/x11vnc/index.html#tunnelling - 145. http://www.karlrunge.com/x11vnc/blockdpy.c - 146. http://www.karlrunge.com/x11vnc/index.html#display_manager - 147. http://www.karlrunge.com/x11vnc/index.html#inetd - 148. http://www.karlrunge.com/x11vnc/index.html#x11vnc_loop - 149. http://www.jirka.org/gdm-documentation/x241.html - 150. http://www.karlrunge.com/x11vnc/x11vnc_loop - 151. http://www.karlrunge.com/x11vnc/index.html#xterminal_xauth - 152. http://www.karlrunge.com/x11vnc/shm_clear - 153. http://www.karlrunge.com/x11vnc/index.html#noshm - 154. http://www.tightvnc.com/ - 155. http://www.karlrunge.com/x11vnc/tight-vncviewer-alphahack.patch - 156. http://www.tightvnc.com/ - 157. http://www.karlrunge.com/x11vnc/index.html#greaterless - 158. http://www.karlrunge.com/x11vnc/index.html#xkbmodtweak + 145. http://www.karlrunge.com/x11vnc/index.html#tunnelling + 146. http://www.karlrunge.com/x11vnc/blockdpy.c + 147. http://www.karlrunge.com/x11vnc/index.html#display_manager + 148. http://www.karlrunge.com/x11vnc/index.html#inetd + 149. http://www.karlrunge.com/x11vnc/index.html#x11vnc_loop + 150. http://www.jirka.org/gdm-documentation/x241.html + 151. http://www.karlrunge.com/x11vnc/x11vnc_loop + 152. http://www.karlrunge.com/x11vnc/index.html#xterminal_xauth + 153. http://www.karlrunge.com/x11vnc/shm_clear + 154. http://www.karlrunge.com/x11vnc/index.html#noshm + 155. http://www.tightvnc.com/ + 156. http://www.karlrunge.com/x11vnc/tight-vncviewer-alphahack.patch + 157. http://www.tightvnc.com/ + 158. http://www.karlrunge.com/x11vnc/index.html#greaterless 159. http://www.karlrunge.com/x11vnc/index.html#xkbmodtweak - 160. http://www.karlrunge.com/x11vnc/index.html#scaling - 161. http://www.karlrunge.com/x11vnc/index.html#solshm + 160. http://www.karlrunge.com/x11vnc/index.html#xkbmodtweak + 161. http://www.karlrunge.com/x11vnc/index.html#display_manager + 162. http://www.karlrunge.com/x11vnc/index.html#scaling + 163. http://www.karlrunge.com/x11vnc/index.html#solshm ======================================================================= @@ -3026,8 +3053,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.7.1pre lastmod: 2005-01-1 -6 +x11vnc: allow VNC connections to real X11 displays. 0.7.1pre lastmod: 2005-01-2 +3 Typical usage is: @@ -3161,6 +3188,8 @@ 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 +-timeout n Exit unless a client connects within the first n seconds + of startup. -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 @@ -3427,9 +3456,9 @@ Options: with -nocursor, and also some values of the "mode" option below. - Note that under XFIXES cursors with transparency - (alpha channel) will not be exactly represented and - so Overlay may be preferred. See also the -alphacut + Note that under XFIXES cursors with transparency (alpha + channel) will not be exactly represented and one may + find Overlay may be preferable. See also the -alphacut and -alphafrac options below as fudge factors to try to improve the situation for cursors with transparency for a given theme. @@ -3488,14 +3517,14 @@ Options: black background). Specify this option to remove the alpha factor. (useful for light colored semi-transparent cursors). --alphablend In XFIXES mode send cursor alpha channel data to - libvncserver. The blending effect will only be - visible in -nocursorshape mode or for clients with - cursorshapeupdates turned off. (However there is a - hack for 32bpp with depth 24, it uses the extra 8 bits - to store cursor transparency for use with a hacked - vncviewer that applies the transparency locally. - See the FAQ for more info). +-noalphablend In XFIXES mode do not send cursor alpha channel data + to libvncserver. The default is to send it. The + alphablend effect will only be visible in -nocursorshape + mode or for clients with cursorshapeupdates turned + off. (However there is a hack for 32bpp with depth 24, + it uses the extra 8 bits to store cursor transparency + for use with a hacked vncviewer that applies the + transparency locally. See the FAQ for more info). -nocursorshape Do not use the TightVNC CursorShapeUpdates extension even if clients support it. See -cursor above. @@ -3759,6 +3788,9 @@ Options: noshared disable -shared mode. forever enable -forever mode. noforever disable -forever mode. + timeout:n reset -timeout to n, if there are + currently no clients, exit unless one + connects in the next n secs. deny deny any new connections, same as "lock" nodeny allow new connections, same as "unlock" connect:host do reverse connection to host, "host" @@ -3842,8 +3874,8 @@ Options: alphafrac:f set -alphafrac to f. alpharemove enable -alpharemove mode. noalpharemove disable -alpharemove mode. - alphablend enable -alphablend mode. - noalphablend disable -alphablend mode. + alphablend disable -noalphablend mode. + noalphablend enable -noalphablend mode. cursorshape disable -nocursorshape mode. nocursorshape enable -nocursorshape mode. cursorpos disable -nocursorpos mode. @@ -3936,8 +3968,8 @@ Options: nowaitmapped flashcmap noflashcmap truecolor notruecolor overlay nooverlay overlay_cursor overlay_yescursor nooverlay_nocursor nooverlay_cursor nooverlay_yescursor - overlay_nocursor visual scale viewonly noviewonly - shared noshared forever noforever once deny lock nodeny + overlay_nocursor visual scale viewonly noviewonly shared + noshared forever noforever once timeout deny lock nodeny unlock connect allowonce allow localhost nolocalhost accept gone shm noshm flipbyteorder noflipbyteorder onetile noonetile blackout xinerama noxinerama xrandr @@ -3959,16 +3991,16 @@ Options: noalwaysshared nevershared noalwaysshared dontdisconnect nodontdisconnect desktop noremote - aro= display vncdisplay desktopname 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 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 - rfbauth passwd + aro= display vncdisplay desktopname http_url 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 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 rfbauth passwd -sync By default -remote commands are run asynchronously, that is, the request is posted and the program immediately diff --git a/x11vnc/tkx11vnc b/x11vnc/tkx11vnc index d04ee67..2fd16d7 100755 --- a/x11vnc/tkx11vnc +++ b/x11vnc/tkx11vnc @@ -139,7 +139,7 @@ Keyboard Pointer =-C:none,arrow,X,some,most cursor: noxfixes - alphablend + noalphablend -- cursorpos nocursorshape @@ -186,11 +186,12 @@ Permissions localhost =RA allowonce: -- + =RA noremote + -- viewonly shared forever - -- - =RA noremote + timeout: -- =SA alwaysshared =SA nevershared @@ -359,35 +360,39 @@ 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 would display on :11 and x11vnc would poll display :0. By default +the gui will display on the value in the DISPLAY env. variable followed +by the value from the -display option. To override this, use something +like: \"-gui otherhost:0\", etc. GUI components: --- ---------- -At the top of the gui is a info text label where information will +1) 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, +2) 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 +3) 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 +4) 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 +5) 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 @@ -396,11 +401,13 @@ 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 + +Cascades Bug: 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). +you can click on its items. Dragging with a mouse button held down will +not work (sorry!). + Key Bindings: @@ -410,6 +417,7 @@ Key Bindings: Anywhere: Control-p invokes \"Actions -> ping\" Anywhere: Control-u and Control-r invoke \"Actions -> update-all\" + Misc: Since x11vnc has so many settings and to avoid further confusion, @@ -547,7 +555,9 @@ proc help_win {item} { set text "Help on $item:\n\n" if {[is_gui_internal $item]} { - ; + if {$item != "gui" && $item != "all"} { + append text " + Is a gui internal Action (cannot be set).\n"; + } } elseif {[is_action $item]} { append text " + Is a remote control Action (cannot be set).\n"; } elseif {[active_when_connected $item]} { @@ -595,6 +605,10 @@ proc help_win {item} { } else { append text "$menu_var($item)\n" } + if {$item == "http" || $item == "httpdir" || $item == "httpport"} { + global vnc_url; + append text "\nURL: $vnc_url\n" + } } if {$item == "start"} { @@ -1221,6 +1235,8 @@ proc update_menu_vars {{query ""}} { set_x11_display $val } elseif {$item == "vncdisplay"} { set_vnc_display $val + } elseif {$item == "http_url"} { + set_vnc_url $val } } } @@ -1353,7 +1369,7 @@ proc show_logfile {} { } proc tail_logfile {} { - global menu_var unset_str + global menu_var unset_str ffont set logfile $menu_var(logfile) set txt "" @@ -1363,7 +1379,7 @@ proc tail_logfile {} { set txt "\nLogfile \"$logfile\" does not exist.\n\n" } else { set cmd "" - set xterm_cmd "xterm -geometry 80x45 -title x11vnc-logfile -e" + set xterm_cmd "xterm -sb -fn $ffont -geometry 80x45 -title x11vnc-logfile -e" set cmd [split $xterm_cmd] lappend cmd "tail" lappend cmd "+1f" @@ -1526,6 +1542,11 @@ proc do_var {item} { } else { push_new_value $item $name $new 1 } + + if {$item == "http"} { + global vnc_url + append_text " URL: $vnc_url\n" + } } } @@ -1637,12 +1658,19 @@ proc set_vnc_display {name} { global vnc_display set vnc_display "VNC display: $name" } +proc set_vnc_url {name} { + global vnc_url + set vnc_url $name +} proc no_x11_display {} { set_x11_display "(*none*)" } proc no_vnc_display {} { set_vnc_display "(*none*)" } +proc no_vnc_url {} { + set_vnc_url "(*none*)" +} proc fetch_displays {} { @@ -1651,6 +1679,7 @@ proc fetch_displays {} { set got_x11 0 set got_vnc 0 + set got_url 0 foreach item [split_query $result] { if {[regexp {^display:(.*)$} $item m0 m1]} { @@ -1659,6 +1688,9 @@ proc fetch_displays {} { } elseif {[regexp {^vncdisplay:(.*)$} $item m0 m1]} { set_vnc_display $m1 set got_vnc 1 + } elseif {[regexp {^http_url:(.*)$} $item m0 m1]} { + set_vnc_url $m1 + set got_url 1 } } if {! $got_x11} { @@ -1667,6 +1699,9 @@ proc fetch_displays {} { if {! $got_vnc} { no_vnc_display } + if {! $got_url} { + no_vnc_url + } } proc disconnect_dialog {client} { @@ -2130,6 +2165,10 @@ MenuSelect>> } } elseif {[info exists menu_var($which)]} { set label "$label value: $menu_var($which)" + if {$which == "http"} { + global vnc_url + set label "$label URL: $vnc_url" + } } } set_info $label diff --git a/x11vnc/tkx11vnc.h b/x11vnc/tkx11vnc.h index 2870d2e..8bca5a6 100644 --- a/x11vnc/tkx11vnc.h +++ b/x11vnc/tkx11vnc.h @@ -145,7 +145,7 @@ "Pointer\n" " =-C:none,arrow,X,some,most cursor:\n" " noxfixes\n" -" alphablend\n" +" noalphablend\n" " --\n" " cursorpos\n" " nocursorshape\n" @@ -192,11 +192,12 @@ " localhost\n" " =RA allowonce:\n" " --\n" +" =RA noremote\n" +" --\n" " viewonly\n" " shared\n" " forever\n" -" --\n" -" =RA noremote\n" +" timeout:\n" " --\n" " =SA alwaysshared\n" " =SA nevershared\n" @@ -365,35 +366,39 @@ "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" +"\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" +"GUI would display on :11 and x11vnc would poll display :0. By default\n" +"the gui will display on the value in the DISPLAY env. variable followed\n" +"by the value from the -display option. To override this, use something\n" +"like: \\\"-gui otherhost:0\\\", etc.\n" "\n" "\n" "GUI components: \n" "--- ----------\n" "\n" -"At the top of the gui is a info text label where information will\n" +"1) 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" +"2) 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" +"3) 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" +"4) 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" +"5) 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" @@ -402,11 +407,13 @@ "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" +"\n" +"Cascades Bug: 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" +"you can click on its items. Dragging with a mouse button held down will\n" +"not work (sorry!).\n" +"\n" "\n" "Key Bindings:\n" "\n" @@ -416,6 +423,7 @@ " Anywhere: Control-p invokes \\\"Actions -> ping\\\"\n" " Anywhere: Control-u and Control-r invoke \\\"Actions -> update-all\\\"\n" "\n" +"\n" "Misc:\n" "\n" "Since x11vnc has so many settings and to avoid further confusion,\n" @@ -553,7 +561,9 @@ " set text \"Help on $item:\\n\\n\"\n" "\n" " if {[is_gui_internal $item]} {\n" -" ;\n" +" if {$item != \"gui\" && $item != \"all\"} {\n" +" append text \" + Is a gui internal Action (cannot be set).\\n\";\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" @@ -601,6 +611,10 @@ " } else {\n" " append text \"$menu_var($item)\\n\"\n" " }\n" +" if {$item == \"http\" || $item == \"httpdir\" || $item == \"httpport\"} {\n" +" global vnc_url;\n" +" append text \"\\nURL: $vnc_url\\n\"\n" +" }\n" " }\n" "\n" " if {$item == \"start\"} {\n" @@ -1227,6 +1241,8 @@ " set_x11_display $val\n" " } elseif {$item == \"vncdisplay\"} {\n" " set_vnc_display $val\n" +" } elseif {$item == \"http_url\"} {\n" +" set_vnc_url $val\n" " }\n" " }\n" " }\n" @@ -1359,7 +1375,7 @@ "}\n" "\n" "proc tail_logfile {} {\n" -" global menu_var unset_str\n" +" global menu_var unset_str ffont\n" " set logfile $menu_var(logfile)\n" " \n" " set txt \"\"\n" @@ -1369,7 +1385,7 @@ " set txt \"\\nLogfile \\\"$logfile\\\" does not exist.\\n\\n\"\n" " } else {\n" " set cmd \"\"\n" -" set xterm_cmd \"xterm -geometry 80x45 -title x11vnc-logfile -e\"\n" +" set xterm_cmd \"xterm -sb -fn $ffont -geometry 80x45 -title x11vnc-logfile -e\"\n" " set cmd [split $xterm_cmd]\n" " lappend cmd \"tail\"\n" " lappend cmd \"+1f\"\n" @@ -1532,6 +1548,11 @@ " } else {\n" " push_new_value $item $name $new 1\n" " }\n" +"\n" +" if {$item == \"http\"} {\n" +" global vnc_url\n" +" append_text \" URL: $vnc_url\\n\"\n" +" }\n" " }\n" "}\n" "\n" @@ -1643,12 +1664,19 @@ " global vnc_display\n" " set vnc_display \"VNC display: $name\"\n" "}\n" +"proc set_vnc_url {name} {\n" +" global vnc_url\n" +" set vnc_url $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 no_vnc_url {} {\n" +" set_vnc_url \"(*none*)\"\n" +"}\n" "\n" "proc fetch_displays {} {\n" "\n" @@ -1657,6 +1685,7 @@ "\n" " set got_x11 0\n" " set got_vnc 0\n" +" set got_url 0\n" "\n" " foreach item [split_query $result] {\n" " if {[regexp {^display:(.*)$} $item m0 m1]} {\n" @@ -1665,6 +1694,9 @@ " } elseif {[regexp {^vncdisplay:(.*)$} $item m0 m1]} {\n" " set_vnc_display $m1\n" " set got_vnc 1\n" +" } elseif {[regexp {^http_url:(.*)$} $item m0 m1]} {\n" +" set_vnc_url $m1\n" +" set got_url 1\n" " }\n" " }\n" " if {! $got_x11} {\n" @@ -1673,6 +1705,9 @@ " if {! $got_vnc} {\n" " no_vnc_display\n" " }\n" +" if {! $got_url} {\n" +" no_vnc_url\n" +" }\n" "}\n" "\n" "proc disconnect_dialog {client} {\n" @@ -2136,6 +2171,10 @@ " }\n" " } elseif {[info exists menu_var($which)]} {\n" " set label \"$label value: $menu_var($which)\"\n" +" if {$which == \"http\"} {\n" +" global vnc_url\n" +" set label \"$label URL: $vnc_url\"\n" +" }\n" " }\n" " }\n" " set_info $label\n" diff --git a/x11vnc/x11vnc.1 b/x11vnc/x11vnc.1 index baca7ed..f6e2ce9 100644 --- a/x11vnc/x11vnc.1 +++ b/x11vnc/x11vnc.1 @@ -2,7 +2,7 @@ .TH X11VNC "1" "January 2005" "x11vnc " "User Commands" .SH NAME x11vnc - allow VNC connections to real X11 displays - version: 0.7.1pre, lastmod: 2005-01-16 + version: 0.7.1pre, lastmod: 2005-01-23 .SH SYNOPSIS .B x11vnc [OPTION]... @@ -191,6 +191,11 @@ 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-timeout\fR \fIn\fR +.IP +Exit unless a client connects within the first n seconds +of startup. +.PP \fB-inetd\fR .IP Launched by @@ -574,9 +579,9 @@ default (see \fB-noxfixes\fR below). This can be disabled with \fB-nocursor,\fR and also some values of the "mode" option below. .IP -Note that under XFIXES cursors with transparency -(alpha channel) will not be exactly represented and -so Overlay may be preferred. See also the \fB-alphacut\fR +Note that under XFIXES cursors with transparency (alpha +channel) will not be exactly represented and one may +find Overlay may be preferable. See also the \fB-alphacut\fR and \fB-alphafrac\fR options below as fudge factors to try to improve the situation for cursors with transparency for a given theme. @@ -647,16 +652,16 @@ black background). Specify this option to remove the alpha factor. (useful for light colored semi-transparent cursors). .PP -\fB-alphablend\fR +\fB-noalphablend\fR .IP -In XFIXES mode send cursor alpha channel data to -libvncserver. The blending effect will only be -visible in \fB-nocursorshape\fR mode or for clients with -cursorshapeupdates turned off. (However there is a -hack for 32bpp with depth 24, it uses the extra 8 bits -to store cursor transparency for use with a hacked -vncviewer that applies the transparency locally. -See the FAQ for more info). +In XFIXES mode do not send cursor alpha channel data +to libvncserver. The default is to send it. The +alphablend effect will only be visible in \fB-nocursorshape\fR +mode or for clients with cursorshapeupdates turned +off. (However there is a hack for 32bpp with depth 24, +it uses the extra 8 bits to store cursor transparency +for use with a hacked vncviewer that applies the +transparency locally. See the FAQ for more info). .PP \fB-nocursorshape\fR .IP @@ -1010,6 +1015,10 @@ forever enable \fB-forever\fR mode. .IP noforever disable \fB-forever\fR mode. .IP +timeout:n reset \fB-timeout\fR to n, if there are + currently no clients, exit unless one + connects in the next n secs. +.IP deny deny any new connections, same as "lock" .IP nodeny allow new connections, same as "unlock" @@ -1150,9 +1159,9 @@ alpharemove enable \fB-alpharemove\fR mode. .IP noalpharemove disable \fB-alpharemove\fR mode. .IP -alphablend enable \fB-alphablend\fR mode. +alphablend disable \fB-noalphablend\fR mode. .IP -noalphablend disable \fB-alphablend\fR mode. +noalphablend enable \fB-noalphablend\fR mode. .IP cursorshape disable \fB-nocursorshape\fR mode. .IP @@ -1306,8 +1315,8 @@ refresh reset close disconnect id sid waitmapped nowaitmapped flashcmap noflashcmap truecolor notruecolor overlay nooverlay overlay_cursor overlay_yescursor nooverlay_nocursor nooverlay_cursor nooverlay_yescursor -overlay_nocursor visual scale viewonly noviewonly -shared noshared forever noforever once deny lock nodeny +overlay_nocursor visual scale viewonly noviewonly shared +noshared forever noforever once timeout deny lock nodeny unlock connect allowonce allow localhost nolocalhost accept gone shm noshm flipbyteorder noflipbyteorder onetile noonetile blackout xinerama noxinerama xrandr @@ -1329,16 +1338,16 @@ httpdir enablehttpproxy noenablehttpproxy alwaysshared noalwaysshared nevershared noalwaysshared dontdisconnect nodontdisconnect desktop noremote .IP -aro= display vncdisplay desktopname 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 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 -rfbauth passwd +aro= display vncdisplay desktopname http_url 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 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 rfbauth passwd .PP \fB-sync\fR .IP diff --git a/x11vnc/x11vnc.c b/x11vnc/x11vnc.c index 8c26ee2..97a9ae2 100644 --- a/x11vnc/x11vnc.c +++ b/x11vnc/x11vnc.c @@ -161,11 +161,11 @@ #include #include +#if OLD_TREE /* * 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 @@ -178,6 +178,10 @@ #define rfbEncryptAndStorePasswd vncEncryptAndStorePasswd #define maxClientWait rfbMaxClientWait #define rfbHttpInitSockets httpInitSockets + +#define RFBUNDRAWCURSOR(s) if (s) {rfbUndrawCursor(s);} +#else +#define RFBUNDRAWCURSOR(s) #endif #if LIBVNCSERVER_HAVE_XSHM @@ -255,7 +259,7 @@ int got_xfixes_cursor_notify = 0; int alpha_threshold = 240; double alpha_frac = 0.33; int alpha_remove = 0; -int alpha_blend = 0; +int alpha_blend = 1; #if LIBVNCSERVER_HAVE_LIBXFIXES #include @@ -269,7 +273,7 @@ static int xdamage_base_event_type; #endif /* date +'lastmod: %Y-%m-%d' */ -char lastmod[] = "0.7.1pre lastmod: 2005-01-16"; +char lastmod[] = "0.7.1pre lastmod: 2005-01-23"; /* X display info */ @@ -545,6 +549,7 @@ int view_only = 0; /* clients can only watch. */ char *viewonly_passwd = NULL; /* view only passwd. */ int inetd = 0; /* spawned from inetd(1) */ int connect_once = 1; /* disconnect after first connection session. */ +int first_conn_timeout = 0; /* -timeout */ int flash_cmap = 0; /* follow installed colormaps */ int force_indexed_color = 0; /* whether to force indexed color for 8bpp */ int launch_gui = 0; /* -gui */ @@ -556,6 +561,7 @@ int nofb = 0; /* do not send any fb updates */ unsigned long subwin = 0x0; /* -id, -sid */ int subwin_wait_mapped = 0; +int no_su = 0; /* -nosu */ int xinerama = 0; /* -xinerama */ int xrandr = 0; /* -xrandr */ @@ -680,6 +686,15 @@ int got_nevershared = 0; /* -- util.h -- */ +// FYI: GB='garbage begin' GE='garbage end'. Lines ending in //G trimmed too. +int debug = 0; +int libvnc_count = 0; + +#if defined(__sun) +long long libvnc_time; +#else +double libvnc_time; +#endif #define NONUL(x) ((x) ? (x) : "") /* XXX usleep(3) is not thread safe on some older systems... */ @@ -808,6 +823,7 @@ int pick_windowid(unsigned long *num) { if (fgets(line, 512, p) == NULL) { break; } +if (0) fprintf(stderr, "line: %s\n", line); q = strstr(line, " id: 0x"); if (q) { q += 5; @@ -1179,6 +1195,7 @@ void XTestFakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, rfbLog("XTestFakeKeyEvent(dpy, keycode=0x%x \"%s\", %s)\n", key, XKeysymToString(XKeycodeToKeysym(dpy, key, 0)), down ? "down":"up"); + fflush(stderr); } if (! xtest_present) { return; @@ -1265,6 +1282,82 @@ void XTestDiscard_wr(Display *dpy) { } +/* -- vcr.h -- */ +/* + * experiment to look for vertical copy rect changes + */ + +void do_vcr(void); +void initialize_vcr(void); + +#define VCR_HACK +#ifdef VCR_HACK +int vcopyrect = 0; + +/* + * vcr_map is a pattern of horizontal offsets for vertical scanlines + * within a column of changed tiles. 15 is in the middle of the tile, + * 0 the left edge, 31 the right edge, etc. It is kind of a recursive + * bisection of the tile. + */ +int vcr_map[] = { + 15, 0, 31, 7, 23, 3, 27, 11, 19, + 1, 5, 9, 13, 17, 21, 25, 29, + 2, 4, 6, 8, 10, 12, 14, 16, + 18, 20, 22, 24, 26, 28, 30 +}; + +/* + * VCR_DEPTH says how far into the vcr_map[] pattern we consider + * e.g. 5 means we consider offsets: 15, 0, 31, 7, 23. + */ +#define VCR_DEPTH 32 +//int vcr_depth = VCR_DEPTH; +int vcr_depth = 9; + +/* + * fbd_old and fbd_new are the vertical "scan lines" of fb data. + * The "40" is just an index into which column (x value) of tiles it + * corresponds to. + * The VCR_DEPTH is an index for which of the above vcr_map[] offsets. + * The 1024 is how tall the display can possibly be (i.e. 1280x1024) + * + * fdb_old contains framebuffer pixel data before the copy_tiles() update. + * fdb_new contains framebuffer pixel data after the copy_tiles() update. + * + * By comparing these vectors with one shifted by a vertical offset, + * we can look for the offset that gives a long run of perfect coincidence + * of the fb pixel data. If a coincidence found, then we have a verticle + * offset for a potential copyRegion transfer (if we find a number > + * 1 of adjacent scanlines with the same offset to form a rectangle). + * + */ +void ***fbd_old; +void ***fbd_new; +char ***fbd_old8; +char ***fbd_new8; +short ***fbd_old16; +short ***fbd_new16; +int ***fbd_old32; +int ***fbd_new32; + +/* smaller temporary ones to improve horrendous I/O (i.e. fewer pages) */ +void **fbt_old; +void **fbt_new; +char **fbt_old8; +char **fbt_new8; +short **fbt_old16; +short **fbt_new16; +int **fbt_old32; +int **fbt_new32; +//int fbd_old[40][VCR_DEPTH][1024]; +//int fbd_new[40][VCR_DEPTH][1024]; + +sraRegionPtr vcr_change_region = NULL; +sraRegionPtr vcr_mod_region = NULL; + +#endif + /* -- cleanup.c -- */ /* * Exiting and error handling routines @@ -1350,6 +1443,7 @@ void clean_up_exit (int ret) { static void interrupted (int sig) { exit_sig = sig; if (exit_flag) { + fprintf(stderr, "extra[%d] signal: %d\n", exit_flag, sig); exit_flag++; if (use_threads) { usleep2(250 * 1000); @@ -1366,6 +1460,7 @@ static void interrupted (int sig) { } if (sig == SIGINT) { shut_down = 1; + fprintf(stderr, "interrupted: set shut_down flag for SIGINT\n"); return; } @@ -1386,6 +1481,10 @@ static void interrupted (int sig) { autorepeat(1); } +if (getenv("GDB")) { + fprintf(stderr, "getc> "); + getc(stdin); +} if (sig) { exit(2); } @@ -1515,6 +1614,31 @@ int get_window_size(Window win, int *x, int *y) { } } +int bs_w = 0; +void remove_backingstore_and_saveunders(Window win) { + int i, nchild; + Window root, parent; + Window *child_list; + XSetWindowAttributes sattr; + for (i=0; i< bs_w; i++) { + fprintf(stderr, " "); + } + bs_w += 2; + fprintf(stderr, "0x%lx\n", win); + sattr.save_under = False; + XChangeWindowAttributes(dpy, win, CWSaveUnder, &sattr); + + if (XQueryTree(dpy, win, &root, &parent, &child_list, &nchild)) { + if (child_list && nchild) { + for (i=0; i %d, y: %d -> %d\n", subwin, dpy_x, new_x, dpy_y, new_y); rfbLog("calling handle_xrandr_change() for resizing\n"); @@ -5102,6 +5277,7 @@ int check_xrandr_event(char *msg) { 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; @@ -5148,9 +5324,11 @@ int check_xrandr_event(char *msg) { 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; } @@ -5243,6 +5421,9 @@ static void selection_request(XEvent *ev) { } else { length = 0; } +rfbLog("selection_request: owner=0x%x requestor=0x%x sel=%d targ=%d prop=%d\n", + req_event->owner, req_event->requestor, req_event->selection, + req_event->target, req_event->property); /* the window may have gone away, so trap errors */ trapped_xerror = 0; @@ -6486,6 +6667,22 @@ char *process_remote_cmd(char *cmd, int stringonly) { rfbLog("process_remote_cmd: disable -forever mode.\n"); connect_once = 1; + } else if (strstr(p, "timeout") == p) { + int to; + COLON_CHECK("timeout:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, + first_conn_timeout); + goto qry; + } + p += strlen("timeout:"); + to = atoi(p); + if (to > 0 ) { + to = -to; + } + first_conn_timeout = to; + rfbLog("process_remote_cmd: set -timeout to %d\n", -to); + } else if (!strcmp(p, "deny") || !strcmp(p, "lock")) { if (query) { snprintf(buf, bufn, "ans=%s:%d", p, deny_all); @@ -7062,9 +7259,7 @@ char *process_remote_cmd(char *cmd, int stringonly) { goto qry; } rfbLog("process_remote_cmd: enabling -repeat mode.\n"); - if (no_autorepeat) { - autorepeat(1); - } + autorepeat(1); /* restore initial setting */ no_autorepeat = 0; } else if (!strcmp(p, "norepeat")) { @@ -7073,11 +7268,10 @@ char *process_remote_cmd(char *cmd, int stringonly) { goto qry; } rfbLog("process_remote_cmd: enabling -norepeat mode.\n"); - if (! no_autorepeat && client_count) { - no_autorepeat = 1; - autorepeat(0); - } no_autorepeat = 1; + if (client_count) { + autorepeat(0); /* disable if any clients */ + } } else if (!strcmp(p, "fb")) { if (query) { @@ -7887,6 +8081,14 @@ char *process_remote_cmd(char *cmd, int stringonly) { } else if (!strcmp(p, "desktopname")) { snprintf(buf, bufn, "aro=%s:%s", p, NONUL(rfb_desktop_name)); + } else if (!strcmp(p, "http_url")) { + if (screen->httpListenSock > -1) { + snprintf(buf, bufn, "aro=%s:http://%s:%d", p, + NONUL(screen->thisHost), screen->httpPort); + } else { + snprintf(buf, bufn, "aro=%s:%s", p, + "http_not_active"); + } } else if (!strcmp(p, "auth")) { snprintf(buf, bufn, "aro=%s:%s", p, NONUL(auth_file)); } else if (!strcmp(p, "rootshift")) { @@ -8317,6 +8519,7 @@ void setup_cursors(void) { first = 0; if (screen) { + RFBUNDRAWCURSOR(screen); screen->cursor = NULL; LOCK(screen->cursorMutex); } @@ -8683,6 +8886,8 @@ int get_xfixes_cursor(int init) { } } + RFBUNDRAWCURSOR(screen); + /* we need to create the cursor and overwrite oldest */ use = oldest; if (cursors[use]->rfb) { @@ -8762,7 +8967,9 @@ int get_xfixes_cursor(int init) { } if (n_opaque >= alpha_frac * n_alpha) { thresh = alpha_threshold; +fprintf(stderr, "OK thresh: %d/%d\n", n_opaque, n_alpha); } else { + int o_opaque = n_opaque; n_opaque = 0; for (i=255; i>=0; i--) { n_opaque += histo[i]; @@ -8771,6 +8978,8 @@ int get_xfixes_cursor(int init) { break; } } +fprintf(stderr, "changed thresh: %d -> %d [%d->%d]/%d\n", + alpha_threshold, thresh, o_opaque, n_opaque, n_alpha); } i = 0; @@ -8977,7 +9186,7 @@ int get_which_cursor(void) { XErrorHandler old_handler; int mode = 0; - if (drag_in_progress) { + if (drag_in_progress || button_mask) { return -1; } @@ -9062,6 +9271,7 @@ int get_which_cursor(void) { return which; } +#if OLD_TREE /* * Some utilities for marking the little cursor patch region as * modified, etc. @@ -9074,12 +9284,11 @@ void mark_cursor_patch_modified(rfbScreenInfoPtr s, int old) { return; } - /* TODO Karl: is this needed any longer? */ - /* if (old) { - /* use oldCursor pos *//* + if (old) { + /* use oldCursor pos */ curx = s->oldCursorX; cury = s->oldCursorY; - } else */ { + } else { curx = s->cursorX; cury = s->cursorY; } @@ -9101,6 +9310,7 @@ void mark_cursor_patch_modified(rfbScreenInfoPtr s, int old) { rfbMarkRectAsModified(s, x1, y1, x1+x2, y1+y2); } +#endif void set_cursor_was_changed(rfbScreenInfoPtr s) { rfbClientIteratorPtr iter; @@ -9235,7 +9445,10 @@ void cursor_position(int x, int y) { rfbClientIteratorPtr iter; rfbClientPtr cl; int cnt = 0, nonCursorPosUpdates_clients = 0; - int x_old, y_old, x_in = x, y_in = y; + int x_in = x, y_in = y; +#if OLD_TREE + int x_old, y_old; +#endif /* x and y are current positions of X11 pointer on the X11 display */ if (!screen) { @@ -9252,16 +9465,27 @@ void cursor_position(int x, int y) { if (x == screen->cursorX && y == screen->cursorY) { return; } - /* TODO Karl: do we really need x_old,y_old? */ - /* + +#if OLD_TREE x_old = screen->oldCursorX; y_old = screen->oldCursorY; - */ + if (screen->cursorIsDrawn) { + rfbUndrawCursor(screen); + } + + LOCK(screen->cursorMutex); + if (! screen->cursorIsDrawn) { + screen->cursorX = x; + screen->cursorY = y; + } + UNLOCK(screen->cursorMutex); +#else LOCK(screen->cursorMutex); screen->cursorX = x; screen->cursorY = y; UNLOCK(screen->cursorMutex); +#endif iter = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(iter)) ) { @@ -9296,13 +9520,13 @@ void cursor_position(int x, int y) { } rfbReleaseClientIterator(iter); - /* TODO Karl: do we need x_old, y_old? */ - /* +#if OLD_TREE if (nonCursorPosUpdates_clients && show_cursor) { if (x_old != x || y_old != y) { mark_cursor_patch_modified(screen, 0); } - }*/ + } +#endif if (debug_pointer && cnt) { rfbLog("cursor_position: sent position x=%3d y=%3d to %d" @@ -9310,12 +9534,27 @@ void cursor_position(int x, int y) { } } +#if !OLD_TREE void set_rfb_cursor(int which) { - int workaround = 1; /* if rfbSetCursor does not mark modified */ -#if OLD_TREE - workaround = 2; -#endif + if (! show_cursor) { + return; + } + if (! screen) { + return; + } + + if (!cursors[which] || !cursors[which]->rfb) { + rfbLog("non-existent cursor: which=%d\n", which); + return; + } else { + rfbSetCursor(screen, cursors[which]->rfb); + } +} + +#else + +void set_rfb_cursor(int which) { if (! show_cursor) { return; @@ -9324,7 +9563,7 @@ void set_rfb_cursor(int which) { return; } - if (workaround && screen->cursor) { + if (screen->cursor) { int all_are_cursor_pos = 1; rfbClientIteratorPtr iter; rfbClientPtr cl; @@ -9349,27 +9588,21 @@ void set_rfb_cursor(int which) { rfbLog("non-existent cursor: which=%d\n", which); return; } else { - rfbSetCursor(screen, cursors[which]->rfb); + rfbSetCursor(screen, cursors[which]->rfb, FALSE); } - /* TODO Karl: is this still necessary? */ - /* this is a 2nd workaround for rfbSetCursor() */ - if (workaround > 1) { - if (screen->underCursorBuffer == NULL && - screen->underCursorBufferLen != 0) { - LOCK(screen->cursorMutex); - screen->underCursorBufferLen = 0; - UNLOCK(screen->cursorMutex); - } - } - - /* TODO Karl: is this still necessary? */ - if (workaround) { - set_cursor_was_changed(screen); + if (screen->underCursorBuffer == NULL && + screen->underCursorBufferLen != 0) { + LOCK(screen->cursorMutex); + screen->underCursorBufferLen = 0; + UNLOCK(screen->cursorMutex); } + set_cursor_was_changed(screen); } +#endif void set_no_cursor(void) { + RFBUNDRAWCURSOR(screen); set_rfb_cursor(CURS_EMPTY); } @@ -9497,6 +9730,8 @@ void set_colormap(int reset) { /* XXX XQueryTree somehow? */ XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &m); if (c && XGetWindowAttributes(dpy, c, &attr)) { + if (debug) fprintf(stderr, "child: %d 0x%x cmap: 0x%x map_installed: %d\n", + tries, (int) c, (int) attr.colormap, attr.map_installed); if (attr.colormap && attr.map_installed) { cmap = attr.colormap; vis = attr.visual; @@ -9546,6 +9781,7 @@ void set_colormap(int reset) { } if (diffs && ! first) { + if (debug) fprintf(stderr, "set_colormap: %d changed colormap entries.\n", diffs); if (! all_clients_initialized()) { rfbLog("set_colormap: warning: sending cmap " "with uninitialized clients.\n"); @@ -9999,6 +10235,9 @@ XImage *initialize_xdisplay_fb(void) { usleep(250 * 1000); goto again; } + if (no_su) { + remove_backingstore_and_saveunders(window); + } if (use_snapfb) { initialize_snap_fb(); } @@ -10644,6 +10883,14 @@ void blackout_tiles(void) { * to simplify things drop down to single copy mode, no vcr, etc... */ single_copytile = 1; +#ifdef VCR_HACK +#if 0 + if (vcopyrect) { + rfbLog("disabling vertical copyrect due to blackouts\n"); + vcopyrect = 0; + } +#endif +#endif /* loop over all tiles. */ for (ty=0; ty < ntiles_y; ty++) { @@ -10825,6 +11072,7 @@ void initialize_xinerama (void) { /* max len is 10000x10000+10000+10000 (23 chars) per geometry */ rcnt = (int) sraRgnCountRects(black_region); + fprintf(stderr, "xinerama: rcnt=%d\n", rcnt); bstr = (char *) malloc(30 * (rcnt+1) * sizeof(char)); tstr = (char *) malloc(30 * sizeof(char)); bstr[0] = '\0'; @@ -10851,6 +11099,7 @@ void initialize_xinerama (void) { sprintf(tstr, "%dx%d+%d+%d,", w, h, x, y); strcat(bstr, tstr); } + fprintf(stderr, "xinerama: bstr: %s\n", bstr); initialize_blackouts(bstr); free(bstr); @@ -10963,6 +11212,47 @@ typedef struct tile_change_region { /* array to hold the tiles region_t-s. */ static region_t *tile_region; +/* for measurements: */ +static int gap_got, gap_tot, isl_got, isl_tot, noretry; +static int hints_used, hint_cnt; +static int wrote_copy_tile_header = 0; +static int scan_cnt = 0, scan_cnt2 = 0; +static double scan_ave = 0, scan_ave2 = 0; +static int memcmp_scan = 0, memcmp_copy = 0, memcpy_copy = 0; +static int xget_scan = 0, xget_copy = 0; +static int scale_data = 0; + +#if defined(__sun) +static long long memcpy_copy_time, memcmp_copy_time, memcmp_scan_time; +static long long copy_tile_time, xshmget_scan_time, xshmget_copy_time; +static long long scale_time; +static hrtime_t beg, end; +static hrtime_t beg2, end2; +#define BEG_FAC 1000000000 +#define BEG if(debug) {beg = gethrtime();} +#define BEG2 if(debug) {beg2 = gethrtime();} +#define END(x) if(debug) {end = gethrtime(); x += (long long) (end - beg);} +#define END2(x) if(debug) {end2 = gethrtime(); x += (long long) (end2 - beg2);} + +#else +/* not very fast or accurate */ +static double memcpy_copy_time, memcmp_copy_time, memcmp_scan_time; +static double copy_tile_time, xshmget_scan_time, xshmget_copy_time; +static double scale_time; +static double doub1 = 0.0, doub2 = 0.0; +static double beg, end; +static double beg2, end2; +#define BEG_FAC 1 +#define BEG if(debug) {beg = dtime(&doub1);} +#define BEG2 if(debug) {beg2 = dtime(&doub2);} +#define END(x) if(debug) {end = dtime(&doub1); x += (end);} +#define END2(x) if(debug) {end2 = dtime(&doub2); x += (end2);} +#endif + +#ifdef VCR_HACK +static int vcr_shift[VCR_DEPTH];/* vcr_map[]*pixelsize, use in copytile */ +static int vcr_cnt = 0; /* for statistics. */ +#endif /* * setup tile numbers and allocate the tile and hint arrays: @@ -11245,6 +11535,11 @@ void initialize_polling_images(void) { fs_frac = 1.1; fs_factor = 0; } + if (debug) { + int fs_tmp = fs_factor ? fs_factor : 1; + rfbLog("fs_factor: %d shm: %d\n", fs_factor, + (bpp/8) * dpy_x * (dpy_y/fs_tmp) ); + } if (! fs_factor) { rfbLog("warning: fullscreen updates are disabled.\n"); } else { @@ -11399,7 +11694,82 @@ static void hint_updates(void) { in_run = 0; } } + hint_cnt = hint_count; + +#ifdef VCR_HACK + if (vcopyrect && vcr_change_region != NULL) { + static char **pic = NULL; + int tx2, ty2, x2, y2; + sraRegionPtr tmp_region; + + if (pic == NULL) { + int n; + pic = (char **) malloc(ntiles_x * sizeof(char *)); + for (n=0; n < ntiles_x; n++) { + pic[n] = (char *) malloc(ntiles_y * sizeof(char)); + } + } + for (ty2=0; ty2 < ntiles_y; ty2++) { + for (tx2=0; tx2 < ntiles_x; tx2++) { + char c = ' '; + n = tx2 + ty2 * ntiles_x; + + if (tile_has_diff[n]) { + c = 'X'; + } else if (tile_tried[n]) { + c = 't'; + } + x2 = tx2 * tile_x; + y2 = ty2 * tile_y; + + tmp_region = (sraRegionPtr) + sraRgnCreateRect(x2, y2, x2 + tile_x, y2 + tile_y); + if (sraRgnAnd(tmp_region, vcr_change_region)) { + if (c == 'X') { + c = '-'; + } else if (c == 't') { + c = '_'; + } else { + c = '.'; + } + } + pic[tx2][ty2] = c; + sraRgnDestroy(tmp_region); + } + } + fprintf(stderr, "\n"); + for (ty2=0; ty2 < ntiles_y; ty2++) { + for (tx2=0; tx2 < ntiles_x; tx2++) { + fprintf(stderr, "%c", pic[tx2][ty2]); + } + fprintf(stderr, "\n"); + } + fprintf(stderr, "\n"); + fprintf(stderr, "vcr_change_region: 0x%x Rects: %d ", (int) vcr_change_region, (int) sraRgnCountRects(vcr_change_region)); + fprintf(stderr, "vcr_mod_region: 0x%x Rects: %d ", (int) vcr_mod_region, (int) sraRgnCountRects(vcr_mod_region)); + for (i=0; i < hint_count; i++) { + int x = hint_list[i].x; + int y = hint_list[i].y; + int w = hint_list[i].w; + int h = hint_list[i].h; + tmp_region = (sraRegionPtr) + sraRgnCreateRect(x, y, x + w, y + h); + sraRgnSubtract(tmp_region, vcr_change_region); + if (!sraRgnEmpty(tmp_region)) { + fprintf(stderr, "M"); + rfbMarkRegionAsModified(screen, tmp_region); + } else { + fprintf(stderr, "_"); + } + sraRgnDestroy(tmp_region); + } + fprintf(stderr, "\n"); + sraRgnDestroy(vcr_change_region); + vcr_change_region = NULL; + return; + } +#endif /* VCR_HACK */ for (i=0; i < hint_count; i++) { /* pass update info to vnc: */ @@ -11581,6 +11951,7 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) { int shrink; /* whether shrinking or expanding */ static int constant_weights = -1, cnt = 0; +BEG if (scale_fac <= 1.0) { shrink = 1; } else { @@ -11688,6 +12059,8 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) { constant_weights = 1; } } +fprintf(stderr, " scale dx: %.15f\n", dx); +fprintf(stderr, " scale dy: %.15f\n", dy); } /* set these all to 1.0 to begin with */ wx = 1.0; @@ -11741,6 +12114,7 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) { I2 = I1 + 1; /* simple interpolation */ ddx = x1 - I1; } +//if (first) fprintf(stderr, " I1=%d I2=%d J1=%d J2=%d\n", I1, I2, J1, J2); /* Zero out accumulators for next pixel average: */ for (b=0; b<4; b++) { @@ -11851,6 +12225,12 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) { w = wx * wy; wtot += w; +#if 0 +if (cnt % 37 == 0) + fprintf(stderr, " w=%15.12f wx=%15.12f wy=%15.12f wtot=%8.3f " + "i=%3d j=%3d idx=%8.3f jdy=%8.3f I=%3d J=%3d\n", + w, wx, wy, wtot, i, j, i * dx, j * dy, I, J); +#endif /* * We average the unsigned char value @@ -11876,6 +12256,7 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) { pixave[2] += w*(us & main_blue_mask); } src += Bpp; +scale_data += Bpp; } } @@ -11902,6 +12283,7 @@ static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) { dest += Bpp; } } +END(scale_time) mark_rect_as_modified(i1, j1, i2, j2, 1); } @@ -11966,6 +12348,7 @@ static int copy_tiles(int tx, int ty, int nt) { char *src, *dst, *s_src, *s_dst, *m_src, *m_dst; char *h_src, *h_dst; +BEG2 if (! first_line) { /* allocate arrays first time in. */ int n = ntiles_x + 1; @@ -12009,11 +12392,14 @@ static int copy_tiles(int tx, int ty, int nt) { } X_LOCK; +BEG XRANDR_SET_TRAP_RET(-1, "copy_tile-set"); /* read in the whole tile run at once: */ copy_image(tile_row[nt], x, y, size_x, size_y); XRANDR_CHK_TRAP_RET(-1, "copy_tile-chk"); +xget_copy += size_x * size_y * pixelsize; +END(xshmget_copy_time) X_UNLOCK; if (blackouts && tile_blackout[n].cover == 1) { @@ -12036,6 +12422,7 @@ static int copy_tiles(int tx, int ty, int nt) { w = (x2 - x1) * pixelsize; s = x1 * pixelsize; +//fprintf(stderr, "rbo: %d w=%d s=%d x=%d-%d y=%d-%d b=%d\n", n, w, s, x1, x2, y1, y2, b); for (line = 0; line < size_y; line++) { if (y1 <= line && line < y2) { memset(b_dst + s, fill, (size_t) w); @@ -12054,6 +12441,63 @@ static int copy_tiles(int tx, int ty, int nt) { for (t=1; t <= nt; t++) { first_line[t] = -1; } +BEG +#ifdef VCR_HACK +/* + * This is the great copyrect hunt macro. See the comments in copytile() + * for the general description. The only difference here in copy_tiles() + * is we have more than one horizontal tile index, tx+(t-1), to deal + * with. + */ + {int k; + for (k=0; k < vcr_depth; k++) { + vcr_shift[k] = vcr_map[k]*pixelsize; + } + } +#define VCR_FB0(DST,SRC) \ + if (vcopyrect) { \ + int k, yl = y + line; \ + if (bpp == 32) { \ + for (k=0; k < vcr_depth; k++) { \ + memcpy(fbd_old32[tx+(t-1)][k]+yl, \ + ((DST)+vcr_shift[k]), 4); \ + memcpy(fbd_new32[tx+(t-1)][k]+yl, \ + ((SRC)+vcr_shift[k]), 4); \ + } \ + } else if (bpp == 16) { /* XXX */ \ + for (k=0; k < vcr_depth; k++) { \ + memcpy(fbd_old16[tx+(t-1)][k]+yl, \ + ((DST)+vcr_shift[k]), 2); \ + memcpy(fbd_new16[tx+(t-1)][k]+yl, \ + ((SRC)+vcr_shift[k]), 2); \ + } \ + } else if (bpp == 8) { /* XXX */ \ + for (k=0; k < vcr_depth; k++) { \ + memcpy(fbd_old8[tx+(t-1)][k]+yl, \ + ((DST)+vcr_shift[k]), 1); \ + memcpy(fbd_new8[tx+(t-1)][k]+yl, \ + ((SRC)+vcr_shift[k]), 1); \ + } \ + } \ + vcr_cnt++; \ + } + +#define VCR_FB(DST,SRC) \ + if (vcopyrect) { \ + int k, yl = y + line; \ + if (bpp == 32) { \ + for (k=0; k < vcr_depth; k++) { \ + memcpy(fbt_old32[(t-1)*tile_x + k]+(yl % tile_y), \ + ((DST)+vcr_shift[k]), 4); \ + memcpy(fbt_new32[(t-1)*tile_x + k]+(yl % tile_y), \ + ((SRC)+vcr_shift[k]), 4); \ + } \ + } \ + vcr_cnt++; \ + } +#else +#define VCR_FB(DST,SRC) +#endif /* VCR_HACK */ /* find the first line with difference: */ w1 = width1 * pixelsize; @@ -12077,10 +12521,13 @@ static int copy_tiles(int tx, int ty, int nt) { if (memcmp(s_dst + off, s_src + off, len)) { first_line[t] = line; } + else { VCR_FB(s_dst + off, s_src + off); } + memcmp_copy += len; } s_src += tile_row[nt]->bytes_per_line; s_dst += main_bytes_per_line; } +END(memcmp_copy_time) /* see if there were any differences for any tile: */ first_min = -1; @@ -12097,6 +12544,7 @@ static int copy_tiles(int tx, int ty, int nt) { for (t=1; t <= nt; t++) { tile_has_diff[n+(t-1)] = 0; } +END2(copy_tile_time) return(0); } else { /* @@ -12113,6 +12561,7 @@ static int copy_tiles(int tx, int ty, int nt) { } } +BEG m_src = src + (tile_row[nt]->bytes_per_line * size_y); m_dst = dst + (main_bytes_per_line * size_y); @@ -12144,9 +12593,11 @@ static int copy_tiles(int tx, int ty, int nt) { } else { len = w1; } + memcmp_copy += len; if (memcmp(m_dst + off, m_src + off, len)) { last_line[t] = line; } + else {VCR_FB(m_dst + off, m_src + off); } } } @@ -12199,6 +12650,7 @@ static int copy_tiles(int tx, int ty, int nt) { dx = dx1; } + memcmp_copy += (2 - left_diff[t] - right_diff[t]) * dw; if (! left_diff[t] && memcmp(h_dst + off, h_src + off, dw)) { left_diff[t] = 1; @@ -12211,17 +12663,50 @@ static int copy_tiles(int tx, int ty, int nt) { h_src += tile_row[nt]->bytes_per_line; h_dst += main_bytes_per_line; } +END(memcmp_copy_time) /* now finally copy the difference to the rfb framebuffer: */ +BEG s_src = src + tile_row[nt]->bytes_per_line * first_min; s_dst = dst + main_bytes_per_line * first_min; +#ifdef VCR_HACK + if (! vcopyrect) { +#endif for (line = first_min; line <= last_max; line++) { /* for I/O speed we do not do this tile by tile */ memcpy(s_dst, s_src, size_x * pixelsize); + memcpy_copy += size_x * pixelsize; + s_src += tile_row[nt]->bytes_per_line; + s_dst += main_bytes_per_line; + } +#ifdef VCR_HACK + } else { + w1 = width1 * pixelsize; + w2 = width2 * pixelsize; + + for (line = first_min; line <= last_max; line++) { + for (t=1; t <= nt; t++) { + if (first_line[t] == -1) { + continue; + } + off = (t-1) * w1; + if (t == nt) { + len = w2; /* possible short tile */ + } else { + len = w1; + } +/* here VCR_FB must be done before the memcpy otherwise there'd be no diffs */ +VCR_FB(s_dst + off, s_src + off); + memcpy(s_dst + off, s_src + off, len); + memcpy_copy += len; + } s_src += tile_row[nt]->bytes_per_line; s_dst += main_bytes_per_line; } + } +#endif /* VCR_HACK */ +END(memcpy_copy_time) /* record all the info in the region array for this tile: */ for (t=1; t <= nt; t++) { @@ -12248,7 +12733,23 @@ static int copy_tiles(int tx, int ty, int nt) { tile_copied[n+s] = 1; } +#ifdef VCR_HACK + if (vcopyrect) { + int k, ps = pixelsize; + if (bpp == 32) { + for (t=1; t <= nt; t++) { + for (k=0; k < vcr_depth; k++) { + memcpy(fbd_old32[tx+(t-1)][k] + y, + fbt_old32[(t-1)*tile_x + k], tile_y * ps); + memcpy(fbd_new32[tx+(t-1)][k] + y, + fbt_new32[(t-1)*tile_x + k], tile_y * ps); + } + } + } /* XXX */ + } +#endif +END2(copy_tile_time) return(1); } @@ -12364,6 +12865,9 @@ static int copy_all_tile_runs(void) { } } } + if (ntcnt) { +// fprintf(stderr, " ntave: %.1f\n", ((double) ntave)/ntcnt); + } return diffs; } @@ -12396,6 +12900,8 @@ static int copy_tiles_backward_pass(void) { tile_has_diff[m] = 2; ct = copy_tiles(x, y-1, 1); if (ct < 0) return ct; /* fatal */ + } else { + noretry++; /* only for statistics */ } } @@ -12406,6 +12912,8 @@ static int copy_tiles_backward_pass(void) { tile_has_diff[m] = 2; ct = copy_tiles(x-1, y, 1); if (ct < 0) return ct; /* fatal */ + } else { + noretry++; } } } @@ -12461,6 +12969,7 @@ static int gap_try(int x, int y, int *run, int *saw, int along_x) { *saw = 1; return 0; } + gap_tot += *run; for (i=1; i <= *run; i++) { /* iterate thru the run. */ if (along_x) { @@ -12473,11 +12982,16 @@ static int gap_try(int x, int y, int *run, int *saw, int along_x) { m = xt + yt * ntiles_x; if (tile_tried[m]) { /* do not repeat tiles */ + gap_tot--; + noretry++; continue; } ct = copy_tiles(xt, yt, 1); if (ct < 0) return ct; /* fatal */ + if (tile_has_diff[m]) { + gap_got++; + } } *run = 0; *saw = 1; @@ -12540,6 +13054,7 @@ static int island_try(int x, int y, int u, int v, int *run) { /* found a discontinuity */ if (tile_tried[m]) { + noretry++; return 0; } else if (*run < grow_fill) { return 0; @@ -12547,6 +13062,10 @@ static int island_try(int x, int y, int u, int v, int *run) { ct = copy_tiles(u, v, 1); if (ct < 0) return ct; /* fatal */ + isl_tot++; + if (tile_has_diff[m]) { + isl_got++; + } } return 1; } @@ -12686,10 +13205,84 @@ int copy_snap(void) { rfbLog("copy_snap: time for -snapfb snapshot: %.3f sec\n", dt); first = 0; } + fprintf(stderr, "copy_snap: %.3f sec\n", dt); return 0; } +/* profiling routines */ +static void do_stats(void) { + static double t = 0; + if (t == 0) { + dtime(&t); + } +#ifdef VCR_HACK + if (vcr_cnt && scan_count == NSCAN - 1) { + // fprintf(stderr, "vcr_cnt: %d\n", vcr_cnt); + vcr_cnt = 0; + } +#endif + if ( scan_count == 0 ) { + hints_used = 0; + gap_got = 0; gap_tot = 0; + isl_got = 0; isl_tot = 0; + noretry = 0; + } else if ( debug && scan_count == NSCAN - 1 ) { + int ave = (int) (1000 * (scan_ave/scan_cnt)); + int ave2 = (int) (1000 * (scan_ave2/scan_cnt2)); + scan_ave = 0; scan_cnt = 0; + scan_ave2 = 0; scan_cnt2 = 0; + fprintf(stderr, "All pix %.2fs (s4u aves: all: %dms scan: " + "%dms). tiles: %4d hints:%3d retry:%3d gaps:%3d/%-3d " + "isl:%3d/%-3d nap_ok=%d\n", + dtime(&t), ave, ave2, nap_diff_count, hints_used, noretry, + gap_got, gap_tot - gap_got, isl_got, isl_tot - isl_got, + nap_ok); + wrote_copy_tile_header = 0; + } +} + +/* + * debugging: print out a picture of the tiles. + */ +static void print_tiles(void) { + /* hack for viewing tile diffs on the screen. */ + static char *prev = NULL; + int n, x, y, ms = 1500; + + ms = 1; + + if (! prev) { + prev = (char *) malloc((size_t) (ntiles * sizeof(char))); + for (n=0; n < ntiles; n++) { + prev[n] = 0; + } + } + fprintf(stderr, " "); + for (x=0; x < ntiles_x; x++) { + fprintf(stderr, "%1d", x % 10); + } + fprintf(stderr, "\n"); + n = 0; + for (y=0; y < ntiles_y; y++) { + fprintf(stderr, "%2d ", y); + for (x=0; x < ntiles_x; x++) { + if (tile_has_diff[n]) { + fprintf(stderr, "X"); + } else if (prev[n]) { + fprintf(stderr, "o"); + } else { + fprintf(stderr, "."); + } + n++; + } + fprintf(stderr, "\n"); + } + for (n=0; n < ntiles; n++) { + prev[n] = tile_has_diff[n]; + } + usleep(ms * 1000); +} /* * Utilities for managing the "naps" to cut down on amount of polling. @@ -12880,6 +13473,9 @@ static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src, x1 = tile_blackout[n].bo[b].x1 - x; x2 = tile_blackout[n].bo[b].x2 - x; +//fprintf(stderr, "cmpskip[%d]: n=%3d X=%3d Y=%3d y=%d-%d w=%2d ps=%2d " +// "beg=%2d end=%2d x=%d-%d\n", b, n, x, y, y1, y2, w, pixelsize, +// beg, end, x1, x2); if (y1 > y || y >= y2) { continue; } @@ -12932,25 +13528,33 @@ static int scan_display(int ystart, int rescan) { /* grab the horizontal scanline from the display: */ X_LOCK; +BEG XRANDR_SET_TRAP_RET(-1, "scan_display-set"); copy_image(scanline, 0, y, 0, 0); XRANDR_CHK_TRAP_RET(-1, "scan_display-chk"); +xget_scan += main_bytes_per_line; +END(xshmget_scan_time) X_UNLOCK; /* for better memory i/o try the whole line at once */ src = scanline->data; dst = main_fb + y * main_bytes_per_line; +BEG if (whole_line && ! memcmp(dst, src, main_bytes_per_line)) { /* no changes anywhere in scan line */ + memcmp_scan += main_bytes_per_line; nodiffs = 1; if (! rescan) { +END(memcmp_scan_time) y += NSCAN; continue; } } +END(memcmp_scan_time) x = 0; +BEG while (x < dpy_x) { n = (x/tile_x) + (y/tile_y) * ntiles_x; @@ -12995,14 +13599,34 @@ static int scan_display(int ystart, int rescan) { tile_count++; } } + } else { + memcmp_scan += w * pixelsize; } x += NSCAN; } y += NSCAN; +END(memcmp_scan_time) } return tile_count; } +#if 0 +void check_key_down(void) { + double t = 0.0; + if (last_keyboard_input < 0) { + rfbCFD(screen, 1000); + if (last_keyboard_input) { + fprintf(stderr, "key *SWITCH*\n"); + } + } + dtime(&t); + if (last_keyboard_input < 0) { + fprintf(stderr, "key DOWN: %3d %12.6f\n", -last_keyboard_input, t - 1089481179.0); + } else { + fprintf(stderr, "key up: %3d %12.6f\n", +last_keyboard_input, t - 1089481179.0); + } +} +#endif /* * toplevel for the scanning, rescanning, and applying the heuristics. @@ -13014,6 +13638,32 @@ int scan_for_updates(int count_only) { double frac1 = 0.1; /* tweak parameter to try a 2nd scan_display() */ double frac2 = 0.35; /* or 3rd */ double frac3 = 0.02; /* do scan_display() again after copy_tiles() */ + double t = 0, t2 = 0, dt2; + double d1, d2, d3, d4, d = 0; + int full = 0; + int gap_g, gap_t, isl_g, isl_t, gap_n, isl_n, tdiff; + + memcmp_scan = 0; memcmp_copy = 0; memcpy_copy = 0; + xget_scan = 0; xget_copy = 0; + memcpy_copy_time = 0; + memcmp_copy_time = 0; + memcmp_scan_time = 0; + xshmget_copy_time = 0; + xshmget_scan_time = 0; + copy_tile_time = 0; + scale_time = 0; + scale_data = 0; + dtime(&t2); + dtime(&d); + + do_stats(); + if (debug) { + dtime(&t); + } + if (debug && scan_count == 0) { + fflush(stderr); + fflush(stdout); + } for (i=0; i < ntiles; i++) { tile_has_diff[i] = 0; tile_tried[i] = 0; @@ -13061,6 +13711,7 @@ int scan_for_updates(int count_only) { nap_set(tile_count); + dt2 = dtime(&t2); if (fs_factor && frac1 >= fs_frac) { /* make frac1 < fs_frac if fullscreen updates are enabled */ frac1 = fs_frac/2.0; @@ -13082,11 +13733,13 @@ int scan_for_updates(int count_only) { tile_count = scan_display(scanlines[cp], 1); SCAN_FATAL(tile_count); + dt2 += dtime(&t2); 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); + dt2 += dtime(&t2); } } scan_in_progress = 0; @@ -13114,11 +13767,16 @@ int scan_for_updates(int count_only) { if (use_threads && pointer_mode != 1) { pointer(-1, 0, 0, NULL); } + /* go finish the stats collecting, etc */ + full = 1; + tile_diffs = tile_count; + goto finish; nap_check(tile_count); return tile_count; } } scan_in_progress = 0; + dtime(&d); /* copy all tiles with differences from display to rfb framebuffer: */ fb_copy_in_progress = 1; @@ -13141,6 +13799,7 @@ int scan_for_updates(int count_only) { } else { tile_diffs = copy_all_tile_runs(); } + d1 = dtime(&d); SCAN_FATAL(tile_diffs); /* @@ -13165,18 +13824,43 @@ int scan_for_updates(int count_only) { tile_diffs = copy_tiles_additional_pass(); SCAN_FATAL(tile_diffs); } + d2 = dtime(&d); + isl_n = noretry; + isl_g = isl_got; + isl_t = isl_tot; /* Given enough tile diffs, try the islands: */ if (grow_fill && tile_diffs > 4) { tile_diffs = grow_islands(); } SCAN_FATAL(tile_diffs); + d3 = dtime(&d); + + isl_n = noretry - isl_n; + isl_g = isl_got - isl_g; + isl_t = isl_tot - isl_t; + + gap_n = noretry; + gap_g = gap_got; + gap_t = gap_tot; /* Given enough tile diffs, try the gaps: */ if (gaps_fill && tile_diffs > 4) { tile_diffs = fill_tile_gaps(); } SCAN_FATAL(tile_diffs); + d4 = dtime(&d); + + gap_n = noretry - gap_n; + gap_g = gap_got - gap_g; + gap_t = gap_tot - gap_t; + + if (0) print_tiles(); +#ifdef VCR_HACK + if (vcopyrect) { + do_vcr(); + } +#endif fb_copy_in_progress = 0; if (use_threads && pointer_mode != 1) { @@ -13207,6 +13891,138 @@ int scan_for_updates(int count_only) { ping_clients(tile_diffs); } +finish: + + tdiff = 0; + for (i=0; i < ntiles; i++) { + if (tile_has_diff[i]) { + tdiff++; + } + } + if (debug) { + double dt = dtime(&t); + + hints_used += hint_cnt; + scan_ave += dt; + scan_cnt++; + scan_ave2 += dt2; + scan_cnt2++; + + if (tdiff >= 4) { + double rat = dt / (tdiff ? tdiff : 1); + double libvnc = (double) libvnc_time / BEG_FAC; + + if (full) { + double rat2 = dt / ntiles; + double rat3 = dpy_x * dpy_y * (bpp/8) / (dt - dt2); + + + rat3 /= 1000000; + rat3 *= 2; /* both XShmGetImage and memcpy to fb */ + fprintf(stderr, "fullscreen: %6.3f %4d %4d %4.1f ms " + "-- (%.3f) T/ntiles %.2f ms rate: %5.1f MB/s " + "vnc: %.3f/%d\n", + dt, tile_count, tdiff, 1000*rat, dt2, 1000*rat2, + rat3, libvnc, libvnc_count); + } else { + /* + * dt = full time + * dt2 = scan_display (sum for both if applicable) + * d1 = copy_all_tiles + * d2 = copy_tiles_backward_pass + * d3 = grow_islands + * d4 = fill_tile_gaps + * memcmp_scan number of bytes; memcmp_scan_time ns + * memcpy_copy number of bytes; memcpy_copy_time ns + * memcmp_copy number of bytes; memcmp_copy_time ns + */ + double mrat1 = (memcmp_scan) / (dt2); + double mrat2 = (memcpy_copy) / (d1 + d2 + d3 + d4); + double mrat3 = (memcmp_copy) / (d1 + d2 + d3 + d4); + double mrat4, mrat5, mrat6, mrat7; + double time4, time5, time6, time7; + double xrat1, xrat2; + double xshm_scan, xshm_copy; + double scale_tm, scale_amt; + double ct_time; + + /* convert to (overall func. call) MB/sec */ + mrat1 = mrat1 / 1000000; /* scanline memcmp */ + mrat2 = mrat2 / 1000000; /* copytile memcpy */ + mrat3 = mrat3 / 1000000; /* copytile memcmp */ + + /* now work out more accurate rates MB/sec */ + time4 = (double) memcpy_copy_time / BEG_FAC; + mrat4 = memcpy_copy / time4; + mrat4 = mrat4 / 1000000; /* copytile memcpy */ + + time5 = (double) memcmp_copy_time / BEG_FAC; + mrat5 = memcmp_copy / time5; + mrat5 = mrat5 / 1000000; /* copytile memcmp */ + + time6 = (double) memcmp_scan_time / BEG_FAC; + mrat6 = memcmp_scan / time6; + mrat6 = mrat6 / 1000000; /* scanline memcmp */ + + /* total time in copytile() */ + ct_time = (double) copy_tile_time / BEG_FAC; + /* total xshmgetimage copytile */ + xshm_copy = (double) xshmget_copy_time / BEG_FAC; + /* total xshmgetimage scanline */ + xshm_scan = (double) xshmget_scan_time / BEG_FAC; + + /* total scale_and_mark time */ + scale_tm = (double) scale_time / BEG_FAC; + scale_amt = (double) scale_data / 1000000; + time7 = (double) scale_time / BEG_FAC; + if (time7 == 0.0) time7 = 1.0; + mrat7 = scale_data / time7; + mrat7 = mrat7 / 1000000; /* scale_and_mark rate */ + + + xrat1 = xget_scan / (1000000 * xshm_scan); + xrat2 = xget_copy / (1000000 * xshm_copy); + + if (! wrote_copy_tile_header) { + fprintf(stderr, "---------- Time tlcnt tldif " + " T/tld -- (Tscan) - Tcopy Tisl Tgap R " + "Tcp2/cp1 Tisl/cp Tgap/cp Y/N-retry " + "Y/N-retry\n"); + wrote_copy_tile_header = 1; + } + fprintf(stderr, "copy_tile: %6.3f %4d %4d %4.1f ms --" + " (%.3f) - %.3f %.3f %.3f R (%.3f) %.3f %.3f" + " isl=%d/%d-%d\tgap=%d/%d-%d\n " + "# scanline: cmp=%7d rate=%5.1f MB/s mcmp=%.4f" + " | xshm=%.4f st=%.4f xt/st=%.2f xrate_s=%5.1f " + "MB/s\n " + "# copytile: cmp=%7d rate=%5.1f MB/s mcmp=%.4f" + " | vnc: %.4f/%03d scale: %.4f %5.2f MB %5.1f MB/s\n " + " cpy=%7d rate=%5.1f MB/s mcpy=%.4f" + " | xshm=%.4f ct=%.4f xt/ct=%.2f xrate_c=%5.1f " + "MB/s\n", + dt, tile_count, tdiff, 1000*rat, dt2, + d1 + d2, d3, d4, + d2/d1, d3/(d1+d2), d4/(d1+d2), + isl_g, isl_t - isl_g, isl_n, + gap_g, gap_t - gap_g, gap_n, + memcmp_scan, mrat6, time6, + xshm_scan, dt2, xshm_scan/dt2, xrat1, + memcmp_copy, mrat5, time5, + libvnc, libvnc_count, + scale_tm, scale_amt, mrat7, + memcpy_copy, mrat4, time4, + xshm_copy, ct_time, xshm_copy/ct_time, xrat2 + ); + if (0 && isl_g < 5 && isl_t - isl_g > 30 ) { + print_tiles(); + } + } + libvnc_time = 0; + libvnc_count = 0; + } + } + /* end of profile timing junk */ nap_check(tile_diffs); return tile_diffs; @@ -13244,9 +14060,13 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, pid_t parent) { rfbLogEnable(0); if (! client_connect_file) { dpy = XOpenDisplay(x11vnc_xdisplay); + if (! dpy && ! x11vnc_xdisplay) { + x11vnc_xdisplay = strdup(":0"); + dpy = XOpenDisplay(x11vnc_xdisplay); + } if (! dpy) { - fprintf(stderr, "gui: could not open display: %s\n", - x11vnc_xdisplay); + fprintf(stderr, "gui: could not open " + "display: %s\n", NONUL(x11vnc_xdisplay)); exit(1); } scr = DefaultScreen(dpy); @@ -13254,6 +14074,7 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, pid_t parent) { initialize_vnc_connect_prop(); } usleep(1000*1000); + fprintf(stderr, "\n"); for (i=0; i quick_spin_fac * dt) { /* get out if spin time comparable to last scan time */ + if (gb) fprintf(stderr, " SPIN-OUT: %.3f qsf * dt: %.3f", spin, quick_spin_fac * dt); break; } if (got_pointer_input > g) { + if (gb) fprintf(stderr, "-%d", got_pointer_input - g_in); g = got_pointer_input; if (eaten++ < max_eat) { continue; @@ -13533,10 +14359,13 @@ static void check_user_input2(double dt) { miss++; } if (miss > 1) { /* 1 means out on 2nd miss */ + if (gb) fprintf(stderr, " MISS-BRK: %.3f", spin); break; } } + if (gb && eaten >= max_eat) fprintf(stderr, " +MAX_EAT"); + if (gb) fprintf(stderr, "\n"); /* * Probably grinding with a lot of fb I/O if dt is this large. @@ -13572,14 +14401,17 @@ static void check_user_input2(double dt) { g = got_pointer_input; miss = 0; + if (gb) fprintf(stderr, " GRIND ms: %d ", ms); for (i=0; i g) { XFlush(dpy); miss = 0; @@ -13594,6 +14426,8 @@ static void check_user_input2(double dt) { break; } } + if (gb && i == split) fprintf(stderr, " +MAX_GR"); + if (gb) fprintf(stderr, " spin: %.3f\n", spin); } } @@ -13607,13 +14441,16 @@ static void check_user_input3(double dt, double dtr, int tile_diffs) { int gcnt, ginput; static int first = 1; + static int gb = 1; if (first) { char *p = getenv("SPIN"); if (p) { double junk; sscanf(p, "%lf,%lf", &dt_cut, &junk); + gb = 1; } +fprintf(stderr, " dt_cut: %f \n", dt_cut); first = 0; } @@ -13621,6 +14458,7 @@ static void check_user_input3(double dt, double dtr, int tile_diffs) { return; } +if (gb) fprintf(stderr, "\n GOT dt: %.3f gpi: %d\n", dt, got_pointer_input); if (dt < dt_cut) { dt = dt_cut; /* this is to try to avoid early exit */ @@ -13654,6 +14492,7 @@ static void check_user_input3(double dt, double dtr, int tile_diffs) { dtm = dtime(&tm); spin += dtm; +if (gb) fprintf(stderr, " dtm=%.4f", dtm); if (got_pointer_input == g) { if (last_was_miss) { @@ -13671,9 +14510,11 @@ static void check_user_input3(double dt, double dtr, int tile_diffs) { if (spin > spin_max) { /* get out if spin time over limit */ +if (gb) fprintf(stderr, " SPIN-OUT: %.4f", spin); break; } else if (got_pointer_input > g) { +if (gb) fprintf(stderr, ">>%d/dtp=%.4f, ", got_pointer_input - g_in, tm - to); to = tm; /* received some input, flush to display. */ got_input = 1; g = got_pointer_input; @@ -13682,9 +14523,11 @@ static void check_user_input3(double dt, double dtr, int tile_diffs) { X_UNLOCK; } else if (--allowed_misses <= 0) { /* too many misses */ +if (gb) fprintf(stderr, ">>*M\nMISS-BRK: %.4f", spin); break; } else if (consecutive_misses >=3) { /* too many misses */ +if (gb) fprintf(stderr, ">>*M\nMISS-CONS: %.4f", spin); break; } else { /* these are misses */ @@ -13697,9 +14540,12 @@ static void check_user_input3(double dt, double dtr, int tile_diffs) { * will batch them. */ wms = 50; +if (gb) fprintf(stderr, ">>*M-FST, "); } else if (button_mask) { wms = 10; +if (gb) fprintf(stderr, ">>*M, "); } else { +if (gb) fprintf(stderr, ">>*M, "); } if (wms) { usleep(wms * 1000); @@ -13714,6 +14560,7 @@ static void check_user_input3(double dt, double dtr, int tile_diffs) { } } +if (gb) fprintf(stderr, "\n"); drag_in_progress = 0; } @@ -13729,6 +14576,7 @@ int fb_update_sent(int *count) { } rfbReleaseClientIterator(i); if (sent != last_count) { + if (0) fprintf(stderr, "\n***FB_UPDATE_SENT***\n"); rc = 1; } if (count != NULL) { @@ -13740,6 +14588,7 @@ int fb_update_sent(int *count) { static void check_user_input4(double dt, double dtr, int tile_diffs) { +static int gb = 1; int g, g_in, i, ginput, gcnt, tmp; int last_was_miss, consecutive_misses; int min_frame_size = 10; /* 10 tiles */ @@ -13763,7 +14612,9 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) { char *p = getenv("SPIN"); if (p) { sscanf(p, "%lf,%lf,%lf,%lf", &dt_cut, &Tfac_r, &Tfac_v, &Tfac_n); + gb = 1; } + fprintf(stderr, "dt_cut: %f Tfac_r/r: %f/%f Tdelay: %f\n", dt_cut, Tfac_r, Tfac_v, Tdelay); first = 0; ssec = time(0); } @@ -13819,11 +14670,16 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) { } /* damn, they didn't push our frame! */ iter++; + if (gb && iter == 1) fprintf(stderr, "PUSH_FRAME:"); + if (gb) fprintf(stderr, " %d", iter); +// measure_send_rates(1); rfbPE(screen, rfb_wait_ms * 1000); +// measure_send_rates(0); push_spin += dtime(&tp); } if (iter) { + if (gb) fprintf(stderr, "\n"); X_LOCK; XFlush(dpy); X_UNLOCK; @@ -13836,6 +14692,7 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) { * when we first enter we require some pointer input */ if (!got_pointer_input) { + if (gb && dtr > 0.05) fprintf(stderr, "-- dt: %.3f dtr: %.3f\n", dt, dtr); return; } @@ -13864,6 +14721,13 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) { rpe_last = to = tc = tm; /* last time we did rfbPE() */ g = g_in = got_pointer_input; + if (gb) { + fprintf(stderr, "\n GOT dt: %.3f dtr: %.3f gpi: %d dt_min: %.3f" + " dt_max: %.3f TD= %d Ttiletm: %.3f" + " now: %.3f vnccpu_rate: %.2f KB/sec screen_rate: %.2f MB/sec net_rate: %.2f KB/sec Delay: %.4f sec \n", + dt, dtr, got_pointer_input, dt_min, dt_max, + tile_diffs, Ttile * tile_diffs, tm - ssec, vnccpu_rate / 1000, screen_rate / 1000000, net_rate/1000, Tdelay); + } tile_diffs = 0; /* reset our knowlegde of tile_diffs to zero */ while (1) { @@ -13885,7 +14749,9 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) { if ( (gcnt == 1 && got_pointer_input > g) || tm-tc > 2*dt_min) { tile_diffs = scan_for_updates(1); tc = tm; + if (gb) fprintf(stderr, " NewTD%s=%d/%.3f", tile_diffs > 10 ? "*" : "", tile_diffs, spin); } + if (gb) fprintf(stderr, " dtm%s=%.4f", dtm >= 0.05 ? "*" : "", dtm); if (got_pointer_input == g) { if (last_was_miss) { @@ -13900,6 +14766,7 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) { if (tile_diffs > min_frame_size && spin > Ttile * tile_diffs + Tdelay) { /* we think we can push the frame */ + if (gb) fprintf(stderr, "\n ##FRAME-OUT: %.4f Btile: %d del: %.3f", spin, Btile * tile_diffs, tm - ssec); push_frame = 1; fb_update_sent(&update_count); break; @@ -13911,9 +14778,11 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) { X_LOCK; XFlush(dpy); X_UNLOCK; + if (gb) fprintf(stderr, "++%d/dtp=%.4f, ", got_pointer_input - g_in, tm - to); to = tm; } else if (consecutive_misses >= 2) { /* too many misses in a row */ + if (gb) fprintf(stderr, ">>*M\nMISS-CONS: %.4f del: %.3f", spin, tm - ssec); break; } else { @@ -13927,13 +14796,17 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) { * of them for the next read. */ wms = 50; + if (gb) fprintf(stderr, ">>*M-FST, "); } else if (button_mask) { wms = 10; + if (gb) fprintf(stderr, ">>*M-DRG, "); } else { wms = 0; + if (gb) fprintf(stderr, ">>*M, "); } if (wms) { + if (gb) fprintf(stderr, "wms=%d, ", wms); usleep(wms * 1000); } } @@ -13944,6 +14817,7 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) { rfbCFD(screen, rfb_wait_ms * 1000); } } + if (gb) fprintf(stderr, "\n"); drag_in_progress = 0; } @@ -14166,6 +15040,8 @@ void measure_send_rates(int init) { ClientData *cd = (ClientData *) cl->clientData; tmp2 = 0.0; dtime(&tmp2); +fprintf(stderr, "client num rects init=%d: req: %d mod: %d time: %.3f\n", init, + (int) sraRgnCountRects(cl->requestedRegion), (int) sraRgnCountRects(cl->modifiedRegion), tmp2); if (init) { continue; } @@ -14244,6 +15120,7 @@ if (init) { dbr = rbs - cd->set_raw_bytes; cmp_rate = db/dt; raw_rate = dbr/dt; +fprintf(stderr, "RATE: %11.2f RAW: %11.2f dt: %.3f B: %7d R: %7d\n", cmp_rate, raw_rate, dt, db, dbr); if (dbr > min_width * min_width * bpp/8) { cd->sample++; if (cd->sample >= RATE_SAMPLES) { @@ -14266,7 +15143,11 @@ void rfbPE(rfbScreenInfoPtr scr, long usec) { return; } if (! use_threads) { +BEG +measure_send_rates(1); rfbProcessEvents(scr, usec); +measure_send_rates(0); +END(libvnc_time); libvnc_count++; } } @@ -14275,7 +15156,9 @@ void rfbCFD(rfbScreenInfoPtr scr, long usec) { return; } if (! use_threads) { +BEG rfbCheckFds(scr, usec); +END(libvnc_time); libvnc_count++; } } @@ -14285,6 +15168,7 @@ void rfbCFD(rfbScreenInfoPtr scr, long usec) { static void watch_loop(void) { int cnt = 0, tile_diffs = 0; double dt = 0.0, dtr = 0.0; + time_t start = time(0); if (use_threads) { rfbRunEventLoop(screen, -1, TRUE); @@ -14299,7 +15183,9 @@ static void watch_loop(void) { if (! use_threads) { double tm = 0.0; dtime(&tm); +// measure_send_rates(1); rfbPE(screen, -1); +// measure_send_rates(0); dtr = dtime(&tm); fb_update_sent(NULL); @@ -14327,11 +15213,24 @@ static void watch_loop(void) { check_connect_inputs(); check_padded_fb(); + if (first_conn_timeout < 0) { + start = time(0); + first_conn_timeout = -first_conn_timeout; + } if (! screen || ! screen->clientHead) { /* waiting for a client */ + if (first_conn_timeout) { + if (time(0) - start > first_conn_timeout) { + rfbLog("No client after %d secs.\n", + first_conn_timeout); + shut_down = 1; + continue; + } + } usleep(200 * 1000); continue; } + first_conn_timeout = 0; if (nofb) { /* no framebuffer polling needed */ @@ -14362,6 +15261,7 @@ static void watch_loop(void) { double tm = 0.0; dtime(&tm); + RFBUNDRAWCURSOR(screen); if (use_snapfb) { int t, tries = 5; copy_snap(); @@ -14451,6 +15351,12 @@ static void print_help(void) { " 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" +#if 0 +"-nosu Intended for use with -id windowid. Follow the window\n" +" tree down and *irreversibly* disable BackingStore and\n" +" SaveUnders for all windows. The hope is this will show\n" +" the popups and menus in -id mode. Use with caution.\n" +#endif "-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" @@ -14521,6 +15427,8 @@ 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" +"-timeout n Exit unless a client connects within the first n seconds\n" +" of startup.\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" @@ -14799,9 +15707,9 @@ static void print_help(void) { " with -nocursor, and also some values of the \"mode\"\n" " option below.\n" " \n" -" Note that under XFIXES cursors with transparency\n" -" (alpha channel) will not be exactly represented and\n" -" so Overlay may be preferred. See also the -alphacut\n" +" Note that under XFIXES cursors with transparency (alpha\n" +" channel) will not be exactly represented and one may\n" +" find Overlay may be preferable. See also the -alphacut\n" " and -alphafrac options below as fudge factors to try\n" " to improve the situation for cursors with transparency\n" " for a given theme.\n" @@ -14860,14 +15768,14 @@ static void print_help(void) { " black background). Specify this option to remove the\n" " alpha factor. (useful for light colored semi-transparent\n" " cursors).\n" -"-alphablend In XFIXES mode send cursor alpha channel data to\n" -" libvncserver. The blending effect will only be\n" -" visible in -nocursorshape mode or for clients with\n" -" cursorshapeupdates turned off. (However there is a\n" -" hack for 32bpp with depth 24, it uses the extra 8 bits\n" -" to store cursor transparency for use with a hacked\n" -" vncviewer that applies the transparency locally.\n" -" See the FAQ for more info).\n" +"-noalphablend In XFIXES mode do not send cursor alpha channel data\n" +" to libvncserver. The default is to send it. The\n" +" alphablend effect will only be visible in -nocursorshape\n" +" mode or for clients with cursorshapeupdates turned\n" +" off. (However there is a hack for 32bpp with depth 24,\n" +" it uses the extra 8 bits to store cursor transparency\n" +" for use with a hacked vncviewer that applies the\n" +" transparency locally. See the FAQ for more info).\n" "\n" "-nocursorshape Do not use the TightVNC CursorShapeUpdates extension\n" " even if clients support it. See -cursor above.\n" @@ -15131,6 +16039,9 @@ static void print_help(void) { " noshared disable -shared mode.\n" " forever enable -forever mode.\n" " noforever disable -forever mode.\n" +" timeout:n reset -timeout to n, if there are\n" +" currently no clients, exit unless one\n" +" connects in the next n secs.\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" @@ -15214,8 +16125,8 @@ static void print_help(void) { " alphafrac:f set -alphafrac to f.\n" " alpharemove enable -alpharemove mode.\n" " noalpharemove disable -alpharemove mode.\n" -" alphablend enable -alphablend mode.\n" -" noalphablend disable -alphablend mode.\n" +" alphablend disable -noalphablend mode.\n" +" noalphablend enable -noalphablend mode.\n" " cursorshape disable -nocursorshape mode.\n" " nocursorshape enable -nocursorshape mode.\n" " cursorpos disable -nocursorpos mode.\n" @@ -15307,8 +16218,8 @@ static void print_help(void) { " nowaitmapped flashcmap noflashcmap truecolor notruecolor\n" " overlay nooverlay overlay_cursor overlay_yescursor\n" " nooverlay_nocursor nooverlay_cursor nooverlay_yescursor\n" -" overlay_nocursor visual scale viewonly noviewonly\n" -" shared noshared forever noforever once deny lock nodeny\n" +" overlay_nocursor visual scale viewonly noviewonly shared\n" +" noshared forever noforever once timeout deny lock nodeny\n" " unlock connect allowonce allow localhost nolocalhost\n" " accept gone shm noshm flipbyteorder noflipbyteorder\n" " onetile noonetile blackout xinerama noxinerama xrandr\n" @@ -15330,16 +16241,16 @@ static void print_help(void) { " noalwaysshared nevershared noalwaysshared dontdisconnect\n" " nodontdisconnect desktop noremote\n" "\n" -" aro= display vncdisplay desktopname auth rootshift\n" -" scale_str scaled_x scaled_y scale_numer scale_denom\n" -" scale_fac scaling_noblend scaling_nomult4 scaling_pad\n" -" scaling_interpolate inetd safer unsafe passwdfile\n" -" using_shm logfile o rc norc h help V version lastmod\n" -" bg sigpipe threads clients client_count pid ext_xtest\n" -" ext_xkb ext_xshm ext_xinerama ext_overlay ext_xfixes\n" -" ext_xdamage ext_xrandr rootwin num_buttons button_mask\n" -" mouse_x mouse_y bpp depth indexed_color dpy_x dpy_y\n" -" rfbauth passwd\n" +" aro= display vncdisplay desktopname http_url 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 sigpipe threads clients client_count pid\n" +" 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 rfbauth passwd\n" "\n" "-sync By default -remote commands are run asynchronously, that\n" " is, the request is posted and the program immediately\n" @@ -15392,6 +16303,25 @@ static void print_help(void) { "\n" "These options are passed to libvncserver:\n" "\n" +; +//" It also sets -xwarppointer, use -noxwarppointer to undo.\n" + char help2[] = +"but first, here are some experimental ones (your version may not have them):\n" +"\n" +"-d Turn on debugging and stats output.\n" +"-naptile n Cutoff/max number of tile changes per poll to permit\n" +" a nap between polls. Use 0 to disable. Default %d\n" +"-napfac n Factor by which to multiply the wait ms (see -wait)\n" +" to get the nap time. Default: %d times waitms\n" +"-napmax time Maximum time in ms for a nap. Default: %d\n" +#ifdef VCR_HACK +"-vcr Vertical CopyRect. Enable an experimental hack to detect\n" +" vertical displacements (i.e. scrolls) and send them\n" +" using the very efficient CopyRect encoding. UNSTABLE!\n" +#endif +"\n" +"These options are passed to libvncserver:\n" +"\n" ; /* have both our help and rfbUsage to stdout for more(1), etc. */ dup2(1, 2); @@ -15417,6 +16347,9 @@ static void print_help(void) { tile_fuzz, "" ); + if (getenv("X11VNC_STD_HELP") == NULL) { + fprintf(stderr, help2, naptile, napfac, napmax); + } rfbUsage(); exit(1); @@ -15722,6 +16655,9 @@ static void check_rcfile(int argc, char **argv) { exit(1); } } + for (i=0; i < argc2; i++) { +// fprintf(stderr, "argv2[%d] \"%s\"\n", i, argv2[i]); + } } int main(int argc, char* argv[]) { @@ -15816,6 +16752,8 @@ int main(int argc, char* argv[]) { } } else if (!strcmp(arg, "-waitmapped")) { subwin_wait_mapped = 1; + } else if (!strcmp(arg, "-nosu")) { + no_su = 1; } else if (!strcmp(arg, "-flashcmap")) { flash_cmap = 1; } else if (!strcmp(arg, "-notruecolor")) { @@ -15842,6 +16780,9 @@ int main(int argc, char* argv[]) { connect_once = 1; } else if (!strcmp(arg, "-many") || !strcmp(arg, "-forever")) { connect_once = 0; + } else if (!strcmp(arg, "-timeout")) { + CHECK_ARGC + first_conn_timeout = atoi(argv[++i]); } else if (!strcmp(arg, "-inetd")) { inetd = 1; } else if (!strcmp(arg, "-connect")) { @@ -15993,8 +16934,8 @@ int main(int argc, char* argv[]) { alpha_frac = atof(argv[++i]); } else if (!strcmp(arg, "-alpharemove")) { alpha_remove = 1; - } else if (!strcmp(arg, "-alphablend")) { - alpha_blend = 1; + } else if (!strcmp(arg, "-noalphablend")) { + alpha_blend = 0; } else if (!strcmp(arg, "-nocursorshape")) { cursor_shape_updates = 0; } else if (!strcmp(arg, "-cursorpos")) { @@ -16113,7 +17054,22 @@ int main(int argc, char* argv[]) { safe_remote_only = 1; } else if (!strcmp(arg, "-deny_all")) { deny_all = 1; - } else if (!strcmp(arg, "-httpdir")) { + } else if (!strcmp(arg, "-d") || !strcmp(arg, "-debug")) { + debug = 1; + } else if (!strcmp(arg, "-naptile")) { + CHECK_ARGC + naptile = atoi(argv[++i]); + } else if (!strcmp(arg, "-napfac")) { + CHECK_ARGC + napfac = atoi(argv[++i]); + } else if (!strcmp(arg, "-napmax")) { + CHECK_ARGC + napmax = atoi(argv[++i]); +#ifdef VCR_HACK + } else if (!strcmp(arg, "-vcr")) { + vcopyrect = 1; +#endif + } else if (!strcmp(arg, "-httpdir")) { CHECK_ARGC http_dir = strdup(argv[++i]); } else { @@ -16381,6 +17337,7 @@ int main(int argc, char* argv[]) { fprintf(stderr, " viewonly: %d\n", view_only); fprintf(stderr, " shared: %d\n", shared); fprintf(stderr, " conn_once: %d\n", connect_once); + fprintf(stderr, " timeout: %d\n", first_conn_timeout); fprintf(stderr, " inetd: %d\n", inetd); fprintf(stderr, " connect: %s\n", client_connect ? client_connect : "null"); @@ -16409,7 +17366,7 @@ int main(int argc, char* argv[]) { fprintf(stderr, " logfile: %s\n", logfile ? logfile : "null"); fprintf(stderr, " logappend: %d\n", logfile_append); - fprintf(stderr, " rc_file: %s\n", rc_rcfile ? rc_rcfile + fprintf(stderr, " rc_file: \%s\n", rc_rcfile ? rc_rcfile : "null"); fprintf(stderr, " norc: %d\n", rc_norc); fprintf(stderr, " bg: %d\n", bg); @@ -16455,6 +17412,8 @@ int main(int argc, char* argv[]) { fprintf(stderr, " sb: %d\n", screen_blank); fprintf(stderr, " sigpipe: %s\n", sigpipe ? sigpipe : "null"); + fprintf(stderr, " nap t-f-m: %d/%d/%d\n", naptile, napfac, + napmax); fprintf(stderr, " threads: %d\n", use_threads); fprintf(stderr, " fs_frac: %.2f\n", fs_frac); fprintf(stderr, " gaps_fill: %d\n", gaps_fill); @@ -16467,6 +17426,7 @@ int main(int argc, char* argv[]) { fprintf(stderr, " noremote: %d\n", !accept_remote_cmds); fprintf(stderr, " safemode: %d\n", safe_remote_only); fprintf(stderr, " deny_all: %d\n", deny_all); + fprintf(stderr, " debug: %d\n", debug); fprintf(stderr, "\n"); rfbLog("x11vnc version: %s\n", lastmod); } else { @@ -16514,6 +17474,26 @@ int main(int argc, char* argv[]) { dpy = XOpenDisplay(""); } + if (! dpy && ! use_dpy && ! getenv("DISPLAY")) { + int i, s = 4; + rfbLog("\a\n"); + rfbLog("*** XOpenDisplay failed. No -display or DISPLAY.\n"); + rfbLog("*** Trying \":0\" in %d seconds. Press Ctrl-C to" + " abort.\n", s); + rfbLog("*** "); + for (i=1; i<=s; i++) { + fprintf(stderr, "%d ", i); + sleep(1); + } + fprintf(stderr, "\n"); + use_dpy = ":0"; + dpy = XOpenDisplay(use_dpy); + if (dpy) { + rfbLog("*** XOpenDisplay of \":0\" successful.\n"); + } + rfbLog("\n"); + } + if (! dpy) { rfbLog("XOpenDisplay failed (%s)\n", use_dpy ? use_dpy:"null"); exit(1); @@ -16760,6 +17740,11 @@ int main(int argc, char* argv[]) { initialize_signals(); +#ifdef VCR_HACK + if (vcopyrect) { + initialize_vcr(); + } +#endif initialize_speeds(); @@ -16813,3 +17798,1009 @@ int main(int argc, char* argv[]) { #undef argv } +/* -- vcr.c -- */ + +/* ######################################################################## */ +/* experiment to look for vertical copy rect changes */ +#ifdef VCR_HACK + +/* + * vcr_send() + * Takes the copyrect info (currently limited to a single column of + * changed tiles) and schedules the CopyRegion with libvncserver. + */ +static void vcr_send(int x0, int y0, int x1, int y1, int del) { + sraRegionPtr copy_region; + + copy_region = (sraRegionPtr) sraRgnCreateRect(x0, y0, x1, y1); + rfbScheduleCopyRegion(screen, copy_region, 0, del); + sraRgnDestroy(copy_region); +} + +/* + * vcr_flush2() + * Current hack to try to force libvncserver to flush the copyRegion + * changes. It splits up the deferUpdateTime into time slices and + * tries to force the rfbSendUpdateBuf for each connected client. + * + * mode = 0 means just try to flush all the copyRegion changes. + * mode = 1 means try to flush both the copyRegion and modifiedRegion changes. + */ +static void vcr_flush2(int mode) { + int k1, k2, j, n, nfac = 10; + rfbClientIteratorPtr i; + rfbClientPtr cl; + + fprintf(stderr, "vcr_flush2 "); + + /* break up the time into ~nfac slices. */ + n = screen->deferUpdateTime / nfac; + + for (j=0; j < nfac + 3; j++) { + /* loop over each time slice */ + + fprintf(stderr, "."); + usleep(n * 1000); + + /* give rfbProcessEvents a shot. */ + rfbPE(screen, 0); + + /* + * loop over connected clients and examine + * their copyRegion/modifiedRegion regions + * if they exist, try to flush them. + */ + i = rfbGetClientIterator(screen); + k1 = 0; + k2 = 0; + while( (cl = rfbClientIteratorNext(i)) ) { + /* check modifiedRegion */ + if (sraRgnEmpty(cl->modifiedRegion)) { + fprintf(stderr, " MOD=empty "); + } else { + fprintf(stderr, " MOD=NOT "); + if (mode == 1) { + rfbSendUpdateBuf(cl); + } + k2++; + } + + /* check copyRegion */ + if (sraRgnEmpty(cl->copyRegion)) { + fprintf(stderr, " CR=empty "); + } else { + fprintf(stderr, " CR=NOT "); + rfbSendUpdateBuf(cl); + k1++; + } + } + rfbReleaseClientIterator(i); + + if (mode == 0) { + if (k1 == 0) { + break; + } + } else if (mode == 1) { + if (k1 == 0 && k2 == 0) { + break; + } + } + } + fprintf(stderr, " k1=%d/k2=%d\n", k1, k2); +} + +/* + * vcr_flush1() + * simple try; didn't work. +static void vcr_flush1(int mode) { + rfbPE(screen, 0); +} + */ + +/* flush wrapper */ +static void vcr_flush(int mode) { + vcr_flush2(mode); +} + +/* + * A structure to describe a discovered vertical translation within + * a pair of old/new vertical scanlines. + */ +typedef struct displacement { + int runlen; /* how long the "best" concidence run was */ + int longruns; /* how many "long" runs we found altogether */ + int disp; /* the verticle displacement of the best run */ + int top; /* the top (smallest y-pixel position) */ + int mid; /* the middle y-pixel position */ + int bot; /* the bottom (highest y-pixel position) */ +} disp_t; + +/* + * A structure for the actual copyrect we find for a bunch of adjacent + * vertical scanlines with the same offest del. + * + * Currently used for a copyrect only within one column of changed + * tiles. libvncserver seems to be pretty good at gluing them + * together horizontally, so we don't bother, but we could use + * this for that too, x1 - x0 would just be bigger. + */ +typedef struct copyrect { + int x0; + int y0; + int x1; + int y1; + int del; +} copyrect_t; + +/* + * vcr_run() + * Given a column (at tile column x) of n_run changed tiles with tile + * column y-values y_run[n_run], try to find the offset with optimal + * pixel coincidence/overlap. + * + * Optimal usually means the set of scanlines with same offset giving + * the longest run of coincidence. But see suggested_disp below... + * + * If suggested_disp != 0, that means that vertical displacement is + * highly preferred, presumably being the results found from vcr_run + * for neighboring columns of changed tiles. suggested_disp is a hint + * from do_vcr(). + * + * The optimal displacement copyrect is for the tile column is returned + * in the copyrect_t cr. + */ +static int vcr_run(int *y_run, int x, int n_run, copyrect_t *cr, + int suggested_disp) { + /* parameter to skip short columns of changed tiles */ + int tile_run_min = 4; + + /* minimum number of coincident y-pixels to be accepted */ + int match_run_min = 3 * tile_y; + + /* variables for the search */ + int x0, y0, yd, del, prev_del; + int run_up, run_dn, run; + int col, n0; + int x_left, x_right; + int y_top, y_bot; + int i, best; + int y_min, y_max, y_mid; + + /* tmp pointers to the old and new vertical scanline fb data */ +// int *fb_old; +// int *fb_new; + char *fb_old8; + char *fb_new8; + short *fb_old16; + short *fb_new16; + int *fb_old32; + int *fb_new32; + + /* we need a disp_t for each vertical scanline in the tile column */ + disp_t verts[VCR_DEPTH]; + + if (suggested_disp) { + fprintf(stderr, "IN vcr_run col=%2d/%3d row=%2d/%3d tn=%2d nr=%2d pref=%3d\n", + x, x * tile_x, y_run[0], y_run[0] * tile_y, y_run[n_run-1], n_run, suggested_disp); + } + + if (! suggested_disp) { + /* get out if the run is too short */ + if (n_run < tile_run_min) { + if (suggested_disp) fprintf(stderr, "OUT vcr_run A\n"); + return 0; + } + } else { + /* be more tolerant of short columns if suggested_disp given */ + if (n_run < tile_run_min/2) { + if (suggested_disp) fprintf(stderr, "OUT vcr_run B\n"); + return 0; + } + match_run_min /= 2; + } + + /* initialize each vert scanline to "nothing found" */ + for (col=0; col < vcr_depth; col++) { + verts[col].runlen = 0; + verts[col].disp = 0; + verts[col].top = -1; + verts[col].mid = -1; + verts[col].bot = -1; + + verts[col].longruns = 0; + } + /* + * best will be the run length (number of coincident pixels) for + * for out "best find" so far. + */ + best = -1; + + /* we start out in the middle of the tile column and work outwards */ + n0 = n_run/2; /* y-tile # in run */ + y0 = y_run[n0] * tile_y; /* pixel position on screen */ + + /* little shift to y-middle of a tile if n_run is not even */ + if (n_run % 2 != 0) { + y0 += tile_y/2; + } + + /* horizontal pixel position of left edge of tile column */ + x0 = x * tile_x; + + /* lowest possible y of the tile column */ + y_min = y_run[0] * tile_y; + /* highest possible y of the tile column */ + y_max = (y_run[n_run-1] + 1) * tile_y; + + fprintf(stderr, " vcr_run: x== %d\n", x); + + /* + * Loop over each vertical scanline, using the vcr_map pattern + * for the order. + */ + for (col=0; col < vcr_depth; col++) { + /* + * Make pointers to offsets in the before/after vertical + * scanline fb data for the desired scanline. + * (Note: the whole scanline need not be up to date, + * but it is up to date for the changed tile column + * considered here) + */ + if (bpp == 32) { + fb_old32 = fbd_old32[x][col]; + fb_new32 = fbd_new32[x][col]; + } else if (bpp == 16) { + fb_old16 = fbd_old16[x][col]; + fb_new16 = fbd_new16[x][col]; + } else if (bpp == 8) { + fb_old8 = fbd_old8[x][col]; + fb_new8 = fbd_new8[x][col]; + } + + /* start at the middle y-value */ + y_mid = y0; + + /* + * check if a previous scanline found a displacment + * with high coincidence. store it in prev_del. + * also use its y_mid value. + */ + prev_del = 0; + for (i = col - 1; i >= 0; i--) { + if (verts[i].runlen > 0) { + y_mid = verts[i].mid; + prev_del = verts[i].disp; + break; + } + } + + /* + * Now loop over all possible displacements between + * the old and new vertical scanlines. + * That is, y_mid + del < y_max and y_mid - del > + * y_min for relative displacement del. + * + * We work out way out +/- from y_mid (trying to avoid + * ambiguities at the top or bottom of the tile column). + */ + + for (del = 1; y_mid+del < y_max || y_mid-del > y_min; del++) { + int i; + for (i=0; i <= 1; i++) { + /* this is just the +/- plus/minux del aspect */ + int d = (1 - 2*i) * del; + + if (prev_del != 0 && d != prev_del) { + /* + * If we have a previous del, we want + * to stick to it to greatly shorten + * the time spent in this loop. + * + * But if we have a suggested_disp we + * let that drop through. + */ + if (d != suggested_disp) { + continue; + } + } + + /* + * Possible speedup: use longruns as a big + * clue the scanlines are basically constant + * (e.g. lies on a solid root background). + */ + if (suggested_disp && d != suggested_disp + && verts[col].longruns > 4) { + continue; + } + + /* + * yd will be the displaced midpoint for the + * new fb fb_new. + * + * y_mid will be the midpoint of the old fb + * fb_old. + */ + yd = y_mid + d; + + if (yd <= y_min || yd >= y_max) { + /* out of range for fb_new */ + continue; + } + + /* + * Loop upwards from these midpoints, but break + * out as soon as there is a pixel difference. + * (i.e. break out at the high-end of the + * coincidence) + */ + if (bpp == 32) { + for (run_up = 0; yd+run_up < y_max + && y_mid+run_up < y_max; run_up++) { + if ( *(fb_old32 + (y_mid+run_up)) + != *(fb_new32 + (yd+run_up))) { + break; + } + } + } + /* + * Loop downwards from these midpoints, but break + * out as soon as there is a pixel difference. + * (i.e. break out at the low-end of the + * coincidence) + * + * Note: for 16bpp we really check two pixels + * at a time, for 8bpp we check 4 at a time. + */ + if (bpp == 32) { + for (run_dn =-1; yd+run_dn > y_min + && y_mid+run_dn > y_min; run_dn--) { + if ( *(fb_old32 + (y_mid+run_dn)) + != *(fb_new32 + (yd+run_dn))) { + break; + } + } + } + + /* compute the total number of coincident pixels */ + run = run_up - run_dn - 1; + + if (run > match_run_min) { + /* + * It is long enough, add to the tally + * of long runs. + */ + verts[col].longruns++; + + /* + * Now check if it is the longest one + * so far or matches the suggested_disp. + * + * TODO: what if d == suggested_disp is + * overridden later? + */ + if (run > verts[col].runlen + || d == suggested_disp) { + if (run > best) { + /* best for any scanline */ + best = run; + } + + /* record the best for this scanline */ + verts[col].runlen = run; + verts[col].disp = d; + verts[col].top = y_mid + run_dn; + verts[col].mid = y_mid; + verts[col].bot = y_mid + run_up; + } + } + } + } + } + + if (best < 0) { + /* did not find anything. */ + fprintf(stderr, "vcr_run return 0, no best\n"); + return 0; + } + + /* + * Now, not all of the coincidences in the vertical scanlines + * may be the same. The may start and stop at different y-values. + * + * And (we hope not, but) a single displacement may not apply + * to all of the vertical scanlines. + * + * So we loop over the scanlines and try to find + * the minimal/optimal set that applies to most of them. + * + * The result will be the basis of the copyrect rectangle + * we send to libvncserver. + */ + + x_left = -1; /* furthest left scanline x position */ + x_right = -1; /* furthest right scanline x position */ + y_top = -1; /* y-bound from the top (small y) */ + y_bot = -1; /* y-bound from the bottom (large y) */ + del = 0; /* the vertical displacement found */ + + for (col=0; col < vcr_depth; col++) { + if (verts[col].runlen < 1) { + /* no concidence found for this scan line */ + continue; + } + if (del == 0) { + /* use the first one... */ + del = verts[col].disp; + } else if (del != verts[col].disp) { + /* + * skip if not equal to first one + * XXX improve + */ + continue; + } + if (y_top < 0 || verts[col].top > y_top) { + /* trim y_top to include this scanline */ + y_top = verts[col].top; + } + if (y_bot < 0 || verts[col].bot < y_bot) { + /* trim y_bot to include this scanline */ + y_bot = verts[col].bot; + } + if (x_left < 0 || vcr_map[col] < x_left ) { + /* hmm, correct?... */ + x_left = vcr_map[col]; + } + if (x_right < 0 || vcr_map[col] > x_right ) { + /* hmm, correct?... */ + x_right = vcr_map[col]; + } + + fprintf(stderr, " vcr_run: m_match: del=%4d run=%3d ymin=%3d ymid=%3d ymax=%3d lruns=%3d col=%d\n", + verts[col].disp, verts[col].runlen, verts[col].top, verts[col].mid, verts[col].bot, verts[col].longruns, vcr_map[col]); + } + + if (del == 0 || x_left == -1 || x_right == -1 || y_top == -1 || y_bot == -1) { + /* not everything agreed/made sense */ + return 0; + } + + if (x_left == x_right || y_top == y_bot) { + /* only one scanline or no y span (line/point not a rectangle) */ + return 0; + } + fprintf(stderr, " vcr_run: x_left=%2d x_right=%2d y_top=%3d y_bot=%3d\n", x_left, x_right, y_top, y_bot); + + /* + * Finally, create the copyrect_t. + * XXX these may still have off by 1 pixel errors... + */ + if (del > 0) { + /* new fb is displaced downward from old fb */ + cr->x0 = x0 + x_left; + cr->y0 = y_top + del + 1; + cr->x1 = x0 + x_right + 1; + cr->y1 = y_bot + del; + cr->del = del; + } else { + /* new fb is displaced upward from old fb */ + cr->x0 = x0 + x_left; + cr->y0 = y_top + del + 1; + cr->x1 = x0 + x_right + 1; + cr->y1 = y_bot + del; + cr->del = del; + } + return 1; +} + +#define WMAX 40 /* max number of horizontal tiles */ +#define YMAX 40 /* max number of vertical tiles */ + +/* + * char fbd_old[ntile_x][tile_x * (bpp/8)][dpy_y]; + */ +void initialize_vcr(void) { + int i, j; + + if (bpp == 24) { + rfbLog("initialize_vcr: disabling -vcr in 24bpp mode\n"); + vcopyrect = 0; + return; + } + if (dpy_x % tile_x != 0) { + rfbLog("initialize_vcr: disabling -vcr: display width is not " + "a multiple of %d\n", tile_x); + vcopyrect = 0; + return; + } + if (dpy_y % tile_y != 0) { + rfbLog("initialize_vcr: disabling -vcr: display height is not " + "a multiple of %d\n", tile_y); + vcopyrect = 0; + return; + } + + if (bpp == 8) { + fbd_old8 = (char ***) malloc(ntiles_x * sizeof(char **)); + fbd_new8 = (char ***) malloc(ntiles_x * sizeof(char **)); + + for (i=0; i < ntiles_x; i++) { + fbd_old8[i] = (char **) malloc(tile_x*sizeof(char *)); + fbd_new8[i] = (char **) malloc(tile_x*sizeof(char *)); + + for (j=0; j < tile_x; j++) { + size_t n = (size_t) dpy_y * sizeof(char *); + fbd_old8[i][j] = (char *) malloc(n); + fbd_new8[i][j] = (char *) malloc(n); + } + } + + /* temp array for one tile row */ + fbt_old8 = (char **) malloc(dpy_x * sizeof(char *)); + fbt_new8 = (char **) malloc(dpy_x * sizeof(char *)); + for (i=0; i < dpy_x; i++) { + size_t n = (size_t) tile_y * sizeof(char *); + fbt_old8[i] = (char *) malloc(n); + fbt_new8[i] = (char *) malloc(n); + } + + } else if (bpp == 16) { + fbd_old16 = (short ***) malloc(ntiles_x * sizeof(short **)); + fbd_new16 = (short ***) malloc(ntiles_x * sizeof(short **)); + + for (i=0; i < ntiles_x; i++) { + fbd_old16[i] = (short **)malloc(tile_x*sizeof(short *)); + fbd_new16[i] = (short **)malloc(tile_x*sizeof(short *)); + + for (j=0; j < tile_x; j++) { + size_t n = (size_t) dpy_y * sizeof(short *); + fbd_old16[i][j] = (short *) malloc(n); + fbd_new16[i][j] = (short *) malloc(n); + } + } + + /* temp array for one tile row */ + fbt_old16 = (short **) malloc(dpy_x * sizeof(short *)); + fbt_new16 = (short **) malloc(dpy_x * sizeof(short *)); + for (i=0; i < dpy_x; i++) { + size_t n = (size_t) tile_y * sizeof(short *); + fbt_old16[i] = (short *) malloc(n); + fbt_new16[i] = (short *) malloc(n); + } + + } else if (bpp == 32) { + fbd_old32 = (int ***) malloc(ntiles_x * sizeof(int **)); + fbd_new32 = (int ***) malloc(ntiles_x * sizeof(int **)); + + for (i=0; i < ntiles_x; i++) { + fbd_old32[i] = (int **) malloc(tile_x*sizeof(int *)); + fbd_new32[i] = (int **) malloc(tile_x*sizeof(int *)); + + for (j=0; j < tile_x; j++) { + size_t n = (size_t) dpy_y * sizeof(int *); + fbd_old32[i][j] = (int *) malloc(n); + fbd_new32[i][j] = (int *) malloc(n); + } + } + + /* temp array for one tile row */ + fbt_old32 = (int **) malloc(dpy_x * sizeof(int *)); + fbt_new32 = (int **) malloc(dpy_x * sizeof(int *)); + for (i=0; i < dpy_x; i++) { + size_t n = (size_t) tile_y * sizeof(int *); + fbt_old32[i] = (int *) malloc(n); + fbt_new32[i] = (int *) malloc(n); + } + + } else { + vcopyrect = 0; + } + if (getenv("VCR_DEPTH")) { + vcr_depth = atoi(getenv("VCR_DEPTH")); + fprintf(stderr, "reset vcr_depth to: %d\n", vcr_depth); + } else { + fprintf(stderr, "vcr_depth is: %d\n", vcr_depth); + } +} + +/* + * do_vcr() + * Top level routine for the great vertical copyrect hunt. + * + * It is called shortly after the copy_tiles + * calls finish, so the vertical scan line framebuffer pixel + * data (old and new) is up to date. + * + * It only deals with tiles, looking for vertical runs of + * changed (or tried) ones. It passes off the actual + * fb pixel coincidence checking to vcr_run() and records + * wnat vcr_run finds. + * + * Finally, it determines the copyrects that need to be + * sent to libvncserver, sends them, tries to flush, etc. + */ +void do_vcr(void) { + int x, y, n; + int dx, pm, x_cm = 0, y_cm = 0, n_cm = 0; + int got, in_run = 0, thd = 0, tt = 0; + int cpr_cnt = 0, suggested_disp; + int tile_tot = 0, tile_vcr = 0; + static int vcr_sleep = -1; + + /* + * y_run is an array that holds the tile y indexes + * for a vertical run of adjacent changed tiles. + */ + int y_run[YMAX]; + + /* + * some info for each column, x_has_diff[] says the + * column has a tile diff or tried tile somewhere + * + * disps[] records the displacement found by + * vcr_run for this column (XXX: more than one?) + * + */ + int x_has_diff[WMAX], disps[WMAX]; + + /* we store the found copyrects here: */ + copyrect_t cpr[4*WMAX], cr; + + + /* zero our arrays. */ + for (x=0; x < ntiles_x; x++) { + x_has_diff[x] = 0; + disps[x] = 0; + } + + /* + * Try to find the "center of mass" of the region of changed + * tiles. Makes the most sense if the changed tiles are + * limited to one window (or subregion of a window). Think + * of a scroll. We are trying to find the middle of the + * changed region and then work our way out from there. + * + * The thought is we have a better chance of discovering + * the "true" vertical displacement if we start in the middle + * rather than at the edges. Even if there is clutter + * around the scrolled region, this could still get us into + * the interior of it. + */ + for (x=0; x < ntiles_x; x++) { + for (y=0; y < ntiles_y; y++) { + n = x + y * ntiles_x; + if (tile_has_diff[n] || tile_tried[n]) { + /* + * note that this column has a diff, + * and accumulate the cm stuff. + */ + x_has_diff[x]++; + x_cm += x; + y_cm += y; + n_cm++; + } + } + } + + if (! n_cm) { + /* no changed or tried tiles. */ + return; + } + + /* + * Compute center of mass. These are tile indexes, not + * pixel indexes, btw. + * + * We currently only use x_cm the "center of mass" in the + * horizontal direction. We compute y_cm but don't use it yet + */ + x_cm = x_cm / n_cm; + y_cm = y_cm / n_cm; + + fprintf(stderr, "\ndo_vcr() x_cm: %d y_cm: %d\n", x_cm, y_cm); + + /* work our way outward from x_cm, dx is the displacement */ + for (dx=0; dx <= ntiles_x; dx++) { + + /* we go +/- (plus or minus) dx */ + for (pm=0; pm <= 1; pm++) { + int x2, dx2, dx2_best; + + if (dx == 0 && pm == 1) { + /* just do dx = 0 once. */ + continue; + } + + /* tile x index: */ + x = x_cm + (1 - 2*pm) * dx; + + if (x < 0 || x >= ntiles_x) { + /* out of range... */ + continue; + } + + /* + * Loop over previously tried columns of tiles looking for + * a preferred y-displacement. This will be used to speed + * up the search (later columns will aim for the same + * vertical translation... keep thinking of a scroll.) + */ + suggested_disp = 0; + dx2_best = -1; + for (x2=0; x2 < ntiles_x; x2++) { + if (! disps[x2]) { + /* + * column with nothing found or + * not yet examined. + */ + continue; + } + + dx2 = x - x2; + if (dx2 < 0) { + dx2 = -dx2; + } + + if (dx2_best == -1 || dx2 < dx2_best) { + suggested_disp = disps[x2]; + dx2_best = dx2; + } + } + + /* + * Now loop down thru the column looking for + * changed/tried tiles that are adjacent (along y). + * A set of these will be called a "run". + */ + got = 0; + in_run = 0; + thd = 0; + tt = 0; + for (y=0; y <= ntiles_y; y++) { + int changed = 0, end = 0; + n = x + y * ntiles_x; + + if (y == ntiles_y) { + end = 1; + } else if (tile_has_diff[n] || tile_tried[n]) { + changed = 1; + tile_tot++; + } + + if (changed) { + got++; + if (tile_has_diff[n]) { + thd++; + } + if (tile_tried[n]) { + tt++; + } + if (! in_run) { + /* start of a run */ + in_run = 1; + } else { + /* continuation of a run */ + in_run++; + } + /* store the y tile number */ + y_run[in_run - 1] = y; + } else { + /* either tile w/o diff or special end case */ + if (! in_run) { + continue; + } + /* this could be the end of a run */ + if (thd <= tt/10) { + /* not enough have real differences */ + ; + } else if (vcr_run(y_run, x, in_run, &cr, + suggested_disp)) { + /* + * otherwise we called vcr_run + * to analyse the run. And it + * found one. Record it. + */ + cpr[cpr_cnt].x0 = cr.x0; + cpr[cpr_cnt].y0 = cr.y0; + cpr[cpr_cnt].x1 = cr.x1; + cpr[cpr_cnt].y1 = cr.y1; + cpr[cpr_cnt].del = cr.del; + cpr_cnt++; + disps[x] = cr.del; + + tile_vcr += (cr.y1 - cr.y0)/tile_y; + } + + if(got > 3) fprintf(stderr, + " do_vcr: run: x: %d pm=%d dx=%d got=%d thd=%d tt=%d in_run=%d\n", + x, pm, dx, got, thd, tt, in_run); + + /* get ready for a new run */ + got = 0; + in_run = 0; + thd = 0; + tt = 0; + } + } + } + } + + if (tile_vcr) fprintf(stderr, " do_vcr: tile_vcr/tile_tot: %d/%d\n", tile_vcr, tile_tot); + + if (cpr_cnt) { + /* we got some copyrects! */ + + int i, j, ndel, del_shift[100], del_cnt[100]; + int willdo = 0, min_cnt = 4; + int x, y, n; + sraRegionPtr tmp_region, mod_region; + + /* + * There there is too much clutter from other tiles w/o + * a vcr, bail out to try to avoid painting errors. + */ + if (tile_vcr < 0.5 * tile_tot) { + fprintf(stderr, "bad tile_vcr SNR.\n"); + return; + } + + /* + * First tally and do some bookkeepping regarding the + * different y-displacements. Hopefully most of the + * time there will be just one displacement. + * + * One has to take some care... if we send libvncserver + * different displacements, it may treat some of them + * as modifiedRegions instead of copyRegions. + */ + ndel = 0; + for (i=0; i= min_cnt) { + /* we know we have least one */ + willdo = 1; + } + got = 1; + } + } + if (! got) { + /* no dup, so set it */ + del_cnt[ndel] = 1; + del_shift[ndel++] = d; + } + } + fprintf(stderr, "\n"); + if (! willdo) { + fprintf(stderr, "no willdo, return\n"); + return; + } + + /* now use willdo to count number of different dels */ + willdo = 0; + for (j=0; j < ndel; j++) { + int d = del_shift[j]; + fprintf(stderr, "del: %d/%d del_cnt: %d\n", + d, ndel, del_cnt[j]); + if (del_cnt[j] >= min_cnt) { + willdo++; + } + } + fprintf(stderr, "willdo number of dels: %d\n", willdo); + + /* + * We noted above that we will do at least one copyrect. + * So we prepare a region of all of the changed/tried + * tiles that we can subtract the copyRegion from, etc. + */ + mod_region = (sraRegionPtr) sraRgnCreate(); + for (x=0; x < ntiles_x; x++) { + for (y=0; y < ntiles_y; y++) { + n = x + y * ntiles_x; + if (! tile_has_diff[n] && ! tile_tried[n]) { + continue; + } + /* XXX optimize this somehow? */ + tmp_region = (sraRegionPtr) + sraRgnCreateRect(x * tile_x, y * tile_x, + (x+1) * tile_x, (y+1) * tile_y); + + sraRgnOr(mod_region, tmp_region); + sraRgnDestroy(tmp_region); + } + } + fprintf(stderr, "\n"); + + /* + * vcr_change_region will be the region corresponding to + * all of our discovered copyrects. + */ + if (vcr_change_region) { + sraRgnDestroy(vcr_change_region); + } + vcr_change_region = (sraRegionPtr) sraRgnCreate(); + + if (vcr_mod_region) { + sraRgnDestroy(vcr_mod_region); + } + vcr_mod_region = NULL; + + /* + * Now we go over the different del's (hopefully just + * one) and send the copyrects with the same del out to + * libvncserver and try to XXX + */ + + for (j=0; j < ndel; j++) { + int d = del_shift[j]; + if (del_cnt[j] < min_cnt) { + /* too short */ + continue; + } + for (i=0; i 1) { + /* if more than 1 delta, must flush each one */ + vcr_flush(0); + } + } + + /* + * tell libvncserver to now flush the modified region + * outside of the copyrect region. + */ + fprintf(stderr, "rfbMarkRegionAsModified\n"); + rfbMarkRegionAsModified(screen, mod_region); + fprintf(stderr, "vcr_flush for mod_region\n"); + + vcr_flush(1); + vcr_mod_region = (sraRegionPtr) sraRgnCreateRgn(mod_region); + + sraRgnDestroy(mod_region); + rfbPE(screen, 0); + + /* hack to sleep */ + if (vcr_sleep < 0 || vcr_sleep == 1) { + if (getenv("VCR_SLEEP") != NULL) { + vcr_sleep = 1; + fprintf(stderr, "sleep...\n"); sleep(atoi(getenv("VCR_SLEEP"))); + } else { + vcr_sleep = 0; + } + } + fprintf(stderr, "done...\n"); + } +} +#endif /* VCR_HACK */ +/* ######################################################################## */ +