diff options
author | runge <runge> | 2005-05-14 04:01:37 +0000 |
---|---|---|
committer | runge <runge> | 2005-05-14 04:01:37 +0000 |
commit | 3a7e70de49823259558694a3f92401a9f966d9da (patch) | |
tree | 66e006d617f50b3c6eddffe228ad87be34ba92d8 /x11vnc | |
parent | 34f714bcdc3e2d7a21a9179357dc93f2561301c8 (diff) | |
download | libtdevnc-3a7e70de49823259558694a3f92401a9f966d9da.tar.gz libtdevnc-3a7e70de49823259558694a3f92401a9f966d9da.zip |
x11vnc: more improvements to -scrollcopyrect and -xkb modes.
Diffstat (limited to 'x11vnc')
-rw-r--r-- | x11vnc/ChangeLog | 9 | ||||
-rw-r--r-- | x11vnc/README | 1166 | ||||
-rwxr-xr-x | x11vnc/tkx11vnc | 86 | ||||
-rw-r--r-- | x11vnc/tkx11vnc.h | 86 | ||||
-rw-r--r-- | x11vnc/x11vnc.1 | 402 | ||||
-rw-r--r-- | x11vnc/x11vnc.c | 5339 |
6 files changed, 5323 insertions, 1765 deletions
diff --git a/x11vnc/ChangeLog b/x11vnc/ChangeLog index 5a28e34..150afb8 100644 --- a/x11vnc/ChangeLog +++ b/x11vnc/ChangeLog @@ -1,3 +1,12 @@ +2005-05-14 Karl Runge <runge@karlrunge.com> + * much more work on "-scrollcopyrect" mode... getting usable. + * remove -pointer_mode 3, shift everyone back down + * -dbg "crash shell" for debugging + * -add_keysyms now the default, periodically clears if needed. + * try to autodetect if -xkb would be a good idea. + * improve keycode guessing for -xkb mode (force ISO_Level3_Shift) + * -remap DEAD, etc. for dead/mute keys remappings. + 2005-05-02 Karl Runge <runge@karlrunge.com> * initial support for using RECORD to detect some types of window scrolls. This is "-scrollcopyrect" mode, use -noscrollcopyrect diff --git a/x11vnc/README b/x11vnc/README index 13a0977..4b420bb 100644 --- a/x11vnc/README +++ b/x11vnc/README @@ -1,5 +1,5 @@ -x11vnc README file Date: Mon May 2 21:41:13 EDT 2005 +x11vnc README file Date: Sat May 14 00:07:44 EDT 2005 The following information is taken from these URLs: @@ -528,8 +528,11 @@ make Here is what is shaping up to be [52]the release notes for 0.7.2.. Note that the [53]X DAMAGE feature will be on by default and so I am interested if that causes any problems. I'd also like to have the new - "wireframe" move/resize stuff on by default as well, let me know of - any issues you find. Thanks! + "wireframe" move/resize stuff, the "wireframe copyrect", and the + "scroll detection copyrect" all on by default as well! The scroll + copyrect is currently the least stable, you can toggle it off via + "-noscr". Let me know of any issues you find for any of these new + features. Thanks! _________________________________________________________________ Some Notes: @@ -1234,9 +1237,10 @@ typedef unsigned int in_addr_t; Debian: (.deb) [155]http://packages.debian.org/x11vnc Slackware: (.tgz) [156]http://www.linuxpackages.net/ Redhat/Fedora: - (.rpm) [157]http://dag.wieers.com/packages/x11vnc/ Solaris: (pkg) - [158]http://www.sunfreeware.com/ wwexptools: (.tgz) - [159]http://www.bell-labs.com/project/wwexptools/packages.html The + (.rpm) [157]http://dag.wieers.com/packages/x11vnc/ SuSE: (.rpm) + [158]http://linux01.gwdg.de/~pbleser/ Solaris: (pkg) + [159]http://www.sunfreeware.com/ wwexptools: (.tgz) + [160]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 @@ -1248,13 +1252,13 @@ 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 [160]build notes above for where to download libz and libjpeg, and + the [161]build notes above for where to download libz and libjpeg, and then build everything with gcc. For Solaris, the http://www.sunfreeware.com/ packages are built with libz and libjpeg. 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 - [161]test binaries. Some may be old, some may have extra debugging + [162]test binaries. Some may be old, some may have extra debugging output, etc. One of them may work on your OS... As a general note, the x11vnc program is simple enough you don't @@ -1275,9 +1279,9 @@ typedef unsigned int in_addr_t; To obtain VNC viewers for the viewing side (Windows, Mac OS, or Unix) try here: - * [162]http://www.tightvnc.com/download.html - * [163]http://www.realvnc.com/download-free.html - * [164]http://sourceforge.net/projects/cotvnc/ + * [163]http://www.tightvnc.com/download.html + * [164]http://www.realvnc.com/download-free.html + * [165]http://sourceforge.net/projects/cotvnc/ Q-7: How can I see all of x11vnc's command line options and @@ -1285,7 +1289,7 @@ typedef unsigned int in_addr_t; Run: x11vnc -opts to list just the option names or run: x11vnc -help for long descriptions about each option. The output is listed - [165]here as well. + [166]here as well. Q-8: I don't like typing arcane command line options every time I @@ -1318,7 +1322,7 @@ display :0 GUI based on the remote-control functionality that was added. It's not particularly user-friendly, it just provides a point and click mode to set all the many x11vnc parameters and obtain help on them. See the - [166]-gui option for more info. Examples: "x11vnc ... -gui" and + [167]-gui option for more info. Examples: "x11vnc ... -gui" and "x11vnc ... -gui other:0" in the latter case the gui is displayed on other:0, not the X display x11vnc is polling. @@ -1326,11 +1330,11 @@ display :0 Q-9: Can I make x11vnc more quiet and also go into the background after starting up? - Use the [167]-q and [168]-bg options, respectively. (also: -quiet is + Use the [168]-q and [169]-bg options, respectively. (also: -quiet is an alias for -q) Note that under -bg the stderr messages will be lost unless you use - the "[169]-o logfile" option. + the "[170]-o logfile" option. Q-10: Sometimes when a VNC viewer dies abruptly, x11vnc also dies with @@ -1343,7 +1347,7 @@ display :0 Up until of Apr/2004 the above fix only works for BSD signal systems (Linux, FreeBSD, ...) For SYSV systems there is a workaround in my - [170]x11vnc.c file. It also has an [171]option -sigpipe exit to have + [171]x11vnc.c file. It also has an [172]option -sigpipe exit to have x11vnc clean up and exit upon receiving SIGPIPE. @@ -1352,7 +1356,7 @@ display :0 As of Mar/2004 in the libvncserver cvs there are a few such options. They are enabled by adding something like -Dxxxx=1 to the CPPFLAGS - environment variable before running configure (see the [172]build + environment variable before running configure (see the [173]build notes for general background). * -DX11VNC_SHARED=1 make -shared the default. * -DX11VNC_FOREVER=1 make -forever the default. @@ -1399,21 +1403,21 @@ display :0 dual-screen mode to pass the keystrokes and mouse motions to the X11 display? - Yes, for best response start up x11vnc with the "[173]-nofb" option + Yes, for best response start up x11vnc with the "[174]-nofb" option (disables framebuffer polling, and does other optimizations) on the secondary display (X11) machine. Then start up Win2VNC on the primary display (Windows) referring it to the secondary display. - This will also work X11 to X11 using [174]x2vnc, however you would + This will also work X11 to X11 using [175]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: - * [175]Original Win2VNC - * [176]Enhanced Win2VNC and [177]sourceforge link - * [178]x2vnc - * [179]x2x also [180]here - * [181]zvnc (MorphOS) + * [176]Original Win2VNC + * [177]Enhanced Win2VNC and [178]sourceforge link + * [179]x2vnc + * [180]x2x also [181]here + * [182]zvnc (MorphOS) All of them will work with x11vnc (except x2x where it is not needed). @@ -1433,7 +1437,7 @@ display :0 on your display to be depth 24 TrueColor? Sun machines often have 8+24 overlay/multi-depth visuals, and you can make the default visual depth 24 TrueColor (see fbconfig(1) and Xsun(1)). 2) As of Feb/2004, in the - libvncserver CVS, x11vnc has the [182]-visual option to allow you to + libvncserver CVS, x11vnc has the [183]-visual option to allow you to force the framebuffer visual to whatever you want (this usually messes up the colors unless you are very clever). In this case, the option provides a convenient workaround for the Win2VNC bug: @@ -1448,7 +1452,7 @@ display :0 PseudoColor (i.e. only 256 distinct colors). The x11vnc colors may start out OK, but after a while they are incorrect in certain windows. - Use the [183]-flashcmap option to have x11vnc watch for changes in the + Use the [184]-flashcmap option to have x11vnc watch for changes in the colormap, and propagate those changes back to connected clients. This can be slow (since the whole screen must be updated over the network whenever the colormap changes). This flashing colormap behavior often @@ -1457,7 +1461,7 @@ display :0 example of this. Consider reconfiguring the system to 16 bpp or depth 24 TrueColor if at all possible. - Also note that in some rare cases the [184]-notruecolor option has + Also note that in some rare cases the [185]-notruecolor option has corrected colors on 8bpp displays. The red, green, and blue masks were non-zero in 8bpp PseudoColor on an obscure setup, and this option corrected the problems. @@ -1468,10 +1472,10 @@ display :0 different color depths: e.g. there are both depth 8 and 24 visuals available at the same time. - You may want to review the [185]previous question regarding 8 bpp + You may want to review the [186]previous question regarding 8 bpp PseudoColor. - On some hardware (Sun/SPARC, Sgi), the [186]-overlay option discussed + On some hardware (Sun/SPARC, Sgi), the [187]-overlay option discussed a couple paragraphs down may solve this for you (you may want to skip to it directly). @@ -1509,7 +1513,7 @@ TrueColor defdepth 24 The -overlay mode: Another option is if the system with overlay visuals is a Sun system running Solaris or Sgi running IRIX you can - use the [187]-overlay x11vnc option (Aug/2004) to have x11vnc use the + use the [188]-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 @@ -1531,7 +1535,7 @@ TrueColor defdepth 24 Colors still not working correctly? Run xwininfo on the application with the messed up colors to verify that the depth of its visual is different from the default visual depth (gotten from xdpyinfo). One - possible workaround in this case is to use the [188]-id option to + possible workaround in this case is to use the [189]-id option to point x11vnc at the application window itself. If the application is complicated (lots of toplevel windows and popup menus) this may not be acceptable, and may even crash x11vnc (but not the application). @@ -1539,7 +1543,7 @@ TrueColor defdepth 24 It is theoretically possible to solve this problem in general (see xwd(1) for example), but it does not seem trivial or sufficiently fast for x11vnc to be able to do so in real time. Fortunately the - [189]-overlay option works for Solaris machines with overlay visuals + [190]-overlay option works for Solaris machines with overlay visuals where most of this problem occurs. @@ -1550,13 +1554,13 @@ TrueColor defdepth 24 the desired application window. After clicking, it will print out much information, including the window id (e.g. 0x6000010). Also, the visual and depth of the window printed out is often useful in - debugging x11vnc [190]problems. + debugging x11vnc [191]problems. When using -id windowid, note that some VNC viewers will have problems rendering screens that have a width that is not a multiple of 4. Try to manually adjust the window width before starting x11vnc -id .... - Also, as of Dec/2004 libvncserver CVS you can use "[191]-id pick" to + Also, as of Dec/2004 libvncserver CVS you can use "[192]-id pick" to have x11vnc run xwininfo(1) for you and after you click the window it extracts the windowid. Besides "pick" there is also "id:root" to allow you to go back to root window when doing remote-control. @@ -1574,7 +1578,7 @@ TrueColor defdepth 24 be able to see these transient windows. If things are not working and you still want to do the single window - polling, try the [192]-sid windowid option ("shifted" windowid). + polling, try the [193]-sid windowid option ("shifted" windowid). x11vnc is known to crash under both -id and -sid, so both modes are still experimental. Please report any reproducible bugs. @@ -1627,15 +1631,15 @@ TrueColor defdepth 24 since you will be polling the X display over the network as opposed to over the local hardware. To do this, run x11vnc on a UNIX machine as close as possible network-wise (e.g. same switch) to the Xterminal - machine. Use the [193]-display option to point the display to that of + machine. Use the [194]-display option to point the display to that of the Xterminal (you'll of course need basic X11 permission to do that) - and also supply the [194]-noshm option (this enables the polling over + and also supply the [195]-noshm option (this enables the polling over the network). The response will likely be sluggish (maybe only one "frame" per second). This mode is not recommended except for "quick checks" of hard to get to X servers. Use something like -wait 150 to cut down on - the polling rate. You may also need [195]-flipbyteorder if the colors + the polling rate. You may also need [196]-flipbyteorder if the colors get messed up due to endian byte order differences. Q-20: How do I get my X permissions (MIT-MAGIC-COOKIE) correct for a @@ -1659,7 +1663,7 @@ TrueColor defdepth 24 copied to the Xterminal. If $HOME/.Xauthority is exported via NFS (this is insecure of course, but has been going on for decades), then x11vnc can simply pick it up via NFS (you may need to use the - [196]-auth option to point to the correct file). Other options include + [197]-auth option to point to the correct file). Other options include copying the auth file using scp, or something like: central-server> xauth nextract - xterm123:0 | ssh xterm123 xauth nmerge - @@ -1671,7 +1675,7 @@ TrueColor defdepth 24 details. If the display name in the cookie file needs to be changed between the - two hosts, see [197]this note on the "xauth add ..." command. + two hosts, see [198]this note on the "xauth add ..." command. A less secure option is to run something like "xhost +127.0.0.1" while sitting at the Xterminal box to allow cookie-free local access for @@ -1685,7 +1689,7 @@ TrueColor defdepth 24 occasional app more efficiently locally on the Xterminal box (e.g. realplayer). - Not recommended, but as a last resort, you could have x11vnc [198]poll + Not recommended, but as a last resort, you could have x11vnc [199]poll the Xterminal Display over the network. For this you would run a "x11vnc -noshm ..." process on the central-server (and hope the network admin doesn't get angry...) @@ -1716,17 +1720,17 @@ TrueColor defdepth 24 As of Dec/2004 in the libvncserver CVS there is a remote control feature. It can change a huge amount of things on the fly: see the - [199]-remote and [200]-query options. To shut down the running x11vnc + [200]-remote and [201]-query options. To shut down the running x11vnc server just type "x11vnc -R stop". To disconnect all clients do "x11vnc -R disconnect:all", etc. - For older versions: If the [201]-forever option has not been supplied, + For older versions: If the [202]-forever option has not been supplied, x11vnc will automatically exit after the first client disconnects. In general you will have to kill the x11vnc process This can be done via: "kill NNNNN" (where NNNNN is the x11vnc process id number found from ps(1)), or "pkill x11vnc", or "killall x11vnc" (Linux only). - If you have not put x11vnc in the background via the [202]-bg option + If you have not put x11vnc in the background via the [203]-bg option or shell & operator, then simply press Ctrl-C in the shell where x11vnc is running to stop it. Potential Gotcha: If somehow your Keypress of Ctrl-C went through x11vnc to the Xserver that then @@ -1735,14 +1739,14 @@ TrueColor defdepth 24 Tapping the stuck key (either via a new x11vnc or at the physical console) will release it from the stuck state. If the keyboard seems to be acting strangely it is often fixed by tapping Ctrl, Shift, and - Alt. Alternatively, the [203]-clear_mods option and [204]-clear_keys + Alt. Alternatively, the [204]-clear_mods option and [205]-clear_keys option can be used to release pressed keys at startup and exit. Q-22: Can I change settings in x11vnc without having to restart it? Can I remote control it? - Look at the [205]-remote (same as -R) and [206]-query (same as -Q) + Look at the [206]-remote (same as -R) and [207]-query (same as -Q) options added in the Dec/2004 libvncserver CVS. They allow nearly everything to be changed dynamically and settings to be queried. Examples: "x11vnc -R shared", "x11vnc -R forever", "x11vnc -R @@ -1754,7 +1758,7 @@ TrueColor defdepth 24 be possible. There is also a simple tcl/tk gui based on this remote control - mechanism. See the [207]-gui option for more info. + mechanism. See the [208]-gui option for more info. [Security and Permissions] @@ -1766,12 +1770,12 @@ TrueColor defdepth 24 vncpasswd(1) program from those packages. The libvncserver package also comes with a simple program: storepasswd in the examples directory. And as of Jun/2004 in the libvncserver CVS x11vnc supports - the -storepasswd "pass" "file" [208]option, which is the the same + the -storepasswd "pass" "file" [209]option, which is the the same functionality of storepasswd. Be sure to quote the "pass" if it contains shell meta characters, spaces, etc. Example: x11vnc -storepasswd 'sword*fish' $HOME/myvncpasswd - You then use the password via the x11vnc option: [209]-rfbauth + You then use the password via the x11vnc option: [210]-rfbauth $HOME/myvncpasswd Compared to vncpasswd(1) the latter two methods are a somewhat unsafe @@ -1780,7 +1784,7 @@ TrueColor defdepth 24 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 [210]-passwdfile and -passwd/-viewpasswd plain + x11vnc also has the [211]-passwdfile and -passwd/-viewpasswd plain text (i.e. not obscured like the -rfbauth VNC passwords) password options. @@ -1789,13 +1793,13 @@ TrueColor defdepth 24 and the other for view-only access to the display? Yes, as of May/2004 in the libvncserver CVS there is the - [211]-viewpasswd option to supply the view-only password. Note the - full-access password option [212]-passwd must be supplied at the same + [212]-viewpasswd option to supply the view-only password. Note the + full-access password option [213]-passwd must be supplied at the same time. E.g.: -passwd sword -viewpasswd fish. To avoid specifying the passwords on the command line (where they could be observed via the ps(1) command by any user) you can use the - [213]-passwdfile option to specify a file containing plain text + [214]-passwdfile option to specify a file containing plain text passwords. Presumably this file is readable only by you, and ideally it is located on the machine x11vnc is run on (to avoid being snooped on over the network). The first line of this file is the full-access @@ -1803,7 +1807,7 @@ TrueColor defdepth 24 it is taken as the view-only password. (use "__EMPTY__" to supply an empty one). - View-only passwords currently do not work for the [214]-rfbauth + View-only passwords currently do not work for the [215]-rfbauth password option (standard VNC password storing mechanism). FWIW, note that although the output (usually placed in $HOME/.vnc/passwd) by the vncpasswd or storepasswd programs (or from x11vnc -storepasswd) looks @@ -1817,7 +1821,7 @@ TrueColor defdepth 24 some users just be able to move the mouse, but not click or type anything? - As of Feb/2005, the [215]-input option allows you to do this. "K", + As of Feb/2005, the [216]-input option allows you to do this. "K", "M", and "B" stand for Keystroke, Mouse-motion, and Button-clicks, respectively. The setting: "-input M" makes attached viewers only able to move the mouse. "-input KMB,M" lets normal clients do everything @@ -1833,15 +1837,15 @@ TrueColor defdepth 24 These defaults are simple safety measures to avoid someone unknowingly leaving his X11 desktop exposed (to the internet, say) for long - periods of time. Use the [216]-forever option (aka -many) to have + periods of time. Use the [217]-forever option (aka -many) to have x11vnc wait for more connections after the first client disconnects. - Use the [217]-shared option to have x11vnc allow multiple clients to + Use the [218]-shared option to have x11vnc allow multiple clients to connect simultaneously. - Recommended additional safety measures include using ssh ([218]see + Recommended additional safety measures include using ssh ([219]see above), stunnel, or a VPN to authenticate and encrypt the viewer - connections or to at least use the -rfbauth passwd-file [219]option to - use VNC password protection (or [220]-passwdfile) It is up to you to + connections or to at least use the -rfbauth passwd-file [220]option to + use VNC password protection (or [221]-passwdfile) It is up to you to apply these security measures, they will not be done for you automatically. @@ -1849,7 +1853,7 @@ TrueColor defdepth 24 Q-27: Can I limit which machines incoming VNC clients can connect from? - Yes, look at the [221]-allow and [222]-localhost options to limit + Yes, look at the [222]-allow and [223]-localhost options to limit connections by hostname or IP address. E.g. x11vnc -allow 192.168.0.1,192.168.0.2 @@ -1861,7 +1865,7 @@ TrueColor defdepth 24 Note that -localhost is the same as "-allow 127.0.0.1" For more control, build libvncserver with libwrap support - [223](tcp_wrappers) and then use /etc/hosts.allow See hosts_access(5) + [224](tcp_wrappers) and then use /etc/hosts.allow See hosts_access(5) for complete details. @@ -1881,7 +1885,7 @@ TrueColor defdepth 24 is "vnc", e.g.: vnc: 192.168.100.3 .example.com - Note that if you run x11vnc out of [224]inetd you do not need to build + Note that if you run x11vnc out of [225]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. @@ -1890,16 +1894,16 @@ TrueColor defdepth 24 internal LAN) rather than having it listen on all network interfaces and relying on -allow to filter unwanted connections out? - As of Mar/2005 in the libvncserver CVS, there is the "[225]-listen + As of Mar/2005 in the libvncserver CVS, there is the "[226]-listen ipaddr" option that enables this. For ipaddr either supply the desired network interface's IP address (or use a hostname that resolves to it) or use the string "localhost". For additional filtering simultaneously - use the "[226]-allow host1,..." option to allow only specific hosts + use the "[227]-allow host1,..." option to allow only specific hosts in. This option is useful if you want insure that no one can even begin a dialog with x11vnc from untrusted network interfaces (e.g. ppp0). The - option [227]-localhost now implies "-listen localhost" since that is + option [228]-localhost now implies "-listen localhost" since that is what most people expect it to do. @@ -1907,24 +1911,24 @@ TrueColor defdepth 24 interface, how I can occasionally allow in a non-localhost via the allowonce remote control command? - To do this specify "[228]-allow localhost". Unlike [229]-localhost + To do this specify "[229]-allow localhost". Unlike [230]-localhost this will leave x11vnc listening on all interfaces (but of course only allowing in local connections, e.g. ssh redirs). Then you can later run "x11vnc -R allowonce:somehost" or use to gui to permit a one-shot connection from a remote host. Note that if you do a lot of changing of the listening interface - ([230]-listen option) via remote control or gui, you may need to also - manually adjust the [231]-allow list if you unexpectedly get into a + ([231]-listen option) via remote control or gui, you may need to also + manually adjust the [232]-allow list if you unexpectedly get into a state where the allow list cannot match any hosts that would be coming - in on the listening interface. If you just toggle [232]-localhost on + in on the listening interface. If you just toggle [233]-localhost on and off x11vnc should see to it that you never get into such a state. Q-31: 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 [233]how to tunnel VNC via + See the description earlier on this page on [234]how to tunnel VNC via SSH from Unix to Unix. A number of ways are described along with some issues you may encounter. @@ -1935,7 +1939,7 @@ TrueColor defdepth 24 Q-32: How can I tunnel my connection to x11vnc via an encrypted SSH channel from Windows using an SSH client like Putty? - [234]Above we described how to tunnel VNC via SSH from Unix to Unix, + [235]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 @@ -1958,8 +1962,8 @@ TrueColor defdepth 24 process in a BAT file including launching the VNC viewer by using the plink Putty utility. Send us the script if you get that working. - For extra protection feel free to run x11vnc with the [235]-localhost - and [236]-rfbauth/[237]-passwdfile options. + For extra protection feel free to run x11vnc with the [236]-localhost + and [237]-rfbauth/[238]-passwdfile options. If the machine you SSH into via Putty is not the same machine with the X display you wish to view (e.g. your company provides incoming SSH @@ -1968,9 +1972,9 @@ TrueColor defdepth 24 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 [238]above another option is to first start the VNC + As discussed [239]above another option is to first start the VNC viewer in "listen" mode, and then launch x11vnc with the - "[239]-connect localhost" option to establish the reverse connection. + "[240]-connect localhost" option to establish the reverse connection. In this case a Remote port redirection (not Local) is needed for port 5500 instead of 5900 (i.e. 'Source port: 5500' and 'Destination: localhost:5500' for a Remote connection). @@ -1981,7 +1985,7 @@ TrueColor defdepth 24 some clients view-only? How about running an arbitrary program to make the decisions? - Yes, look at the "[240]-accept command" option, it allows you to + Yes, look at the "[241]-accept command" option, it allows you to specify an external command that is run for each new client. (use quotes around the command if it contains spaces, etc.). If the external command returns 0 the client is accepted, otherwise the @@ -2000,7 +2004,7 @@ TrueColor defdepth 24 own simple popup window. To accept the client press "y" or click mouse on the "Yes" button. To reject the client press "n" or click mouse on the "No" button. To accept the client View-only, press "v" or click - mouse on the "View" button. If the [241]-viewonly option has been + mouse on the "View" button. If the [242]-viewonly option has been supplied, the "View" action will not be present: the whole display is view only in that case. @@ -2016,7 +2020,7 @@ TrueColor defdepth 24 program to prompt the user whether the client should be accepted or not. This requires that you have xmessage installed and available via PATH. In case it is not already on your system, the xmessage program - is available at [242]ftp://ftp.x.org/ + is available at [243]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 @@ -2055,7 +2059,7 @@ elif [ $rc = 4 ]; then fi exit 1 - Stefan Radman has written a nice dtksh script [243]dtVncPopup for use + Stefan Radman has written a nice dtksh script [244]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. @@ -2064,7 +2068,7 @@ exit 1 popup is being run, so attached clients will not receive screen updates, etc during this period. - To run a command when a client disconnects, use the "[244]-gone + To run a command when a client disconnects, use the "[245]-gone command" option. This is for the user's convenience only: the return code of the command is not interpreted by x11vnc. The same environment variables are set as in "-accept command" (except that RFB_MODE will @@ -2079,13 +2083,13 @@ exit 1 such support. One approximate method involves starting x11vnc with the - [245]-localhost option. This basically requires the viewer user to log + [246]-localhost option. This basically requires the viewer user to log into the workstation where x11vnc is running via their Unix username and password, and then somehow set up a port redirection of his vncviewer 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 [246]elsewhere on this page. Of course a malicious user + mentioned [247]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 @@ -2094,7 +2098,7 @@ exit 1 Regarding limiting the set of Unix usernames who can connect, the traditional way would be to further require a VNC password to supplied (-rfbauth, -passwd, etc). A scheme that avoids a second password - involves using the [247]-accept option that runs a program to examine + involves using the [248]-accept option that runs a program to examine the connection information to determine which user is connecting from the local machine. For example, the program could use the ident service on the local machine (normally ident should not be trusted @@ -2127,7 +2131,7 @@ exit 1 # reject it display manager like gdm(1). Can I have x11vnc later switch to a different user? - As of Feb/2005 x11vnc has the [248]-users option that allows things + As of Feb/2005 x11vnc has the [249]-users option that allows things like this. Please read the documentation on it (in the x11vnc -help output) carefully for features and caveats. It's use can often decrease security unless care is taken. A nice use of it is "-users @@ -2148,7 +2152,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 [249]blockdpy.c The idea behind it is simple (but + source for it is [250]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 @@ -2164,8 +2168,8 @@ exit 1 # reject it bulletproof. A really robust solution would likely require X server and perhaps even video hardware support. - The blockdpy utility is launched by the [250]-accept option and told - to exit via the [251]-gone option (the vnc client user should + The blockdpy utility is launched by the [251]-accept option and told + to exit via the [252]-gone option (the vnc client user should obviously re-lock the screen before disconnecting!). Instructions can be found in the source code for the utility at the above link. @@ -2173,7 +2177,7 @@ exit 1 # reject it Q-37: Can I have x11vnc automatically lock the screen when I disconnect the VNC viewer? - Yes, a user mentions he uses the [252]-gone option under CDE to run a + Yes, a user mentions he uses the [253]-gone option under CDE to run a screen lock program: x11vnc -display :0 -forever -gone 'dtaction LockDisplay' @@ -2196,11 +2200,11 @@ exit 1 # reject it permissions to connect to the X display. Here are some ideas: - * Use the description under "Continuously" in the [253]FAQ on x11vnc + * Use the description under "Continuously" in the [254]FAQ on x11vnc and Display Managers - * Use the description in the [254]FAQ on x11vnc and inetd(1) + * Use the description in the [255]FAQ on x11vnc and inetd(1) * Start x11vnc from your $HOME/.xsession (or $HOME/.xinitrc) - * Although less reliable, see the [255]x11vnc_loop rc.local hack + * Although less reliable, see the [256]x11vnc_loop rc.local hack below. The display manager scheme will not be specific to which user has the @@ -2231,7 +2235,7 @@ x11vnc -logfile $HOME/.x11vnc.log -rfbauth $HOME/.vnc/passwd -forever -bg while running x11vnc as root, e.g. for the gnome display manager, gdm: x11vnc -auth /var/gdm/:0.Xauth -display :0 - (the [256]-auth option sets the XAUTHORITY variable for you). + (the [257]-auth option sets the XAUTHORITY variable for you). There will be a similar thing for xdm using however a different auth directory path (perhaps something like @@ -2256,7 +2260,7 @@ x11vnc -logfile $HOME/.x11vnc.log -rfbauth $HOME/.vnc/passwd -forever -bg auth file should be in /var/dt), you'll also need to add something like Dtlogin*grabServer:False to the Xconfig file (/etc/dt/config/Xconfig or /usr/dt/config/Xconfig on Solaris, see - [257]the example at the end of this FAQ). Then restart dtlogin, e.g.: + [258]the example at the end of this FAQ). Then restart dtlogin, e.g.: /etc/init.d/dtlogin stop; /etc/init.d/dtlogin start or reboot. Continuously. Have x11vnc reattach each time the X server is @@ -2310,7 +2314,7 @@ rever -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 [258]full details + killed immediately after the user logs in. Here are [259]full details on how to configure gdm _________________________________________________________________ @@ -2352,13 +2356,13 @@ rever -bg If you do not want to deal with any display manager startup scripts, here is a kludgey script that can be run manually or out of a boot - file like rc.local: [259]x11vnc_loop It will need some local + file like rc.local: [260]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 method described above is greatly preferred. If the machine is a traditional Xterminal you may want to read - [260]this FAQ. + [261]this FAQ. Q-40: Can I run x11vnc out of inetd(1)? How about xinetd(1)? @@ -2368,7 +2372,7 @@ rever -bg 5900 stream tcp nowait root /usr/sbin/tcpd /usr/local/bin/x11vnc_sh - where the shell script /usr/local/bin/x11vnc_sh uses the [261]-inetd + where the shell script /usr/local/bin/x11vnc_sh uses the [262]-inetd option and looks something like (you'll need to customize to your settings). #!/bin/sh @@ -2380,13 +2384,13 @@ rever -bg (otherwise the standard error also goes to the VNC vncviewer, and that confuses it greatly). If you do not use a wrapper script as above but rather call x11vnc directly in /etc/inetd.conf and do not redirect - stderr to a file, then you must specify the -q (aka [262]-quiet) + stderr to a file, then you must specify the -q (aka [263]-quiet) option: "/usr/local/bin/x11vnc -q -inetd ..." or use "-o logfile" to collect the output in a file. The wrapper script with redirection to a log file is the recommended method because the errors and warnings printed out are very useful in troubleshooting problems. - Note also the need to set XAUTHORITY via [263]-auth to point to the + Note also the need to set XAUTHORITY via [264]-auth to point to the MIT-COOKIE auth file to get permission to connect to the X display (setting and exporting the XAUTHORITY variable accomplishes the same thing). See the x11vnc_loop file in the previous question for more @@ -2434,7 +2438,7 @@ service x11vncservice web browser? To have x11vnc serve up a Java VNC viewer applet to any web browsers - that connect to it, run x11vnc with this [264]option: + that connect to it, run x11vnc with this [265]option: -httpdir /path/to/the/java/classes/dir (this directory will contain the files index.vnc and, for example, @@ -2464,7 +2468,7 @@ service x11vncservice As of Mar/2004 in the libvncserver CVS x11vnc supports reverse connections. On Unix one starts the VNC viewer in listen mode: vncviewer -listen (see your documentation for Windows, etc), and then - starts up x11vnc with the [265]-connect option. To connect immediately + starts up x11vnc with the [266]-connect option. To connect immediately at x11vnc startup time use the "-connect host:port" option (use commas for a list of hosts to connect to). The ":port" is optional (default is 5500). If a file is specified instead: -connect /path/to/some/file @@ -2472,7 +2476,7 @@ service x11vncservice hosts to connect to. To use the vncconnect(1) program (from the core VNC package at - www.realvnc.com) specify the [266]-vncconnect option to x11vnc (Note: + www.realvnc.com) specify the [267]-vncconnect option to x11vnc (Note: as of Dec/2004 -vncconnect is now the default). vncconnect(1) must be pointed to the same X11 DISPLAY as x11vnc (since it uses X properties to communicate with x11vnc). If you do not have or do not want to get @@ -2514,7 +2518,7 @@ xprop -root -f VNC_CONNECT 8s -set VNC_CONNECT "$1" There are some annoyances WRT Xvfb though. The default keyboard mapping seems to be very poor. One should run x11vnc with - [267]-add_keysyms option to have keysyms added automatically. Also, to + [268]-add_keysyms option to have keysyms added automatically. Also, to add the Shift_R and Control_R modifiers something like this is needed: #!/bin/sh xmodmap -e "keycode any = Shift_R" @@ -2541,7 +2545,7 @@ xmodmap -e "add Control = Control_L Control_R" An X server can be started on the headless machine (sometimes this requires configuring the X server to not fail if it cannot detect a keyboard or mouse, see the next paragraph). Then you can export that X - display via x11vnc (e.g. see [268]this FAQ) and access it from + display via x11vnc (e.g. see [269]this FAQ) and access it from anywhere on the network via a VNC viewer. Some tips on getting X servers to start on machines without keyboard @@ -2584,7 +2588,7 @@ xmodmap -e "add Control = Control_L Control_R" 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 [269]shm_clear to list and prompt for removal + Here is a shell script [270]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, @@ -2618,35 +2622,35 @@ ied) in /etc/system. See the next paragraph for more workarounds. To minimize the number of shm segments used by x11vnc try using the - [270]-onetile option (corresponds to only 3 shm segments used, and + [271]-onetile option (corresponds to only 3 shm segments used, and adding -fs 1.0 knocks it down to 2). If you are having much trouble with shm segments, consider disabling shm completely via the - [271]-noshm option. Performance will be somewhat degraded but when + [272]-noshm option. Performance will be somewhat degraded but when done over local machine sockets it should be acceptable (see an - [272]earlier question discussing -noshm). + [273]earlier question discussing -noshm). Q-46: How can I make x11vnc use less system resources? - The [273]-nap and "[274]-wait n" (where n is the sleep between polls + The [274]-nap and "[275]-wait n" (where n is the sleep between polls in milliseconds, the default is 30 or so) option are good places to start. Reducing the X server bits per pixel depth (e.g. to 16bpp or even 8bpp) will further decrease memory I/O and network I/O. Using the - [275]-onetile option will use less memory and use fewer shared memory - slots (add [276]-fs 1.0 for one less slot). + [276]-onetile option will use less memory and use fewer shared memory + slots (add [277]-fs 1.0 for one less slot). Q-47: How can I make x11vnc use MORE system resources? - You can try [277]-threads and dial down the wait time (e.g. -wait 1) - and possibly dial down [278]-defer as well. Note that if you try to + You can try [278]-threads and dial down the wait time (e.g. -wait 1) + and possibly dial down [279]-defer as well. Note that if you try to increase the "frame rate" too much you can bog down the server end with the extra work it needs to do compressing the framebuffer data, etc. That said, it is possible to "stream" video via x11vnc if the video window is small enough. E.g. a 256x192 xawtv TV capture window (using - the x11vnc [279]-id option) can be streamed over a LAN or wireless at + the x11vnc [280]-id option) can be streamed over a LAN or wireless at a reasonable frame rate. @@ -2662,7 +2666,7 @@ ied) * Use a smaller desktop size (e.g. 1024x768 instead of 1280x1024) * Make sure the desktop background is a solid color (the background is resent every time it is re-exposed). Consider using the - [280]-solid [color] option. + [281]-solid [color] option. * Configure your window manager or desktop "theme" to not use fancy images, shading, and gradients for the window decorations, etc. Disable Opaque moves, resizes, and animations. @@ -2678,7 +2682,7 @@ ied) worth it, but could be of use in some situations. VNC viewer parameters: - * Use a [281]TightVNC enabled viewer! + * Use a [282]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 @@ -2696,19 +2700,19 @@ ied) vncviewer to be very slow) x11vnc parameters: - * Try using [282]-nodragging (no screen updates when dragging mouse, + * Try using [283]-nodragging (no screen updates when dragging mouse, but sometimes you miss visual feedback) - * Try the [283]-progressive pixelheight mode with the block + * Try the [284]-progressive pixelheight mode with the block pixelheight 100 or so (delays sending vertical blocks since they may change while viewer is receiving earlier ones) - * Set [284]-fs 1.0 (disables fullscreen updates) - * Try increasing [285]-wait or [286]-defer (reduces the maximum + * Set [285]-fs 1.0 (disables fullscreen updates) + * Try increasing [286]-wait or [287]-defer (reduces the maximum "frame rate", but won't help much for large screen changes) - * If you just want to watch one (simple) window use [287]-id (cuts + * If you just want to watch one (simple) window use [288]-id (cuts down extraneous polling and updates, but can be buggy or insufficient) - * Set [288]-nosel (disables all clipboard selection exchange) - * Use [289]-nocursor and [290]-nocursorpos (repainting the remote + * Set [289]-nosel (disables all clipboard selection exchange) + * Use [290]-nocursor and [291]-nocursorpos (repainting the remote cursor position and shape takes resources and round trips) @@ -2723,7 +2727,7 @@ ied) second to read the changes induce from moving a large window, if this to be done a number of times in succession the window or scroll appears to "lurch" forward). See the description in the - [291]-pointer_mode option for more info. The next bottleneck is + [292]-pointer_mode option for more info. The next bottleneck is compressing all of these changes and sending them out to connected viewers, however the VNC protocol is pretty much self-adapting with respect to that (updates are only packaged and sent when viewers ask @@ -2733,25 +2737,25 @@ ied) tree. The default should now be much better than before and dragging small windows around should no longer be a huge pain. If for some reason these changes make matters worse, you can go back to the old - way via the "[292]-pointer_mode 1" option. + way via the "[293]-pointer_mode 1" option. - Also added was the [293]-nodragging option that disables all screen + Also added was the [294]-nodragging option that disables all screen updates while dragging with the mouse (i.e. mouse motion with a button held down). This gives the snappiest response, but might be undesired in some circumstances when you want to see the visual feedback while dragging (e.g. menu traversal or text selection). - As of Dec/2004 in the libvncserver CVS the [294]-pointer_mode n option + As of Dec/2004 in the libvncserver CVS the [295]-pointer_mode n option was introduced. n=1 is the original mode, n=2 an improvement, etc.. See the -pointer_mode n help for more info. - Also, in some circumstances the [295]-threads option can improve + Also, in some circumstances the [296]-threads option can improve response considerably. Be forewarned that if more than one vncviewer is connected at the same time then libvncserver may not be thread safe (try to get the viewers to use different VNC encodings, e.g. tight and ZRLE). - See the [296]wireframe FAQ below for a scheme to sweep this problem + See the [297]wireframe FAQ below for a scheme to sweep this problem under the rug for window moves or resizes. @@ -2766,13 +2770,13 @@ ied) shown. When the window move/resize stops, it returns to normal processing: you should only see the window appear in the new position. This spares you from interacting with a "lurching" window between all - of the intermediate steps. BTW the lurching is due to [297]slow video + of the intermediate steps. BTW the lurching is due to [298]slow video card read rates. A displacement, even a small one, of a large window requires a non-negligible amount of time, a good fraction of a second, to read in from the hardware framebuffer. The mode is currently on by default because most people are inflicted - with the problem. It can be disabled with the [298]-nowireframe + with the problem. It can be disabled with the [299]-nowireframe option. Why might one want to turn off the wireframing? Since x11vnc is merely guessing when windows are being moved/resized, it may guess poorly for your window-manager or desktop, or even for the way you @@ -2812,13 +2816,13 @@ ied) * Maximum time to show a wireframe animation. * Minimum time between sending wireframe outlines. - See the [299]"-wireframe tweaks" option for more details. On a slow + See the [300]"-wireframe tweaks" option for more details. On a slow link, e.g. dialup modem, you may want to increase all four of these times, e.g. double them from the defaults. CopyRect encoding: In addition to the above there is the - [300]"-wirecopyrect mode" option. It is also on by default. This + [301]"-wirecopyrect mode" option. It is also on by default. This instructs x11vnc to not only show show the wireframe animation, but to also instruct all connected VNC viewers to locally translate the window image data from the original position to the new position on @@ -2882,13 +2886,13 @@ ied) DAMAGE rectangles to contain real damage. The larger rectangles are only used as hints to focus the traditional scanline polling (i.e. if a scanline doesn't intersect a recent DAMAGE rectangle, the scan is - skipped). You can use the "[301]-xd_area A" option to adjust the size + skipped). You can use the "[302]-xd_area A" option to adjust the size of the trusted DAMAGE rectangles. The default is 20000 pixels (e.g. a 140x140 square, etc). Use "-xd_area 0" to disable the cutoff and trust all DAMAGE rectangles. - The option "[302]-xd_mem f" may also be of use in tuning the - algorithm. To disable using DAMAGE entirely use "[303]-noxdamage". + The option "[303]-xd_mem f" may also be of use in tuning the + algorithm. To disable using DAMAGE entirely use "[304]-noxdamage". [Mouse Cursor Shapes] @@ -2906,16 +2910,16 @@ ied) this is because the cursor shape is often downloaded to the graphics hardware (video card), but I could be mistaken. - A simple kludge is provided by the "[304]-cursor X" option that + A simple kludge is provided by the "[305]-cursor X" option that changes the cursor when the mouse is on the root background (or any window has the same cursor as the root background). Note that desktops like GNOME or KDE often cover up the root background, so this won't - work for those cases. Also see the "[305]-cursor some" option for + work for those cases. Also see the "[306]-cursor some" option for additional kludges. Note that as of Aug/2004 in the libvncserver CVS, on Solaris using the SUN_OVL overlay extension and IRIX, x11vnc can show the correct mouse - cursor when the [306]-overlay option is supplied. See [307]this FAQ + cursor when the [307]-overlay option is supplied. See [308]this FAQ for more info. Also as of Dec/2004 in the libvncserver CVS XFIXES X extension support @@ -2923,7 +2927,7 @@ ied) XFIXES fixes the problem of the cursor-shape being write-only: x11vnc can now query the X server for the current shape and send it back to the connected viewers. XFIXES is available on recent Linux Xorg based - distros and [308]Solaris 10. + distros and [309]Solaris 10. The only XFIXES issue is the handling of alpha channel transparency in cursors. If a cursor has any translucency then in general it must be @@ -2931,7 +2935,7 @@ ied) cursor transparency can also handled exactly: when the VNC Viewer requires the cursor shape be drawn into the VNC framebuffer or if you apply a patch to your VNC Viewer to extract hidden alpha channel data - under 32bpp. [309]Details can be found here. + under 32bpp. [310]Details can be found here. Q-53: When using XFIXES cursorshape mode, some of the cursors look @@ -2964,17 +2968,17 @@ ied) for most cursor themes and you don't have to worry about it. In case it still looks bad for your cursor theme, there are (of - course!) some tunable parameters. The "[310]-alphacut n" option lets + course!) some tunable parameters. The "[311]-alphacut n" option lets you set the threshold "n" (between 0 and 255): cursor pixels with alpha values below n will be considered completely transparent while values equal to or above n will be completely opaque. The default is - 240. The "[311]-alphafrac f" option tries to correct individual + 240. The "[312]-alphafrac f" option tries to correct individual cursors that did not fare well with the default -alphacut value: if a cursor has less than fraction f (between 0.0 and 1.0) of its pixels selected by the default -alphacut, the threshold is lowered until f of its pixels are selected. The default fraction is 0.33. - Finally, there is an option [312]-alpharemove that is useful for + Finally, there is an option [313]-alpharemove that is useful for themes where many cursors are light colored (e.g. "whiteglass"). XFIXES returns the cursor data with the RGB values pre-multiplied by the alpha value. If the white cursors look too grey, specify @@ -2990,11 +2994,11 @@ ied) send the alpha channel data to libvncserver. However, this data will only be used for VNC clients that do not support the CursorShapeUpdates VNC extension (or have disabled it). It can be - disabled for all clients with the [313]-nocursorshape x11vnc option. + disabled for all clients with the [314]-nocursorshape x11vnc option. In this case the cursor is drawn, correctly blended with the background, into the VNC framebuffer before being sent out to the client. So the alpha blending is done on the x11vnc side. Use the - [314]-noalphablend option to disable this behavior (always approximate + [315]-noalphablend option to disable this behavior (always approximate transparent cursors with opaque RGB values). The CursorShapeUpdates VNC extension complicates matters because the @@ -3022,9 +3026,9 @@ ied) Q-55: 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 [315]tightvnc extension + This default takes advantage of a [316]tightvnc extension (CursorShapeUpdates) that allows specifying a cursor image shape for - the local VNC viewer. You may disable it with the [316]-nocursor + the local VNC viewer. You may disable it with the [317]-nocursor option to x11vnc if your viewer does not have this extension. Note: as of Aug/2004 in the libvncserver CVS this should be fixed: the @@ -3038,18 +3042,18 @@ ied) clients (i.e. passive viewers can see the mouse cursor being moved around by another viewer)? - Use the [317]-cursorpos option when starting x11vnc. A VNC viewer must + Use the [318]-cursorpos option when starting x11vnc. A VNC viewer must support the Cursor Positions Updates for the user to see the mouse motions (the TightVNC viewers support this). As of Aug/2004 in the - libvncserver CVS -cursorpos is the default. See also [318]-nocursorpos - and [319]-nocursorshape. + libvncserver CVS -cursorpos is the default. See also [319]-nocursorpos + and [320]-nocursorshape. Q-57: Is it possible to swap the mouse buttons (e.g. left-handed operation), or arbitrarily remap them? How about mapping button clicks to keystrokes, e.g. to partially emulate Mouse wheel scrolling? - You can remap the mouse buttons via something like: [320]-buttonmap + You can remap the mouse buttons via something like: [321]-buttonmap 13-31 (or perhaps 12-21). Also, note that xmodmap(1) lets you directly adjust the X server's button mappings, but in some circumstances it might be more desirable to have x11vnc do it. @@ -3057,7 +3061,7 @@ ied) One user had an X server with only one mouse button(!) and was able to map all of the VNC client mouse buttons to it via: -buttonmap 123-111. - Note that the [321]-debug_pointer option prints out much info for + Note that the [322]-debug_pointer option prints out much info for every mouse/pointer event and is handy in solving problems. To map mouse button clicks to keystrokes you can use the alternate @@ -3079,7 +3083,7 @@ ied) Exactly what keystroke "scrolling" events they should be bound to depends on one's taste. If this method is too approximate, one could - consider not using [322]-buttonmap but rather configuring the X server + consider not using [323]-buttonmap but rather configuring the X server to think it has a mouse with 5 buttons even though the physical mouse does not. @@ -3100,7 +3104,7 @@ ied) Q-58: How can I get my AltGr and Shift modifiers to work between keyboards for different languages? - The option [323]-modtweak should be of some use for this. It is a mode + The option [324]-modtweak should be of some use for this. It is a mode that monitors the state of the Shift and AltGr Modifiers and tries to deduce the correct keycode to send, possibly by sending fake modifier key presses and releases in addition to the actual keystroke. @@ -3109,17 +3113,17 @@ 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 [324]this FAQ + and ">" it. This key does not exist on the keyboard (see [325]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 [325]FAQ about the -xkb option for a more powerful method + Also see the [326]FAQ about the -xkb option for a more powerful method of modifier tweaking for use on X servers with the XKEYBOARD extension. When trying to resolve keyboard mapping problems, note that the - [326]-debug_keyboard option prints out much info for every keystroke + [327]-debug_keyboard option prints out much info for every keystroke and so can be useful debugging things. @@ -3170,17 +3174,17 @@ ied) -remap less-comma These are convenient in that they do not modify the actual X server - settings. The former ([327]-modtweak) is a mode that monitors the + settings. The former ([328]-modtweak) is a mode that monitors the state of the Shift and AltGr modifiers and tries to deduce the correct keycode sequence to send. Since Jul/2004 -modtweak is now the default. - The latter ([328]-remap less-comma) is an immediate remapping of the + The latter ([329]-remap less-comma) is an immediate remapping of the keysym less to the keysym comma when it comes in from a client (so when Shift is down the comma press will yield "<"). - See also the [329]FAQ about the -xkb option as a possible workaround + See also the [330]FAQ about the -xkb option as a possible workaround using the XKEYBOARD extension. - Note that the [330]-debug_keyboard option prints out much info for + Note that the [331]-debug_keyboard option prints out much info for every keystroke to aid debugging keyboard problems. @@ -3205,7 +3209,7 @@ ied) In both cases no AltGr is sent to the VNC server, but we know AltGr is needed on the physical international keyboard to type a "@". - This all worked fine with x11vnc running with the [331]-modtweak + This all worked fine with x11vnc running with the [332]-modtweak option (it figures out how to adjust the Modifier keys (Shift or AltGr) to get the "@"). However it fails under recent versions of XFree86 (and the X.org fork). These run the XKEYBOARD extension by @@ -3223,7 +3227,7 @@ ied) * there is a new option -xkb to use the XKEYBOARD extension API to do the Modifier key tweaking. - The [332]-xkb option seems to fix all of the missing keys: "@", "<", + The [333]-xkb option seems to fix all of the missing keys: "@", "<", ">", etc.: it is recommended that you try it if you have this sort of problem. Let us know if there are any remaining problems (see the next paragraph for some known problems). If you specify the -debug_keyboard @@ -3241,7 +3245,7 @@ ied) was attached to keycode 93 (no physical key generates this keycode) while ISO_Level3_Shift was attached to keycode 113. The keycode skipping option was used to disable the ghost key: - [333]-skip_keycodes 93 + [334]-skip_keycodes 93 * In implementing -xkb we noticed that some characters were still not getting through, e.g. "~" and "^". This is not really an XKEYBOARD problem. What was happening was the VNC viewer was @@ -3258,14 +3262,14 @@ ied) What to do? In general the VNC protocol has not really solved this problem: what should be done if the VNC viewer sends a keysym not recognized by the VNC server side? Workarounds can possibly be - created using the [334]-remap x11vnc option: + created using the [335]-remap x11vnc option: -remap asciitilde-dead_tilde,asciicircum-dead_circumflex etc. Use -remap filename if the list is long. Please send us your workarounds for this problem on your keyboard. Perhaps we can have x11vnc adjust automatically at some point. Also see the - [335]-add_keysyms option in the next paragraph. - * To complement the above workaround using the [336]-remap, an - option [337]-add_keysyms was added. This option instructs x11vnc + [336]-add_keysyms option in the next paragraph. + * To complement the above workaround using the [337]-remap, an + option [338]-add_keysyms was added. This option instructs x11vnc to bind any unknown Keysyms coming in from VNC viewers to unused Keycodes in the X server. This modifies the global state of the X server. When x11vnc exits it removes the extra keymappings it @@ -3282,7 +3286,7 @@ ied) Short answer: disable key autorepeating by running the command "xset r off" on the Xserver where x11vnc is run (restore via "xset r on") or - use the new (Jul/2004) [338]-norepeat x11vnc option. You will still + use the new (Jul/2004) [339]-norepeat x11vnc option. You will still have autorepeating because that is taken care of on your VNC viewer side. Update: as of Dec/2004 -norepeat is now the default. Use -repeat to disable it. @@ -3304,7 +3308,7 @@ ied) off", does the problem go away? The workaround is to manually apply "xset r off" and "xset r on" as - needed, or to use the [339]-norepeat (which has since Dec/2004 been + needed, or to use the [340]-norepeat (which has since Dec/2004 been made the default). Note that with X server autorepeat turned off the VNC viewer side of the connection will (nearly always) do its own autorepeating so there is no big loss here, unless someone is also @@ -3315,7 +3319,7 @@ ied) keystrokes!! Are you using x11vnc to log in to an X session? (as described in - [340]this FAQ) If so, x11vnc is starting before your session and it + [341]this FAQ) If so, x11vnc is starting before your session and it disables autorepeat when you connect, but then after you log in your session startup (GNOME, KDE, ...) could be resetting the autorepeat to be on. Or it could be something inside your desktop trying to be @@ -3339,7 +3343,7 @@ ied) 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? - Something like "[341]-remap Super_R-Mode_switch" x11vnc option may + Something like "[342]-remap Super_R-Mode_switch" x11vnc option may work. Note that Super_R is the "Right Windoze(tm) Flaggie" key; you may want to choose another. The -debug_keyboard option comes in handy in finding keysym names (so does xev(1)). @@ -3362,7 +3366,7 @@ ied) Since xmodmap(1) modifies the X server mappings you may not want to do this (because it affects local work on that machine). Something like - the [342]-remap Alt_L-Meta_L to x11vnc may be sufficient for ones + the [343]-remap Alt_L-Meta_L to x11vnc may be sufficient for ones needs, and does not modify the X server environment. Note that you cannot send Alt_L in this case, maybe -remap Super_L-Meta_L would be a better choice if the Super_L key is typically unused. @@ -3373,7 +3377,7 @@ ied) This can be done directly in some X servers using AccessX and Pointer_EnableKeys, but is a bit awkward. It may be more convenient to - have x11vnc do the remapping. This can be done via the [343]-remap + have x11vnc do the remapping. This can be done via the [344]-remap option using the fake "keysyms" Button1, Button2, etc. as the "to" keys (i.e. the ones after the "-") @@ -3381,7 +3385,7 @@ ied) a touchpad with only two buttons. It is difficult to do a middle button "paste" because (using XFree86 Emulate3Buttons) you have to click both buttons on the touch pad at the same time. This remapping: - [344]-remap Super_R-Button2 + [345]-remap Super_R-Button2 maps the Super_R "flag" key press to the Button2 click, thereby making X pasting a bit easier. @@ -3410,7 +3414,7 @@ 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 [345]this + local display. Especially for hand-held devices. See also [346]this FAQ on x11vnc scaling. @@ -3419,7 +3423,7 @@ ied) As of Jun/2004 in the libvncserver CVS x11vnc provides basic server-side scaling. It is a global scaling of the desktop, not a - per-client setting. To enable it use the "[346]-scale fraction" + per-client setting. To enable it use the "[347]-scale fraction" option. "fraction" can either be a floating point number (e.g. -scale 0.5) or the alternative m/n fraction notation (e.g. -scale 2/3). Note that if fraction is greater than one the display is magnified. @@ -3439,7 +3443,7 @@ ied) One can also use the ":nb" with an integer scale factor (say "-scale 2:nb") to use x11vnc as a screen magnifier for vision impaired - [347]applications. Since with integer scale factors the framebuffers + [348]applications. Since with integer scale factors the framebuffers become huge and scaling operations time consuming, be sure to use ":nb" for the fastest response. @@ -3465,12 +3469,12 @@ ied) If one desires per-client scaling for something like 1:1 from a workstation and 1:2 from a smaller device (e.g. handheld), currently the only option is to run two (or more) x11vnc processes with - different scalings listening on separate ports ([348]-rfbport option, + different scalings listening on separate ports ([349]-rfbport option, etc.). As of Mar/2005 in the libvncserver CVS x11vnc now scales the mouse cursor with the same scale factor as the screen. If you don't want - that, use the [349]"-scale_cursor frac" option to set the cursor + that, use the [350]"-scale_cursor frac" option to set the cursor scaling to a different factor (e.g. use "-scale_cursor 1" to keep the cursor at its natural unscaled size). @@ -3487,16 +3491,16 @@ ied) rectangular (e.g. 1280x1024 and 1024x768 monitors joined together), then there will be "non-existent" areas on the screen. The X server will return "garbage" image data for these areas and so they may be - distracting to the viewer. The [350]-blackout x11vnc option allows you + distracting to the viewer. The [351]-blackout x11vnc option allows you to blacken-out rectangles by specifying their WxH+X+Y geometries. If - your system has the libXinerama library, the [351]-xinerama x11vnc + your system has the libXinerama library, the [352]-xinerama x11vnc option can be used to have it automatically determine the rectangles to be blackened out. (Note on 8bpp PseudoColor displays the fill color may not be black). Some users have reported that the mouse does not behave properly for their Xinerama display: i.e. the mouse cannot be moved to all regions - of the large display. If this happens try using the [352]-xwarppointer + of the large display. If this happens try using the [353]-xwarppointer option. This instructs x11vnc to fake mouse pointer motions using the XWarpPointer function instead of the XTestFakeMotionEvent XTEST function. (This may be due to a bug in the X server for XTEST when @@ -3521,22 +3525,22 @@ 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 [353]FAQ to increase the limit. It - is probably also a good idea to run with the [354]-onetile option in + /etc/system as mentioned in another [354]FAQ to increase the limit. It + is probably also a good idea to run with the [355]-onetile option in this case (to limit each x11vnc to 3 shm segments), or even - [355]-noshm to use no shm segments. + [356]-noshm to use no shm segments. Q-70: Can x11vnc show only a portion of the display? (E.g. for a special purpose rfb application). - As of Mar/2005 in the libvncserver CVS x11vnc has the "[356]-clip + As of Mar/2005 in the libvncserver CVS x11vnc has the "[357]-clip WxH+X+Y" option to select a rectangle of width W, height H and offset (X, Y). Thus the VNC screen will be the clipped sub-region of the display and be only WxH in size. This also works to view a sub-region of a single application window if - the [357]-id or [358]-sid options are used. The offset is measured + the [358]-id or [359]-sid options are used. The offset is measured from the upper left corner of the selected window. @@ -3545,7 +3549,7 @@ ied) crash. As of Dec/2004 in the libvncserver CVS x11vnc supports XRANDR. You - enable it with the [359]-xrandr option to make x11vnc monitor XRANDR + enable it with the [360]-xrandr option to make x11vnc monitor XRANDR events and also trap X server errors if the screen change occurred in the middle of an X call like XGetImage. Once it traps the screen change it will create a new framebuffer using the new screen. If the @@ -3554,7 +3558,7 @@ ied) viewer will automatically resize. Otherwise, the new framebuffer is fit as best as possible into the original viewer size (portions of the screen may be clipped, unused, etc). For these viewers you can try the - [360]-padgeom option to make the region big enough to hold all resizes + [361]-padgeom option to make the region big enough to hold all resizes and rotations. If you specify "-xrandr newfbsize" then vnc viewers that do not @@ -3608,9 +3612,9 @@ ied) * Fullscreen mode The way VMWare does Fullscreen mode on Linux is to display the Guest - desktop in a separate Virtual Console (e.g. VC 8) (see [361]this FAQ + desktop in a separate Virtual Console (e.g. VC 8) (see [362]this FAQ on VC's for background). Unfortunately, this Fullscreen VC is not an X - server. So x11vnc cannot access it (however, [362]see this for a + server. So x11vnc cannot access it (however, [363]see this for a possible partial workaround). x11vnc works fine with "Normal X application window" and "Quick-Switch mode" because these use X. @@ -3627,13 +3631,13 @@ ied) response. One can also cut the display depth (e.g. to 16bpp) in this 2nd X session to improve video performance. This 2nd X session emulates Fullscreen mode to some degree and can be viewed via x11vnc - as long as the VMWare X session [363]is in the active VC. + as long as the VMWare X session [364]is in the active VC. Also note that with a little bit of playing with "xwininfo -all -children" output one can extract the (non-toplevel) windowid of the of the Guest desktop only when VMWare is running as a normal X application. Then one can export just the guest desktop (i.e. without - the VMWare menu buttons) by use of the [364]-id windowid option. The + the VMWare menu buttons) by use of the [365]-id windowid option. The caveats are the X session VMWare is in must be in the active VC and the window must be fully visible, so this mode is not terribly convenient, but could be useful in some circumstances (e.g. running @@ -3714,7 +3718,7 @@ ied) screen to either shm or a mapped file. The format of these is XWD and so the initial header should be skipped. BTW, since XWD is not strictly RGB the view will only be approximate. Of course for the case - of Xvfb x11vnc can poll it much better via the [365]X API, but you get + of Xvfb x11vnc can poll it much better via the [366]X API, but you get the idea. By default in -rawfb mode x11vnc will actually close any X display it @@ -3767,7 +3771,7 @@ ied) keystrokes into the Linux console (e.g. the virtual consoles: /dev/tty1, /dev/tty2, etc) in x11vnc/misc/vcinject.pl. It is based on the vncterm/LinuxVNC.c program also in the libvncserver CVS. So to - view and interact with VC #2 (assuming it is the [366]active VC) one + view and interact with VC #2 (assuming it is the [367]active VC) one can run something like: x11vnc -rawfb map:/dev/fb0@1024x768x16 -pipeinput './vcinject.pl 2' @@ -3780,7 +3784,7 @@ ied) more accurate and faster LinuxVNC program. The only advantage x11vnc -rawfb might have is that it can presumably allow interaction with a non-text application, e.g. one based on svgalib. For example the - [367]VMWare Fullscreen mode is actually viewable under -rawfb. But + [368]VMWare Fullscreen mode is actually viewable under -rawfb. But this isn't much use until one figures out how to inject keystrokes and mouse events. @@ -3811,9 +3815,9 @@ ied) As of Jan/2004 in the libvncserver CVS x11vnc supports the "CutText" part of the rfb protocol. Furthermore, x11vnc is able to hold the PRIMARY selection (Xvnc does not seem to do this). If you don't want - the Clipboard/Selection exchanged use the [368]-nosel option. If you + the Clipboard/Selection exchanged use the [369]-nosel option. If you don't want the PRIMARY selection to be polled for changes use the - [369]-noprimary option. + [370]-noprimary option. You may need to watch out for desktop utilities such as KDE's "Klipper" that do odd things with the selection, clipboard, and @@ -3828,7 +3832,7 @@ ied) not on by default in Solaris, see Xserver(1) for how to turn it on via +kb), and so you won't hear them if the extension is not present. - If you don't want to hear the beeps use the [370]-nobell option. If + If you don't want to hear the beeps use the [371]-nobell option. If you want to hear the audio from the remote applications, consider trying a redirector such as esd. @@ -3844,7 +3848,7 @@ ied) Click on the PayPal button below for more info. Also, in general I always enjoy hearing from x11vnc users, how they use it, what new features they would like, etc. Please send me an - [371]email! + [372]email! [PayPal] @@ -4007,220 +4011,221 @@ References 155. http://packages.debian.org/x11vnc 156. http://www.linuxpackages.net/search_view.php?by=name&name=x11vnc 157. http://dag.wieers.com/packages/x11vnc/ - 158. http://www.sunfreeware.com/ - 159. http://www.bell-labs.com/project/wwexptools/packages.html - 160. http://www.karlrunge.com/x11vnc/index.html#solarisbuilding - 161. http://www.karlrunge.com/x11vnc/bins - 162. http://www.tightvnc.com/download.html - 163. http://www.realvnc.com/download-free.html - 164. http://sourceforge.net/projects/cotvnc/ - 165. http://www.karlrunge.com/x11vnc/x11vnc_opts.html - 166. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-gui - 167. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-q - 168. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-bg - 169. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-o - 170. http://www.karlrunge.com/x11vnc/x11vnc.c - 171. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-sigpipe - 172. http://www.karlrunge.com/x11vnc/index.html#solarisbuilding - 173. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nofb - 174. http://fredrik.hubbe.net/x2vnc.html - 175. http://www.hubbe.net/~hubbe/win2vnc.html - 176. http://www.deboer.gmxhome.de/ - 177. http://sourceforge.net/projects/win2vnc/ - 178. http://fredrik.hubbe.net/x2vnc.html - 179. http://freshmeat.net/projects/x2x/ - 180. http://ftp.digital.com/pub/Digital/SRC/x2x/ - 181. http://zapek.com/software/zvnc/ - 182. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-visual - 183. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-flashcmap - 184. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-notruecolor - 185. http://www.karlrunge.com/x11vnc/index.html#faq-8bpp - 186. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-overlay + 158. http://linux01.gwdg.de/~pbleser/rpm-navigation.php?cat=Network/x11vnc/ + 159. http://www.sunfreeware.com/ + 160. http://www.bell-labs.com/project/wwexptools/packages.html + 161. http://www.karlrunge.com/x11vnc/index.html#solarisbuilding + 162. http://www.karlrunge.com/x11vnc/bins + 163. http://www.tightvnc.com/download.html + 164. http://www.realvnc.com/download-free.html + 165. http://sourceforge.net/projects/cotvnc/ + 166. http://www.karlrunge.com/x11vnc/x11vnc_opts.html + 167. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-gui + 168. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-q + 169. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-bg + 170. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-o + 171. http://www.karlrunge.com/x11vnc/x11vnc.c + 172. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-sigpipe + 173. http://www.karlrunge.com/x11vnc/index.html#solarisbuilding + 174. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nofb + 175. http://fredrik.hubbe.net/x2vnc.html + 176. http://www.hubbe.net/~hubbe/win2vnc.html + 177. http://www.deboer.gmxhome.de/ + 178. http://sourceforge.net/projects/win2vnc/ + 179. http://fredrik.hubbe.net/x2vnc.html + 180. http://freshmeat.net/projects/x2x/ + 181. http://ftp.digital.com/pub/Digital/SRC/x2x/ + 182. http://zapek.com/software/zvnc/ + 183. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-visual + 184. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-flashcmap + 185. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-notruecolor + 186. http://www.karlrunge.com/x11vnc/index.html#faq-8bpp 187. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-overlay - 188. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id - 189. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-overlay - 190. http://www.karlrunge.com/x11vnc/index.html#faq-overlays - 191. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id - 192. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-sid - 193. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-display - 194. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noshm - 195. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-flipbyteorder - 196. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-auth - 197. http://www.karlrunge.com/x11vnc/index.html#xauth_pain - 198. http://www.karlrunge.com/x11vnc/index.html#faq-noshm - 199. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remote - 200. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-query - 201. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-forever - 202. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-bg - 203. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-clear_mods - 204. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-clear_keys - 205. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remote - 206. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-query - 207. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-gui - 208. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-storepasswd - 209. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-rfbauth - 210. http://www.karlrunge.com/x11vnc/index.html#faq-passwdfile - 211. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-viewpasswd - 212. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-passwd - 213. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-passwdfile - 214. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-rfbauth - 215. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-input - 216. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-forever - 217. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-shared - 218. http://www.karlrunge.com/x11vnc/index.html#tunnelling - 219. http://www.karlrunge.com/x11vnc/index.html#faq-passwd - 220. http://www.karlrunge.com/x11vnc/index.html#faq-passwdfile - 221. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-allow - 222. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-localhost - 223. http://www.karlrunge.com/x11vnc/index.html#faq-tcp_wrappers - 224. http://www.karlrunge.com/x11vnc/index.html#faq-inetd - 225. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-listen - 226. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-allow - 227. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-localhost - 228. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-allow - 229. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-localhost - 230. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-listen - 231. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-allow - 232. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-localhost - 233. http://www.karlrunge.com/x11vnc/index.html#tunnelling + 188. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-overlay + 189. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id + 190. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-overlay + 191. http://www.karlrunge.com/x11vnc/index.html#faq-overlays + 192. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id + 193. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-sid + 194. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-display + 195. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noshm + 196. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-flipbyteorder + 197. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-auth + 198. http://www.karlrunge.com/x11vnc/index.html#xauth_pain + 199. http://www.karlrunge.com/x11vnc/index.html#faq-noshm + 200. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remote + 201. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-query + 202. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-forever + 203. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-bg + 204. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-clear_mods + 205. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-clear_keys + 206. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remote + 207. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-query + 208. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-gui + 209. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-storepasswd + 210. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-rfbauth + 211. http://www.karlrunge.com/x11vnc/index.html#faq-passwdfile + 212. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-viewpasswd + 213. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-passwd + 214. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-passwdfile + 215. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-rfbauth + 216. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-input + 217. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-forever + 218. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-shared + 219. http://www.karlrunge.com/x11vnc/index.html#tunnelling + 220. http://www.karlrunge.com/x11vnc/index.html#faq-passwd + 221. http://www.karlrunge.com/x11vnc/index.html#faq-passwdfile + 222. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-allow + 223. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-localhost + 224. http://www.karlrunge.com/x11vnc/index.html#faq-tcp_wrappers + 225. http://www.karlrunge.com/x11vnc/index.html#faq-inetd + 226. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-listen + 227. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-allow + 228. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-localhost + 229. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-allow + 230. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-localhost + 231. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-listen + 232. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-allow + 233. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-localhost 234. http://www.karlrunge.com/x11vnc/index.html#tunnelling - 235. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-localhost - 236. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-rfbauth - 237. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-passwdfile - 238. http://www.karlrunge.com/x11vnc/index.html#tunnelling - 239. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-connect - 240. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-accept - 241. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-viewonly - 242. ftp://ftp.x.org/ - 243. http://www.karlrunge.com/x11vnc/dtVncPopup - 244. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-gone - 245. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-localhost - 246. http://www.karlrunge.com/x11vnc/index.html#tunnelling - 247. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-accept - 248. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-users - 249. http://www.karlrunge.com/x11vnc/blockdpy.c - 250. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-accept - 251. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-gone + 235. http://www.karlrunge.com/x11vnc/index.html#tunnelling + 236. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-localhost + 237. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-rfbauth + 238. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-passwdfile + 239. http://www.karlrunge.com/x11vnc/index.html#tunnelling + 240. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-connect + 241. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-accept + 242. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-viewonly + 243. ftp://ftp.x.org/ + 244. http://www.karlrunge.com/x11vnc/dtVncPopup + 245. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-gone + 246. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-localhost + 247. http://www.karlrunge.com/x11vnc/index.html#tunnelling + 248. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-accept + 249. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-users + 250. http://www.karlrunge.com/x11vnc/blockdpy.c + 251. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-accept 252. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-gone - 253. http://www.karlrunge.com/x11vnc/index.html#display-manager-continuously - 254. http://www.karlrunge.com/x11vnc/index.html#faq-inetd - 255. http://www.karlrunge.com/x11vnc/index.html#x11vnc_loop - 256. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-auth - 257. http://www.karlrunge.com/x11vnc/index.html#dtlogin_solaris - 258. http://www.jirka.org/gdm-documentation/x241.html - 259. http://www.karlrunge.com/x11vnc/x11vnc_loop - 260. http://www.karlrunge.com/x11vnc/index.html#faq-xterminal-xauth - 261. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-inetd - 262. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-q - 263. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-auth - 264. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-httpdir - 265. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-connect - 266. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-vncconnect - 267. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-add_keysyms - 268. http://www.karlrunge.com/x11vnc/index.html#display-manager-continuously - 269. http://www.karlrunge.com/x11vnc/shm_clear - 270. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-onetile - 271. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noshm - 272. http://www.karlrunge.com/x11vnc/index.html#faq-noshm - 273. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nap - 274. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wait - 275. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-onetile - 276. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-fs - 277. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-threads - 278. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-defer - 279. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id - 280. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-solid - 281. http://www.tightvnc.com/ - 282. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nodragging - 283. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-progressive - 284. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-fs - 285. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wait - 286. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-defer - 287. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id - 288. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nosel - 289. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursor - 290. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorpos - 291. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-pointer_mode + 253. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-gone + 254. http://www.karlrunge.com/x11vnc/index.html#display-manager-continuously + 255. http://www.karlrunge.com/x11vnc/index.html#faq-inetd + 256. http://www.karlrunge.com/x11vnc/index.html#x11vnc_loop + 257. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-auth + 258. http://www.karlrunge.com/x11vnc/index.html#dtlogin_solaris + 259. http://www.jirka.org/gdm-documentation/x241.html + 260. http://www.karlrunge.com/x11vnc/x11vnc_loop + 261. http://www.karlrunge.com/x11vnc/index.html#faq-xterminal-xauth + 262. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-inetd + 263. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-q + 264. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-auth + 265. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-httpdir + 266. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-connect + 267. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-vncconnect + 268. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-add_keysyms + 269. http://www.karlrunge.com/x11vnc/index.html#display-manager-continuously + 270. http://www.karlrunge.com/x11vnc/shm_clear + 271. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-onetile + 272. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noshm + 273. http://www.karlrunge.com/x11vnc/index.html#faq-noshm + 274. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nap + 275. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wait + 276. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-onetile + 277. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-fs + 278. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-threads + 279. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-defer + 280. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id + 281. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-solid + 282. http://www.tightvnc.com/ + 283. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nodragging + 284. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-progressive + 285. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-fs + 286. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wait + 287. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-defer + 288. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id + 289. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nosel + 290. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursor + 291. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorpos 292. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-pointer_mode - 293. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nodragging - 294. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-pointer_mode - 295. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-threads - 296. http://www.karlrunge.com/x11vnc/index.html#faq-wireframe - 297. http://www.karlrunge.com/x11vnc/index.html#faq-pointer-mode - 298. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wireframe + 293. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-pointer_mode + 294. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nodragging + 295. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-pointer_mode + 296. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-threads + 297. http://www.karlrunge.com/x11vnc/index.html#faq-wireframe + 298. http://www.karlrunge.com/x11vnc/index.html#faq-pointer-mode 299. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wireframe 300. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wireframe - 301. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xd_area - 302. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xd_mem - 303. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noxdamage - 304. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-cursor + 301. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wireframe + 302. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xd_area + 303. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xd_mem + 304. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noxdamage 305. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-cursor - 306. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-overlay - 307. http://www.karlrunge.com/x11vnc/index.html#the-overlay-mode - 308. http://www.karlrunge.com/x11vnc/index.html#solaris10-build - 309. http://www.karlrunge.com/x11vnc/index.html#faq-xfixes-alpha-hacks - 310. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-alphacut - 311. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-alphafrac - 312. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-alpharemove - 313. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorshape - 314. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noalphablend - 315. http://www.tightvnc.com/ - 316. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursor - 317. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-cursorpos - 318. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorpos - 319. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorshape - 320. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-buttonmap - 321. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-debug_pointer - 322. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-buttonmap - 323. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-modtweak - 324. http://www.karlrunge.com/x11vnc/index.html#faq-greaterless - 325. http://www.karlrunge.com/x11vnc/index.html#faq-xkbmodtweak - 326. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-debug_keyboard - 327. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-modtweak - 328. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap - 329. http://www.karlrunge.com/x11vnc/index.html#faq-xkbmodtweak - 330. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-debug_keyboard - 331. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-modtweak - 332. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xkb - 333. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-skip_keycodes - 334. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap - 335. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-add_keysyms - 336. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap - 337. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-add_keysyms - 338. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-norepeat + 306. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-cursor + 307. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-overlay + 308. http://www.karlrunge.com/x11vnc/index.html#the-overlay-mode + 309. http://www.karlrunge.com/x11vnc/index.html#solaris10-build + 310. http://www.karlrunge.com/x11vnc/index.html#faq-xfixes-alpha-hacks + 311. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-alphacut + 312. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-alphafrac + 313. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-alpharemove + 314. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorshape + 315. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noalphablend + 316. http://www.tightvnc.com/ + 317. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursor + 318. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-cursorpos + 319. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorpos + 320. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorshape + 321. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-buttonmap + 322. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-debug_pointer + 323. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-buttonmap + 324. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-modtweak + 325. http://www.karlrunge.com/x11vnc/index.html#faq-greaterless + 326. http://www.karlrunge.com/x11vnc/index.html#faq-xkbmodtweak + 327. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-debug_keyboard + 328. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-modtweak + 329. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap + 330. http://www.karlrunge.com/x11vnc/index.html#faq-xkbmodtweak + 331. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-debug_keyboard + 332. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-modtweak + 333. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xkb + 334. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-skip_keycodes + 335. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap + 336. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-add_keysyms + 337. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap + 338. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-add_keysyms 339. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-norepeat - 340. http://www.karlrunge.com/x11vnc/index.html#faq-display-manager - 341. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap + 340. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-norepeat + 341. http://www.karlrunge.com/x11vnc/index.html#faq-display-manager 342. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap 343. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap 344. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap - 345. http://www.karlrunge.com/x11vnc/index.html#faq-scaling - 346. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scale - 347. http://www.cus.cam.ac.uk/~ssb22/source/vnc-magnification.html - 348. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-rfbport - 349. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scale_cursor - 350. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-blackout - 351. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xinerama - 352. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xwarppointer - 353. http://www.karlrunge.com/x11vnc/index.html#faq-solshm - 354. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-onetile - 355. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noshm - 356. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-clip - 357. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id + 345. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap + 346. http://www.karlrunge.com/x11vnc/index.html#faq-scaling + 347. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scale + 348. http://www.cus.cam.ac.uk/~ssb22/source/vnc-magnification.html + 349. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-rfbport + 350. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scale_cursor + 351. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-blackout + 352. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xinerama + 353. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xwarppointer + 354. http://www.karlrunge.com/x11vnc/index.html#faq-solshm + 355. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-onetile + 356. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noshm + 357. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-clip 358. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id - 359. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xrandr - 360. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-padgeom - 361. http://www.karlrunge.com/x11vnc/index.html#faq-linuxvc - 362. http://www.karlrunge.com/x11vnc/index.html#faq-rawfb - 363. http://www.karlrunge.com/x11vnc/index.html#faq-linuxvc - 364. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id - 365. http://www.karlrunge.com/x11vnc/index.html#faq-xvfb - 366. http://www.karlrunge.com/x11vnc/index.html#faq-linuxvc - 367. http://www.karlrunge.com/x11vnc/index.html#faq-vmware - 368. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nosel - 369. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noprimary - 370. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nobell - 371. mailto:xvml@karlrunge.com + 359. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id + 360. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xrandr + 361. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-padgeom + 362. http://www.karlrunge.com/x11vnc/index.html#faq-linuxvc + 363. http://www.karlrunge.com/x11vnc/index.html#faq-rawfb + 364. http://www.karlrunge.com/x11vnc/index.html#faq-linuxvc + 365. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id + 366. http://www.karlrunge.com/x11vnc/index.html#faq-xvfb + 367. http://www.karlrunge.com/x11vnc/index.html#faq-linuxvc + 368. http://www.karlrunge.com/x11vnc/index.html#faq-vmware + 369. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nosel + 370. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noprimary + 371. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nobell + 372. mailto:xvml@karlrunge.com ======================================================================= @@ -4233,7 +4238,7 @@ x11vnc: a VNC server for real X displays Here are all of x11vnc command line options: % x11vnc -opts (see below for -help long descriptions) -x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-05-02 +x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-05-14 x11vnc options: -display disp -auth file @@ -4260,43 +4265,49 @@ x11vnc options: -o logfile -flag file -rc filename -norc -h, -help -?, -opts - -V, -version -q - -bg -modtweak - -nomodtweak -xkb - -skip_keycodes string -add_keysyms - -clear_mods -clear_keys - -remap string -norepeat - -repeat -nofb - -nobell -nosel - -noprimary -cursor [mode] - -nocursor -arrow n - -noxfixes -alphacut n - -alphafrac fraction -alpharemove - -noalphablend -nocursorshape - -cursorpos -nocursorpos - -xwarppointer -buttonmap string - -nodragging -wireframe [str] - -nowireframe -wirecopyrect mode - -nowirecopyrect -scrollcopyrect mode + -V, -version -dbg + -q -bg + -modtweak -nomodtweak + -xkb -noxkb + -skip_keycodes string -skip_dups + -noskip_dups -add_keysyms + -noadd_keysyms -clear_mods + -clear_keys -remap string + -norepeat -repeat + -nofb -nobell + -nosel -noprimary + -cursor [mode] -nocursor + -arrow n -noxfixes + -alphacut n -alphafrac fraction + -alpharemove -noalphablend + -nocursorshape -cursorpos + -nocursorpos -xwarppointer + -buttonmap string -nodragging + -wireframe [str] -nowireframe + -wirecopyrect mode -nowirecopyrect + -debug_wireframe -scrollcopyrect mode -noscrollcopyrect -scr_area n - -pointer_mode n -input_skip n - -speeds rd,bw,lat -debug_pointer - -debug_keyboard -defer time - -wait time -nap + -scr_skip list -scr_inc list + -scr_keys list -scr_parms string + -debug_scroll -pointer_mode n + -input_skip n -speeds rd,bw,lat + -debug_pointer -debug_keyboard + -defer time -wait time + -readtimeout n -nap -nonap -sb time -noxdamage -xd_area A -xd_mem f -sigpipe string -threads -nothreads -fs f -gaps n -grow n -fuzz n - -snapfb -rawfb string - -pipeinput cmd -gui [gui-opts] - -remote command -query variable - -sync -noremote - -yesremote -unsafe - -safer -privremote - -nocmds -deny_all - + -debug_tiles -snapfb + -rawfb string -pipeinput cmd + -gui [gui-opts] -remote command + -query variable -sync + -noremote -yesremote + -unsafe -safer + -privremote -nocmds + -deny_all libvncserver options: -rfbport port TCP port for RFB protocol @@ -4322,7 +4333,7 @@ libvncserver options: % x11vnc -help -x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-05-02 +x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-05-14 Typical usage is: @@ -4795,6 +4806,9 @@ Options: -?, -opts Only list the x11vnc options. -V, -version Print program version and last modification date. +-dbg instead of exiting after cleaning up, run a simple + "debug crash shell" when fatal errors are trapped. + -q Be quiet by printing less informational output to stderr. Same as -quiet. -bg Go into the background after screen setup. Messages to @@ -4814,9 +4828,13 @@ Options: where a Keysym is bound to multiple keys (e.g. "<" + ">" and "," + "<" keys). Default: -modtweak -xkb When in modtweak mode, use the XKEYBOARD extension (if - the X display supports it) to do the modifier tweaking. +-noxkb the X display supports it) to do the modifier tweaking. This is powerful and should be tried if there are still keymapping problems when using -modtweak by itself. + The default is to check whether some common keysyms, + e.g. !, @, [, are only accessible via -xkb mode and if + so then automatically enable the mode. To disable this + automatic detection use -noxkb. -skip_keycodes string Ignore the comma separated list of decimal keycodes. Perhaps these are keycodes not on your keyboard but your X server thinks exist. Currently only applies @@ -4825,10 +4843,16 @@ Options: when ambiguities exist (more than one Keycode per Keysym). Run 'xmodmap -pk' to see your keymapping. Example: "-skip_keycodes 94,114" +-skip_dups Some VNC viewers send impossible repeated key events, +-noskip_dups e.g. key-down, key-down, key-up, key-up all for the + same key, or 20 downs in a row for the same key! + Setting -skip_dups means to skip these duplicates and + just process the first event. Default: -skip_dups -add_keysyms If a Keysym is received from a VNC viewer and - that Keysym does not exist in the X server, then +-noadd_keysyms that Keysym does not exist in the X server, then add the Keysym to the X server's keyboard mapping. - Added Keysyms will be removed when x11vnc exits. + Added Keysyms will be removed periodically and also + when x11vnc exits. Default: -add_keysyms -clear_mods At startup and exit clear the modifier keys by sending KeyRelease for each one. The Lock modifiers are skipped. Used to clear the state if the display was accidentally @@ -4845,14 +4869,53 @@ Options: To map a key to a button click, use the fake Keysyms "Button1", ..., etc. E.g: "-remap Super_R-Button2" (useful for pasting on a laptop) --norepeat Option -norepeat disables X server key auto repeat --repeat when VNC clients are connected. This works around a + + Dead keys: "dead" (or silent, mute) keys are keys that + do not produce a character but must be followed by a 2nd + keystroke. This is often used for accenting characters, + e.g. to put "'" on top of "a" by pressing the dead + key and then "a". Note that this interpretation + is not part of core X11, it is up to the toolkit or + application to decide how to react to the sequence. + The X11 names for these keysyms are "dead_grave", + "dead_acute", etc. However some VNC viewers send the + keysyms "grave", "acute" instead thereby disabling + the accenting. To work around this -remap can be used. + For example "-remap grave-dead_grave,acute-dead_acute" + As a convenience, "-remap DEAD" applies these remaps: + + g grave-dead_grave + a acute-dead_acute + c asciicircum-dead_circumflex + t asciitilde-dead_tilde + m macron-dead_macron + b breve-dead_breve + D abovedot-dead_abovedot + d diaeresis-dead_diaeresis + o degree-dead_abovering + A doubleacute-dead_doubleacute + r caron-dead_caron + e cedilla-dead_cedilla + + If you just want a subset use the first letter + label, e.g. "-remap DEAD=ga" to get the first two. + Additional remaps may also be supplied via commas, + e.g. "-remap DEAD=ga,Super_R-Button2". Finally, + "DEAD=missing" means to apply all of the above as + long as the left hand member is not already in the + X11 keymap. + +-norepeat Option -norepeat disables X server key auto repeat when +-repeat VNC clients are connected and VNC keyboard input is + not idle for more than 5 minutes. This works around a repeating keystrokes bug (triggered by long processing - delays between key down and key up client events: - either from large screen changes or high latency). + delays between key down and key up client events: either + from large screen changes or high latency). + Default: -norepeat + Note: your VNC viewer side will likely do autorepeating, so this is no loss unless someone is simultaneously at - the real X display. Default: -norepeat + the real X display. Use "-norepeat N" to set how many times norepeat will be reset if something else (e.g. X session manager) @@ -5082,6 +5145,10 @@ Options: so CopyRect is skipped when scaling unless you specify -wirecopyrect on the command line or by remote-control. +-debug_wireframe Turn on debugging info printout for the wireframe + heuristics. "-dwf" is an alias. Specify multiple + times for more output. + -scrollcopyrect mode Like -wirecopyrect, but use heuristics to try to guess -noscrollcopyrect if a window has scrolled its contents (either vertically or horizontally). This requires the RECORD X extension @@ -5098,12 +5165,13 @@ Options: it does there is a nice speedup from using the VNC CopyRect encoding (see -wirecopyrect). The speedup is both in reduced network traffic and reduced X - framebuffer polling/copying. On the other hand, - it may induce undesired transients (e.g. a terminal - cursor being scrolled up when it should not be) or other - painting errors. These are automatically repaired in a - short period of time. If this is unacceptable disable - the feature with -noscrollcopyrect. + framebuffer polling/copying. On the other hand, it may + induce undesired transients (e.g. a terminal cursor + being scrolled up when it should not be) or other + painting errors (window tearing, bunching-up, etc). + These are automatically repaired in a short period + of time. If this is unacceptable disable the feature + with -noscrollcopyrect. "mode" can be "never" (same as -noscrollcopyrect) to never try the copyrect, "keys" means to try it @@ -5125,6 +5193,102 @@ Options: it controlled. We want to be sure to skip the small scrollbar and get the large panel. Default: 60000 +-scr_skip list Skip scroll detection for applications matching + the comma separated list of strings in "list". + Some applications implement their scrolling in + strange ways where the XCopyArea, etc, also applies + to invisible portions of the window: if we CopyRect + those areas it looks awful during the scroll and + there may be painting errors left after the scroll. + Soffice.bin is the worst known offender. + + Use "##" to denote the start of the application class + (e.g. "##XTerm") and "++" to denote the start + of the application instance name (e.g. "++xterm"). + The string your list is matched against is of the form + "^^WM_NAME##Class++Instance<same-for-any-subwindows>" + The "xlsclients -la" command will provide this info. + + If a pattern is prefixed with "KEY:" it only applies + to Keystroke generated scrolls (e.g. Up arrow). If it + is prefixed with "MOUSE:" it only applies to Mouse + induced scrolls (e.g. dragging on a scrollbar). + Default: ##Soffice.bin + +-scr_inc list Opposite of -scr_skip: this list is consulted first + and if there is a match the window will be monitored + via RECORD for scrolls irrespective of -scr_skip. + Use -scr_skip '*' to skip anything that does not match + your -scr_inc. Use -scr_inc '*' to include everything. + +-scr_keys list For keystroke scroll detection, only apply the RECORD + heuristics to the comma separated list of keysyms in + "list". You may find the RECORD overhead for every + one of your keystrokes disrupts typing too much, but you + don't want to turn it off completely with "-scr mouse" + and -scr_parms does not work or is too confusing. + + The listed keysyms can be numeric or the keysym + names in the <X11/keysymdef.h> header file or from the + xev(1) program. Example: "-scr_keys Up,Down,Return". + One probably wants to have application specific lists + (e.g. for terminals, etc) but that is too icky to think + about for now... + + If "list" begins with the "-" character the list + is taken as an exclude list: all keysyms except those + list will be considered. The special string "builtin" + expands to an internal list of keysyms that are likely + to cause scrolls. BTW, by default modifier keys, + Shift_L, Control_R, etc, are skipped since they almost + never induce scrolling by themselves. + +-scr_parms string Set various parameters for the scrollcopyrect mode. + The format is similar to that for -wireframe and packed + with lots of parameters: + + Format: T+B+L+R,t1+t2+t3,s1+s2+s3+s4+s5 + Default: 0+64+32+32,0.02+0.10+0.9,0.03+0.06+0.5+0.1+5.0 + + If you leave nothing between commas: ",," the default + value is used. If you don't specify enough commas, + the trailing parameters are set to their defaults. + + "T+B+L+R" indicates four integers for how close in + pixels the pointer has to be from the Top, Bottom, Left, + or Right edges of the window to consider scrollcopyrect. + If -wireframe overlaps it takes precedence. This is a + speedup to quickly exclude a window from being watched + for scrollcopyrect: set them all to zero to not try + the speedup (things like selecting text will likely + be slower). + + "t1+t2+t3" specify three floating point times in + seconds that apply to scrollcopyrect detection with + *Keystroke* input: t1 is how long to wait after a key + is pressed for the first scroll, t2 is how long to keep + looking after a Keystroke scroll for more scrolls. + t3 is how frequently to try to update surrounding + scrollbars outside of the scrolling area (0.0 to + disable) + + "s1+s2+s3+s4+s5" specify five floating point times + in seconds that apply to scrollcopyrect detection with + *Mouse* input: s1 is how long to wait after a mouse + button is pressed for the first scroll, s2 is how long + to keep waiting for additional scrolls after the first + Mouse scroll was detected. s3 is how frequently to + try to update surrounding scrollbars outside of the + scrolling area (0.0 to disable). s4 is how long to + buffer pointer motion (to try to get fewer, bigger + mouse scrolls). s5 is the maximum time to spend just + updating the scroll window without updating the rest + of the screen. + +-debug_scroll Turn on debugging info printout for the scroll + heuristics. "-ds" is an alias. Specify it multiple + times for more output. + -pointer_mode n Various pointer motion update schemes. "-pm" is an alias. The problem is pointer motion can cause rapid changes on the screen: consider the rapid changes @@ -5135,7 +5299,7 @@ Options: or scrolling. So a scheme has to be used to "eat" much of that pointer input before re-polling the screen and sending out framebuffer updates. The mode number - "n" can be 0 to 5 and selects one of the schemes + "n" can be 0 to 4 and selects one of the schemes desribed below. n=0: does the same as -nodragging. (all screen polling @@ -5149,22 +5313,18 @@ Options: of input events it tries to detect if it should try to "eat" additional pointer events before continuing. - n=3 is basically the same as n=2 except with slightly - tweaked parameters. We made this a new one so one - could use -pm 2 for the old behavior. NOT FINISHED. - - n=4 is basically a dynamic -nodragging mode: it detects + n=3 is basically a dynamic -nodragging mode: it detects when the mouse motion has paused and then refreshes the display. - n=5 attempts to measures network rates and latency, + n=4 attempts to measures network rates and latency, the video card read rate, and how many tiles have been changed on the screen. From this, it aggressively tries to push screen "frames" when it decides it has enough resources to do so. NOT FINISHED. - The default n is 2. Note that modes 2, 3, 4, 5 will - skip -input_skip keyboard events (but it will not count + The default n is 2. Note that modes 2, 3, 4 will skip + -input_skip keyboard events (but it will not count pointer events). Also note that these modes are not available in -threads mode which has its own pointer event handling mechanism. @@ -5182,7 +5342,7 @@ Options: -speeds rd,bw,lat x11vnc tries to estimate some speed parameters that are used to optimize scheduling (e.g. -pointer_mode - 5) and other things. Use the -speeds option to set + 4) and other things. Use the -speeds option to set these manually. The triple "rd,bw,lat" corresponds to video h/w read rate in MB/sec, network bandwidth to clients in KB/sec, and network latency to clients in @@ -5214,6 +5374,10 @@ Options: (deferUpdateTime) Default: 30 -wait time Time in ms to pause between screen polls. Used to cut down on load. Default: 30 +-readtimeout n Set libvncserver rfbMaxClientWait to n seconds. On + slow links that take a long time to paint the first + screen libvncserver may hit the timeout and drop the + connection. Default: 20 seconds. -nap Monitor activity and if it is low take longer naps -nonap between screen polls to really cut down load when idle. Default: take naps @@ -5222,7 +5386,8 @@ Options: for about 1.5 secs). Use 0 to disable. Default: 60 -noxdamage Do not use the X DAMAGE extension to detect framebuffer - changes even if it is available. + changes even if it is available. Use -xdamage if your + default is to have it off. x11vnc's use of the DAMAGE extension: 1) significantly reduces the load when the screen is not changing much, @@ -5268,6 +5433,7 @@ Options: by checking the tile near the boundary. Default: 3 -fuzz n Tolerance in pixels to mark a tiles edges as changed. Default: 2 +-debug_tiles Print debugging output for tiles, fb updates, etc. -snapfb Instead of polling the X display framebuffer (fb) for changes, periodically copy all of X display fb into main @@ -5526,6 +5692,8 @@ Options: xkb enable -xkb modtweak mode. noxkb disable -xkb modtweak mode. skip_keycodes:str enable -xkb -skip_keycodes "str". + skip_dups enable -skip_dups mode. + noskip_dups disable -skip_dups mode. add_keysyms enable -add_keysyms mode. noadd_keysyms stop adding keysyms. those added will still be removed at exit. @@ -5580,6 +5748,10 @@ Options: " noscrollcopyrect disable -scrollcopyrect mode. "noscr" scr_area:n set -scr_area to n + scr_skip:list set -scr_skip to "list" + scr_inc:list set -scr_inc to "list" + scr_keys:list set -scr_keys to "list" + scr_parms:str set -scr_parms parameters. pointer_mode:n set -pointer_mode to n. same as "pm" input_skip:n set -input_skip to n. speeds:str set -speeds to str. @@ -5589,7 +5761,7 @@ Options: nodebug_keyboard disable -debug_keyboard, same as "nodk" defer:n set -defer to n ms,same as deferupdate:n wait:n set -wait to n ms. - rfbwait:n set -rfbwait (rfbMaxClientWait) to n ms. + readtimeout:n set read timeout to n seconds. nap enable -nap mode. nonap disable -nap mode. sb:n set -sb to n s, same as screen_blank:n @@ -5630,6 +5802,10 @@ Options: nodebug_wireframe disable debugging wireframe mechanism. debug_scroll enable debugging scrollcopy mechanism. nodebug_scroll disable debugging scrollcopy mechanism. + debug_tiles enable -debug_tiles + nodebug_tiles disable -debug_tiles + dbg enable -dbg crash shell + nodbg disable -dbg crash shell noremote disable the -remote command processing, it cannot be turned back on. @@ -5684,38 +5860,40 @@ Options: solid nosolid blackout xinerama noxinerama xtrap noxtrap xrandr noxrandr xrandr_mode padgeom quiet q noquiet modtweak nomodtweak xkb noxkb skip_keycodes - add_keysyms noadd_keysyms clear_mods noclear_mods - clear_keys noclear_keys remap repeat norepeat - fb nofb bell nobell sel nosel primary noprimary - cursorshape nocursorshape cursorpos nocursorpos cursor - show_cursor noshow_cursor nocursor arrow xfixes noxfixes - xdamage noxdamage xd_area xd_mem alphacut alphafrac - alpharemove noalpharemove alphablend noalphablend - xwarp xwarppointer noxwarp noxwarppointer buttonmap - dragging nodragging wireframe_mode wireframe wf - nowireframe nowf wirecopyrect wcr nowirecopyrect nowcr - scr_area scrollcopyrect scr noscrollcopyrect noscr - pointer_mode pm input_skip input client_input speeds - debug_pointer dp nodebug_pointer nodp debug_keyboard dk - nodebug_keyboard nodk deferupdate defer wait rfbwait - nap nonap sb screen_blank fs gaps grow fuzz snapfb - nosnapfb rawfb progressive rfbport http nohttp httpport + skip_dups noskip_dups add_keysyms noadd_keysyms + clear_mods noclear_mods clear_keys noclear_keys + remap repeat norepeat fb nofb bell nobell sel nosel + primary noprimary cursorshape nocursorshape cursorpos + nocursorpos cursor show_cursor noshow_cursor nocursor + arrow xfixes noxfixes xdamage noxdamage xd_area xd_mem + alphacut alphafrac alpharemove noalpharemove alphablend + noalphablend xwarppointer xwarp noxwarppointer noxwarp + buttonmap dragging nodragging wireframe_mode wireframe + wf nowireframe nowf wirecopyrect wcr nowirecopyrect + nowcr scr_area scr_skip scr_inc scr_keys scr_parms + scrollcopyrect scr noscrollcopyrect noscr pointer_mode + pm input_skip input client_input speeds debug_pointer dp + nodebug_pointer nodp debug_keyboard dk nodebug_keyboard + nodk deferupdate defer wait readtimeout nap nonap + sb screen_blank fs gaps grow fuzz snapfb nosnapfb + rawfb progressive rfbport http nohttp httpport httpdir enablehttpproxy noenablehttpproxy alwaysshared noalwaysshared nevershared noalwaysshared dontdisconnect - nodontdisconnect desktop noremote - - aro= debug_xevents nodebug_xevents debug_xevents - debug_xdamage nodebug_xdamage debug_xdamage - debug_wireframe nodebug_wireframe debug_wireframe - debug_scroll nodebug_scroll debug_scroll display - vncdisplay desktopname http_url auth users rootshift - clipshift scale_str scaled_x scaled_y scale_numer - scale_denom scale_fac scaling_blend scaling_nomult4 - scaling_pad scaling_interpolate inetd privremote - unsafe safer nocmds passwdfile using_shm logfile - o flag rc norc h help V version lastmod bg sigpipe - threads readrate netrate netlatency pipeinput clients - client_count pid ext_xtest ext_xtrap ext_xrecord + nodontdisconnect desktop debug_xevents nodebug_xevents + debug_xevents debug_xdamage nodebug_xdamage + debug_xdamage debug_wireframe nodebug_wireframe + debug_wireframe debug_scroll nodebug_scroll debug_scroll + debug_tiles dbt nodebug_tiles nodbt debug_tiles dbg + nodbg noremote + + aro= display vncdisplay desktopname http_url auth + users rootshift clipshift scale_str scaled_x scaled_y + scale_numer scale_denom scale_fac scaling_blend + scaling_nomult4 scaling_pad scaling_interpolate inetd + privremote unsafe safer nocmds passwdfile using_shm + logfile o flag rc norc h help V version lastmod bg + sigpipe threads readrate netrate netlatency pipeinput + clients client_count pid ext_xtest ext_xtrap ext_xrecord 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 @@ -5809,7 +5987,7 @@ These options are passed to libvncserver: Pretty wild huh? [1]Contact me if you have any questions or problems. Personally, I use: -x11vnc -rfbauth $HOME/.vnc/passwd -flashcmap -cursor X -add_keysyms +x11vnc -rfbauth $HOME/.vnc/passwd -flashcmap -cursor X -solid (the -flashcmap only matters on old 8-bit X displays) diff --git a/x11vnc/tkx11vnc b/x11vnc/tkx11vnc index 5128d52..2e05e53 100755 --- a/x11vnc/tkx11vnc +++ b/x11vnc/tkx11vnc @@ -132,6 +132,7 @@ Keyboard =D norepeat =D add_keysyms skip_keycodes: + skip_dups modtweak xkb -- @@ -180,6 +181,15 @@ Misc -- =RA remote-cmd: =GA all-settings + -- + =GAL Misc-Tuning:: + fs: + gaps: + grow: + fuzz: + readtimeout: + snapfb + threads Debugging debug_pointer @@ -191,8 +201,10 @@ Debugging -- debug_xevents debug_xdamage - debug_wireframe + =-C:0,1,2,3 debug_wireframe: debug_scroll + debug_tiles + dbg -- =GA show-start-cmd =DG debug_gui @@ -234,15 +246,24 @@ Tuning input_skip: =D nodragging -- + noshm + flipbyteorder + onetile + -- + xdamage + xd_area: + xd_mem: + -- wireframe wireframe_mode: =-C:never,top,always wirecopyrect: + -- =-C:never,keys,mouse,always scrollcopyrect: scr_area: - -- - noshm - flipbyteorder - onetile + scr_skip: + scr_inc: + scr_keys: + scr_parms: -- D speeds: =D wait: @@ -250,18 +271,6 @@ Tuning =D nap screen_blank: -- - fs: - gaps: - grow: - fuzz: - snapfb - -- - xdamage - xd_area: - xd_mem: - -- - threads - -- progressive: " } @@ -395,6 +404,11 @@ and one for power users. set helptext(all) $helpall + set helptext(Misc-Tuning:) " +These contain some miscellaneous tuning parameters that are not +often used. Placed here to avoid overcrowding in the Tuning menu. +" + set helptext(gui) " tkx11vnc is a simple frontend to x11vnc. Nothing fancy, it merely provides an interface to each of the many x11vnc command line options and @@ -701,7 +715,7 @@ proc help_win {item} { set text "Help on $item:\n\n" if {[is_gui_internal $item]} { - if {$item != "gui" && $item != "all"} { + if {$item != "gui" && $item != "all" && $item != "Misc-Tuning:"} { append text " + Is a gui internal Action (cannot be set).\n"; } } elseif {[is_action $item]} { @@ -1157,6 +1171,9 @@ proc push_new_value {item name new {query 1}} { if {$do_query_all} { set all [all_query_vars] set qargs [list "-Q" $all] + + global last_query_all_time + set last_query_all_time [clock seconds] } set rqargs [concat $rargs $qargs] @@ -1646,6 +1663,7 @@ proc all_query_vars {} { proc query_all {{quiet 0}} { global query_ans_list query_aro_list all_settings + global last_query_all_time set qry [all_query_vars] @@ -1663,6 +1681,7 @@ proc query_all {{quiet 0}} { append_text "Failed to retrieve settings.\n" } } + set last_query_all_time [clock seconds] return $all } @@ -1816,6 +1835,9 @@ proc do_action {item} { } elseif {$item == "tail-logfile"} { tail_logfile return + } elseif {$item == "Misc-Tuning:"} { + menu_help "$item" + return } elseif {$item == "stop+quit"} { push_new_value "stop" "stop" 1 0 set_connected no @@ -2481,6 +2503,29 @@ proc make_menu_items {} { } } +proc menu_posted {} { + global last_query_all_time query_all_freq + global connected_to_x11vnc + set now [clock seconds] + + + if {$connected_to_x11vnc} { + set quiet 0 + set refresh [expr "$last_query_all_time + $query_all_freq"] + + # puts "menu_posted $now $last_query_all_time" + # puts "menu_posted $refresh" + + if {$now > $refresh} { + append_text "Refreshing settings... " + query_all $quiet + if {$quiet} { + append_text "done\n" + } + } + } +} + proc make_widgets {} { global template global menu_b menu_m menu_count @@ -2557,7 +2602,7 @@ proc make_widgets {} { -anchor w -menu $menu -background $fbg \ -font $bfont pack $menub -side top -fill x - menu $menu -tearoff 0 + menu $menu -tearoff 0 -postcommand menu_posted } } pack $f -side top -fill x @@ -3032,6 +3077,7 @@ global bfont ffont old_labels global connected_to_x11vnc global delay_sleep extra_sleep extra_sleep_split global cache_all_query_vars +global last_query_all_time query_all_freq set unset_str "(unset)" set connected_to_x11vnc 0 @@ -3045,6 +3091,8 @@ set reply_xdisplay "" set all_settings "None so far." set always_update 1 set cache_all_query_vars "" +set query_all_freq 120 +set last_query_all_time [clock seconds] # these are no longer used under x11vnc -sync: set delay_sleep 350 diff --git a/x11vnc/tkx11vnc.h b/x11vnc/tkx11vnc.h index a92a212..bf9e26b 100644 --- a/x11vnc/tkx11vnc.h +++ b/x11vnc/tkx11vnc.h @@ -138,6 +138,7 @@ " =D norepeat\n" " =D add_keysyms\n" " skip_keycodes:\n" +" skip_dups\n" " modtweak\n" " xkb\n" " --\n" @@ -186,6 +187,15 @@ " --\n" " =RA remote-cmd:\n" " =GA all-settings\n" +" --\n" +" =GAL Misc-Tuning::\n" +" fs:\n" +" gaps:\n" +" grow:\n" +" fuzz:\n" +" readtimeout:\n" +" snapfb\n" +" threads\n" "\n" "Debugging\n" " debug_pointer\n" @@ -197,8 +207,10 @@ " --\n" " debug_xevents\n" " debug_xdamage\n" -" debug_wireframe\n" +" =-C:0,1,2,3 debug_wireframe:\n" " debug_scroll\n" +" debug_tiles\n" +" dbg\n" " --\n" " =GA show-start-cmd\n" " =DG debug_gui\n" @@ -240,15 +252,24 @@ " input_skip:\n" " =D nodragging\n" " --\n" +" noshm\n" +" flipbyteorder\n" +" onetile\n" +" --\n" +" xdamage\n" +" xd_area:\n" +" xd_mem:\n" +" --\n" " wireframe\n" " wireframe_mode:\n" " =-C:never,top,always wirecopyrect:\n" +" --\n" " =-C:never,keys,mouse,always scrollcopyrect:\n" " scr_area:\n" -" --\n" -" noshm\n" -" flipbyteorder\n" -" onetile\n" +" scr_skip:\n" +" scr_inc:\n" +" scr_keys:\n" +" scr_parms:\n" " -- D\n" " speeds:\n" " =D wait:\n" @@ -256,18 +277,6 @@ " =D nap\n" " screen_blank:\n" " --\n" -" fs:\n" -" gaps:\n" -" grow:\n" -" fuzz:\n" -" snapfb\n" -" --\n" -" xdamage\n" -" xd_area:\n" -" xd_mem:\n" -" --\n" -" threads\n" -" --\n" " progressive:\n" "\"\n" "}\n" @@ -401,6 +410,11 @@ "\n" " set helptext(all) $helpall\n" "\n" +" set helptext(Misc-Tuning:) \"\n" +"These contain some miscellaneous tuning parameters that are not\n" +"often used. Placed here to avoid overcrowding in the Tuning menu.\n" +"\"\n" +"\n" " set helptext(gui) \"\n" "tkx11vnc is a simple frontend to x11vnc. Nothing fancy, it merely\n" "provides an interface to each of the many x11vnc command line options and\n" @@ -707,7 +721,7 @@ " set text \"Help on $item:\\n\\n\"\n" "\n" " if {[is_gui_internal $item]} {\n" -" if {$item != \"gui\" && $item != \"all\"} {\n" +" if {$item != \"gui\" && $item != \"all\" && $item != \"Misc-Tuning:\"} {\n" " append text \" + Is a gui internal Action (cannot be set).\\n\";\n" " }\n" " } elseif {[is_action $item]} {\n" @@ -1163,6 +1177,9 @@ " if {$do_query_all} {\n" " set all [all_query_vars]\n" " set qargs [list \"-Q\" $all]\n" +"\n" +" global last_query_all_time\n" +" set last_query_all_time [clock seconds]\n" " }\n" "\n" " set rqargs [concat $rargs $qargs]\n" @@ -1652,6 +1669,7 @@ "\n" "proc query_all {{quiet 0}} {\n" " global query_ans_list query_aro_list all_settings\n" +" global last_query_all_time\n" "\n" " set qry [all_query_vars]\n" "\n" @@ -1669,6 +1687,7 @@ " append_text \"Failed to retrieve settings.\\n\"\n" " }\n" " }\n" +" set last_query_all_time [clock seconds]\n" " return $all\n" "}\n" "\n" @@ -1822,6 +1841,9 @@ " } elseif {$item == \"tail-logfile\"} {\n" " tail_logfile\n" " return\n" +" } elseif {$item == \"Misc-Tuning:\"} {\n" +" menu_help \"$item\"\n" +" return\n" " } elseif {$item == \"stop+quit\"} {\n" " push_new_value \"stop\" \"stop\" 1 0\n" " set_connected no\n" @@ -2487,6 +2509,29 @@ " }\n" "}\n" "\n" +"proc menu_posted {} {\n" +" global last_query_all_time query_all_freq\n" +" global connected_to_x11vnc\n" +" set now [clock seconds]\n" +"\n" +"\n" +" if {$connected_to_x11vnc} {\n" +" set quiet 0\n" +" set refresh [expr \"$last_query_all_time + $query_all_freq\"]\n" +"\n" +" # puts \"menu_posted $now $last_query_all_time\"\n" +" # puts \"menu_posted $refresh\"\n" +"\n" +" if {$now > $refresh} {\n" +" append_text \"Refreshing settings... \"\n" +" query_all $quiet\n" +" if {$quiet} {\n" +" append_text \"done\\n\"\n" +" }\n" +" }\n" +" }\n" +"}\n" +"\n" "proc make_widgets {} {\n" " global template \n" " global menu_b menu_m menu_count\n" @@ -2563,7 +2608,7 @@ " -anchor w -menu $menu -background $fbg \\\n" " -font $bfont\n" " pack $menub -side top -fill x\n" -" menu $menu -tearoff 0\n" +" menu $menu -tearoff 0 -postcommand menu_posted\n" " }\n" " }\n" " pack $f -side top -fill x\n" @@ -3038,6 +3083,7 @@ "global connected_to_x11vnc\n" "global delay_sleep extra_sleep extra_sleep_split\n" "global cache_all_query_vars\n" +"global last_query_all_time query_all_freq\n" "\n" "set unset_str \"(unset)\"\n" "set connected_to_x11vnc 0\n" @@ -3051,6 +3097,8 @@ "set all_settings \"None so far.\"\n" "set always_update 1\n" "set cache_all_query_vars \"\"\n" +"set query_all_freq 120\n" +"set last_query_all_time [clock seconds]\n" "\n" "# these are no longer used under x11vnc -sync:\n" "set delay_sleep 350\n" diff --git a/x11vnc/x11vnc.1 b/x11vnc/x11vnc.1 index 2086491..582f806 100644 --- a/x11vnc/x11vnc.1 +++ b/x11vnc/x11vnc.1 @@ -2,7 +2,7 @@ .TH X11VNC "1" "May 2005" "x11vnc " "User Commands" .SH NAME x11vnc - allow VNC connections to real X11 displays - version: 0.7.2, lastmod: 2005-05-02 + version: 0.7.2, lastmod: 2005-05-14 .SH SYNOPSIS .B x11vnc [OPTION]... @@ -647,6 +647,11 @@ Print this help text. .IP Print program version and last modification date. .PP +\fB-dbg\fR +.IP +instead of exiting after cleaning up, run a simple +"debug crash shell" when fatal errors are trapped. +.PP \fB-q\fR .IP Be quiet by printing less informational output to @@ -677,12 +682,16 @@ identical keyboards). Also useful in resolving cases where a Keysym is bound to multiple keys (e.g. "<" + ">" and "," + "<" keys). Default: \fB-modtweak\fR .PP -\fB-xkb\fR +\fB-xkb,\fR \fB-noxkb\fR .IP When in modtweak mode, use the XKEYBOARD extension (if the X display supports it) to do the modifier tweaking. This is powerful and should be tried if there are still keymapping problems when using \fB-modtweak\fR by itself. +The default is to check whether some common keysyms, +e.g. !, @, [, are only accessible via \fB-xkb\fR mode and if +so then automatically enable the mode. To disable this +automatic detection use \fB-noxkb.\fR .PP \fB-skip_keycodes\fR \fIstring\fR .IP @@ -695,12 +704,21 @@ when ambiguities exist (more than one Keycode per Keysym). Run 'xmodmap \fB-pk'\fR to see your keymapping. Example: "\fB-skip_keycodes\fR \fI94,114\fR" .PP -\fB-add_keysyms\fR +\fB-skip_dups,\fR \fB-noskip_dups\fR +.IP +Some VNC viewers send impossible repeated key events, +e.g. key-down, key-down, key-up, key-up all for the +same key, or 20 downs in a row for the same key! +Setting \fB-skip_dups\fR means to skip these duplicates and +just process the first event. Default: \fB-skip_dups\fR +.PP +\fB-add_keysyms,\fR \fB-noadd_keysyms\fR .IP If a Keysym is received from a VNC viewer and that Keysym does not exist in the X server, then add the Keysym to the X server's keyboard mapping. -Added Keysyms will be removed when x11vnc exits. +Added Keysyms will be removed periodically and also +when x11vnc exits. Default: \fB-add_keysyms\fR .PP \fB-clear_mods\fR .IP @@ -727,17 +745,57 @@ header file for a list of Keysym names, or use To map a key to a button click, use the fake Keysyms "Button1", ..., etc. E.g: "\fB-remap\fR \fISuper_R-Button2\fR" (useful for pasting on a laptop) +.IP +Dead keys: "dead" (or silent, mute) keys are keys that +do not produce a character but must be followed by a 2nd +keystroke. This is often used for accenting characters, +e.g. to put "'" on top of "a" by pressing the dead +key and then "a". Note that this interpretation +is not part of core X11, it is up to the toolkit or +application to decide how to react to the sequence. +The X11 names for these keysyms are "dead_grave", +"dead_acute", etc. However some VNC viewers send the +keysyms "grave", "acute" instead thereby disabling +the accenting. To work around this \fB-remap\fR can be used. +For example "\fB-remap\fR \fIgrave-dead_grave,acute-dead_acute\fR" +.IP +As a convenience, "\fB-remap\fR \fIDEAD\fR" applies these remaps: +.IP + g grave-dead_grave + a acute-dead_acute + c asciicircum-dead_circumflex + t asciitilde-dead_tilde + m macron-dead_macron + b breve-dead_breve + D abovedot-dead_abovedot + d diaeresis-dead_diaeresis + o degree-dead_abovering + A doubleacute-dead_doubleacute + r caron-dead_caron + e cedilla-dead_cedilla +.IP +.IP +If you just want a subset use the first letter +label, e.g. "\fB-remap\fR \fIDEAD=ga\fR" to get the first two. +Additional remaps may also be supplied via commas, +e.g. "\fB-remap\fR \fIDEAD=ga,Super_R-Button2\fR". Finally, +"DEAD=missing" means to apply all of the above as +long as the left hand member is not already in the +X11 keymap. .PP \fB-norepeat,\fR \fB-repeat\fR .IP -Option \fB-norepeat\fR disables X server key auto repeat -when VNC clients are connected. This works around a +Option \fB-norepeat\fR disables X server key auto repeat when +VNC clients are connected and VNC keyboard input is +not idle for more than 5 minutes. This works around a repeating keystrokes bug (triggered by long processing -delays between key down and key up client events: -either from large screen changes or high latency). +delays between key down and key up client events: either +from large screen changes or high latency). +Default: \fB-norepeat\fR +.IP Note: your VNC viewer side will likely do autorepeating, so this is no loss unless someone is simultaneously at -the real X display. Default: \fB-norepeat\fR +the real X display. .IP Use "\fB-norepeat\fR \fIN\fR" to set how many times norepeat will be reset if something else (e.g. X session manager) @@ -1018,6 +1076,12 @@ Note: there can be painting errors when using \fB-scale\fR so CopyRect is skipped when scaling unless you specify \fB-wirecopyrect\fR on the command line or by remote-control. .PP +\fB-debug_wireframe\fR +.IP +Turn on debugging info printout for the wireframe +heuristics. "\fB-dwf\fR" is an alias. Specify multiple +times for more output. +.PP \fB-scrollcopyrect\fR \fImode,\fR \fB-noscrollcopyrect\fR .IP Like \fB-wirecopyrect,\fR but use heuristics to try to guess @@ -1036,12 +1100,13 @@ This scheme will not always detect scrolls, but when it does there is a nice speedup from using the VNC CopyRect encoding (see \fB-wirecopyrect).\fR The speedup is both in reduced network traffic and reduced X -framebuffer polling/copying. On the other hand, -it may induce undesired transients (e.g. a terminal -cursor being scrolled up when it should not be) or other -painting errors. These are automatically repaired in a -short period of time. If this is unacceptable disable -the feature with \fB-noscrollcopyrect.\fR +framebuffer polling/copying. On the other hand, it may +induce undesired transients (e.g. a terminal cursor +being scrolled up when it should not be) or other +painting errors (window tearing, bunching-up, etc). +These are automatically repaired in a short period +of time. If this is unacceptable disable the feature +with \fB-noscrollcopyrect.\fR .IP "mode" can be "never" (same as \fB-noscrollcopyrect)\fR to never try the copyrect, "keys" means to try it @@ -1065,6 +1130,113 @@ scrollbar first and then shifted the large panel it controlled. We want to be sure to skip the small scrollbar and get the large panel. Default: 60000 .PP +\fB-scr_skip\fR \fIlist\fR +.IP +Skip scroll detection for applications matching +the comma separated list of strings in \fIlist\fR. +Some applications implement their scrolling in +strange ways where the XCopyArea, etc, also applies +to invisible portions of the window: if we CopyRect +those areas it looks awful during the scroll and +there may be painting errors left after the scroll. +Soffice.bin is the worst known offender. +.IP +Use "##" to denote the start of the application class +(e.g. "##XTerm") and "++" to denote the start +of the application instance name (e.g. "++xterm"). +The string your list is matched against is of the form +"^^WM_NAME##Class++Instance<same-for-any-subwindows>" +The "xlsclients \fB-la"\fR command will provide this info. +.IP +If a pattern is prefixed with "KEY:" it only applies +to Keystroke generated scrolls (e.g. Up arrow). If it +is prefixed with "MOUSE:" it only applies to Mouse +induced scrolls (e.g. dragging on a scrollbar). +Default: ##Soffice.bin +.PP +\fB-scr_inc\fR \fIlist\fR +.IP +Opposite of \fB-scr_skip:\fR this list is consulted first +and if there is a match the window will be monitored +via RECORD for scrolls irrespective of \fB-scr_skip.\fR +Use \fB-scr_skip\fR '*' to skip anything that does not match +your \fB-scr_inc.\fR Use \fB-scr_inc\fR '*' to include everything. +.PP +\fB-scr_keys\fR \fIlist\fR +.IP +For keystroke scroll detection, only apply the RECORD +heuristics to the comma separated list of keysyms in +\fIlist\fR. You may find the RECORD overhead for every +one of your keystrokes disrupts typing too much, but you +don't want to turn it off completely with "\fB-scr\fR \fImouse\fR" +and \fB-scr_parms\fR does not work or is too confusing. +.IP +The listed keysyms can be numeric or the keysym +names in the <X11/keysymdef.h> header file or from the +.IR xev (1) +program. Example: "\fB-scr_keys\fR \fIUp,Down,Return\fR". +One probably wants to have application specific lists +(e.g. for terminals, etc) but that is too icky to think +about for now... +.IP +If \fIlist\fR begins with the "-" character the list +is taken as an exclude list: all keysyms except those +list will be considered. The special string "builtin" +expands to an internal list of keysyms that are likely +to cause scrolls. BTW, by default modifier keys, +Shift_L, Control_R, etc, are skipped since they almost +never induce scrolling by themselves. +.PP +\fB-scr_parms\fR \fIstring\fR +.IP +Set various parameters for the scrollcopyrect mode. +The format is similar to that for \fB-wireframe\fR and packed +with lots of parameters: +.IP +Format: T+B+L+R,t1+t2+t3,s1+s2+s3+s4+s5 +Default: 0+64+32+32,0.02+0.10+0.9,0.03+0.06+0.5+0.1+5.0 +.IP +If you leave nothing between commas: ",," the default +value is used. If you don't specify enough commas, +the trailing parameters are set to their defaults. +.IP +"T+B+L+R" indicates four integers for how close in +pixels the pointer has to be from the Top, Bottom, Left, +or Right edges of the window to consider scrollcopyrect. +If \fB-wireframe\fR overlaps it takes precedence. This is a +speedup to quickly exclude a window from being watched +for scrollcopyrect: set them all to zero to not try +the speedup (things like selecting text will likely +be slower). +.IP +"t1+t2+t3" specify three floating point times in +seconds that apply to scrollcopyrect detection with +*Keystroke* input: t1 is how long to wait after a key +is pressed for the first scroll, t2 is how long to keep +looking after a Keystroke scroll for more scrolls. +t3 is how frequently to try to update surrounding +scrollbars outside of the scrolling area (0.0 to +disable) +.IP +"s1+s2+s3+s4+s5" specify five floating point times +in seconds that apply to scrollcopyrect detection with +*Mouse* input: s1 is how long to wait after a mouse +button is pressed for the first scroll, s2 is how long +to keep waiting for additional scrolls after the first +Mouse scroll was detected. s3 is how frequently to +try to update surrounding scrollbars outside of the +scrolling area (0.0 to disable). s4 is how long to +buffer pointer motion (to try to get fewer, bigger +mouse scrolls). s5 is the maximum time to spend just +updating the scroll window without updating the rest +of the screen. +.PP +\fB-debug_scroll\fR +.IP +Turn on debugging info printout for the scroll +heuristics. "\fB-ds\fR" is an alias. Specify it multiple +times for more output. +.PP \fB-pointer_mode\fR \fIn\fR .IP Various pointer motion update schemes. "\fB-pm\fR" is @@ -1077,7 +1249,7 @@ screen changes: everything will bog down when dragging or scrolling. So a scheme has to be used to "eat" much of that pointer input before re-polling the screen and sending out framebuffer updates. The mode number -\fIn\fR can be 0 to 5 and selects one of the schemes +\fIn\fR can be 0 to 4 and selects one of the schemes desribed below. .IP n=0: does the same as \fB-nodragging.\fR (all screen polling @@ -1091,22 +1263,18 @@ n=2 is an improved scheme: by watching the current rate of input events it tries to detect if it should try to "eat" additional pointer events before continuing. .IP -n=3 is basically the same as n=2 except with slightly -tweaked parameters. We made this a new one so one -could use \fB-pm\fR 2 for the old behavior. NOT FINISHED. -.IP -n=4 is basically a dynamic \fB-nodragging\fR mode: it detects +n=3 is basically a dynamic \fB-nodragging\fR mode: it detects when the mouse motion has paused and then refreshes the display. .IP -n=5 attempts to measures network rates and latency, +n=4 attempts to measures network rates and latency, the video card read rate, and how many tiles have been changed on the screen. From this, it aggressively tries to push screen "frames" when it decides it has enough resources to do so. NOT FINISHED. .IP -The default n is 2. Note that modes 2, 3, 4, 5 will -skip \fB-input_skip\fR keyboard events (but it will not count +The default n is 2. Note that modes 2, 3, 4 will skip +\fB-input_skip\fR keyboard events (but it will not count pointer events). Also note that these modes are not available in \fB-threads\fR mode which has its own pointer event handling mechanism. @@ -1128,7 +1296,7 @@ Default: 10 .IP x11vnc tries to estimate some speed parameters that are used to optimize scheduling (e.g. \fB-pointer_mode\fR -5) and other things. Use the \fB-speeds\fR option to set +4) and other things. Use the \fB-speeds\fR option to set these manually. The triple \fIrd,bw,lat\fR corresponds to video h/w read rate in MB/sec, network bandwidth to clients in KB/sec, and network latency to clients in @@ -1173,6 +1341,13 @@ Time in ms to wait for updates before sending to client Time in ms to pause between screen polls. Used to cut down on load. Default: 30 .PP +\fB-readtimeout\fR \fIn\fR +.IP +Set libvncserver rfbMaxClientWait to n seconds. On +slow links that take a long time to paint the first +screen libvncserver may hit the timeout and drop the +connection. Default: 20 seconds. +.PP \fB-nap\fR .IP Monitor activity and if it is low take longer naps @@ -1191,7 +1366,8 @@ for about 1.5 secs). Use 0 to disable. Default: 60 \fB-noxdamage\fR .IP Do not use the X DAMAGE extension to detect framebuffer -changes even if it is available. +changes even if it is available. Use \fB-xdamage\fR if your +default is to have it off. .IP x11vnc's use of the DAMAGE extension: 1) significantly reduces the load when the screen is not changing much, @@ -1259,6 +1435,10 @@ by checking the tile near the boundary. Default: 3 Tolerance in pixels to mark a tiles edges as changed. Default: 2 .PP +\fB-debug_tiles\fR +.IP +Print debugging output for tiles, fb updates, etc. +.PP \fB-snapfb\fR .IP Instead of polling the X display framebuffer (fb) for @@ -1436,22 +1616,22 @@ Please report reproducible bugs. The following \fB-remote/-R\fR commands are supported: .IP stop terminate the server, same as "quit" - "exit" or "shutdown". +"exit" or "shutdown". .IP ping see if the x11vnc server responds. - Return is: ans=ping:<xdisplay> +Return is: ans=ping:<xdisplay> .IP blacken try to push a black fb update to all - clients (due to timings a client - could miss it). Same as "zero", also - "zero:x1,y1,x2,y2" for a rectangle. +clients (due to timings a client +could miss it). Same as "zero", also +"zero:x1,y1,x2,y2" for a rectangle. .IP refresh send the entire fb to all clients. .IP reset recreate the fb, polling memory, etc. .IP id:windowid set \fB-id\fR window to "windowid". empty - or "root" to go back to root window +or "root" to go back to root window .IP sid:windowid set \fB-sid\fR window to "windowid" .IP @@ -1478,7 +1658,7 @@ nooverlay disable \fB-overlay\fR mode. overlay_cursor in \fB-overlay\fR mode, enable cursor drawing. .IP overlay_nocursor disable cursor drawing. same as - nooverlay_cursor. +nooverlay_cursor. .IP visual:vis set \fB-visual\fR to "vis" .IP @@ -1499,8 +1679,8 @@ forever enable \fB-forever\fR mode. 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. +currently no clients, exit unless one +connects in the next n secs. .IP http enable http client connections. .IP @@ -1511,24 +1691,24 @@ deny deny any new connections, same as "lock" nodeny allow new connections, same as "unlock" .IP connect:host do reverse connection to host, "host" - may be a comma separated list of hosts - or host:ports. See \fB-connect.\fR +may be a comma separated list of hosts +or host:ports. See \fB-connect.\fR .IP disconnect:host disconnect any clients from "host" - same as "close:host". Use host - "all" to close all current clients. - If you know the client internal hex ID, - e.g. 0x3 (returned by "\fB-query\fR \fIclients\fR" - and RFB_CLIENT_ID) you can use that too. +same as "close:host". Use host +"all" to close all current clients. +If you know the client internal hex ID, +e.g. 0x3 (returned by "\fB-query\fR \fIclients\fR" +and RFB_CLIENT_ID) you can use that too. .IP allowonce:host For the next connection only, allow - connection from "host". +connection from "host". .IP allow:hostlist set \fB-allow\fR list to (comma separated) - "hostlist". See \fB-allow\fR and \fB-localhost.\fR - Do not use with \fB-allow\fR /path/to/file - Use "+host" to add a single host, and - use "\fB-host\fR" to delete a single host +"hostlist". See \fB-allow\fR and \fB-localhost.\fR +Do not use with \fB-allow\fR /path/to/file +Use "+host" to add a single host, and +use "\fB-host\fR" to delete a single host .IP localhost enable \fB-localhost\fR mode .IP @@ -1543,9 +1723,9 @@ lookup disable \fB-nolookup\fR mode. input:str set \fB-input\fR to "str", empty to disable. .IP client_input:str set the K, M, B \fB-input\fR on a per-client - basis. select which client as for - disconnect, e.g. client_input:host:MB - or client_input:0x2:K +basis. select which client as for +disconnect, e.g. client_input:host:MB +or client_input:0x2:K .IP accept:cmd set \fB-accept\fR "cmd" (empty to disable). .IP @@ -1556,12 +1736,12 @@ noshm enable \fB-noshm\fR mode. shm disable \fB-noshm\fR mode (i.e. use shm). .IP flipbyteorder enable \fB-flipbyteorder\fR mode, you may need - to set noshm for this to do something. +to set noshm for this to do something. .IP noflipbyteorder disable \fB-flipbyteorder\fR mode. .IP onetile enable \fB-onetile\fR mode. (you may need to - set shm for this to do something) +set shm for this to do something) .IP noonetile disable \fB-onetile\fR mode. .IP @@ -1572,10 +1752,10 @@ nosolid disable \fB-solid\fR mode. solid_color:color set \fB-solid\fR color (and apply it). .IP blackout:str set \fB-blackout\fR "str" (empty to disable). - See \fB-blackout\fR for the form of "str" - (basically: WxH+X+Y,...) - Use "+WxH+X+Y" to append a single - rectangle use "-WxH+X+Y" to delete one +See \fB-blackout\fR for the form of "str" +(basically: WxH+X+Y,...) +Use "+WxH+X+Y" to append a single +rectangle use "-WxH+X+Y" to delete one .IP xinerama enable \fB-xinerama\fR mode. (if applicable) .IP @@ -1592,8 +1772,8 @@ noxrandr disable \fB-xrandr\fR mode. xrandr_mode:mode set the \fB-xrandr\fR mode to "mode". .IP padgeom:WxH set \fB-padgeom\fR to WxH (empty to disable) - If WxH is "force" or "do" the padded - geometry fb is immediately applied. +If WxH is "force" or "do" the padded +geometry fb is immediately applied. .IP quiet enable \fB-quiet\fR mode. .IP @@ -1609,10 +1789,14 @@ noxkb disable \fB-xkb\fR modtweak mode. .IP skip_keycodes:str enable \fB-xkb\fR \fB-skip_keycodes\fR "str". .IP +skip_dups enable \fB-skip_dups\fR mode. +.IP +noskip_dups disable \fB-skip_dups\fR mode. +.IP add_keysyms enable \fB-add_keysyms\fR mode. .IP noadd_keysyms stop adding keysyms. those added will - still be removed at exit. +still be removed at exit. .IP clear_mods enable \fB-clear_mods\fR mode and clear them. .IP @@ -1623,10 +1807,10 @@ clear_keys enable \fB-clear_keys\fR mode and clear them. noclear_keys disable \fB-clear_keys\fR mode. .IP remap:str set \fB-remap\fR "str" (empty to disable). - See \fB-remap\fR for the form of "str" - (basically: key1-key2,key3-key4,...) - Use "+key1-key2" to append a single - keymapping, use "-key1-key2" to delete. +See \fB-remap\fR for the form of "str" +(basically: key1-key2,key3-key4,...) +Use "+key1-key2" to append a single +keymapping, use "-key1-key2" to delete. .IP norepeat enable \fB-norepeat\fR mode. .IP @@ -1653,7 +1837,7 @@ cursor:mode enable \fB-cursor\fR "mode". show_cursor enable showing a cursor. .IP noshow_cursor disable showing a cursor. (same as - "nocursor") +"nocursor") .IP arrow:n set \fB-arrow\fR to alternate n. .IP @@ -1707,6 +1891,14 @@ noscrollcopyrect disable \fB-scrollcopyrect__mode_.\fR "noscr" .IP scr_area:n set \fB-scr_area\fR to n .IP +scr_skip:list set \fB-scr_skip\fR to "list" +.IP +scr_inc:list set \fB-scr_inc\fR to "list" +.IP +scr_keys:list set \fB-scr_keys\fR to "list" +.IP +scr_parms:str set \fB-scr_parms\fR parameters. +.IP pointer_mode:n set \fB-pointer_mode\fR to n. same as "pm" .IP input_skip:n set \fB-input_skip\fR to n. @@ -1725,7 +1917,7 @@ defer:n set \fB-defer\fR to n ms,same as deferupdate:n .IP wait:n set \fB-wait\fR to n ms. .IP -rfbwait:n set \fB-rfbwait\fR (rfbMaxClientWait) to n ms. +readtimeout:n set read timeout to n seconds. .IP nap enable \fB-nap\fR mode. .IP @@ -1756,7 +1948,7 @@ nosnapfb disable \fB-snapfb\fR mode. rawfb:str set \fB-rawfb\fR mode to "str". .IP progressive:n set libvncserver \fB-progressive\fR slice - height parameter to n. +height parameter to n. .IP desktop:str set \fB-desktop\fR name to str for new clients. .IP @@ -1773,17 +1965,17 @@ noenablehttpproxy disable \fB-enablehttpproxy\fR mode. alwaysshared enable \fB-alwaysshared\fR mode. .IP noalwaysshared disable \fB-alwaysshared\fR mode. - (may interfere with other options) +(may interfere with other options) .IP nevershared enable \fB-nevershared\fR mode. .IP nonevershared disable \fB-nevershared\fR mode. - (may interfere with other options) +(may interfere with other options) .IP dontdisconnect enable \fB-dontdisconnect\fR mode. .IP nodontdisconnect disable \fB-dontdisconnect\fR mode. - (may interfere with other options) +(may interfere with other options) .IP debug_xevents enable debugging X events. .IP @@ -1801,9 +1993,17 @@ debug_scroll enable debugging scrollcopy mechanism. .IP nodebug_scroll disable debugging scrollcopy mechanism. .IP +debug_tiles enable \fB-debug_tiles\fR +.IP +nodebug_tiles disable \fB-debug_tiles\fR +.IP +dbg enable \fB-dbg\fR crash shell +.IP +nodbg disable \fB-dbg\fR crash shell +.IP .IP noremote disable the \fB-remote\fR command processing, - it cannot be turned back on. +it cannot be turned back on. .IP .IP The @@ -1873,38 +2073,40 @@ noflipbyteorder onetile noonetile solid_color solid nosolid blackout xinerama noxinerama xtrap noxtrap xrandr noxrandr xrandr_mode padgeom quiet q noquiet modtweak nomodtweak xkb noxkb skip_keycodes -add_keysyms noadd_keysyms clear_mods noclear_mods -clear_keys noclear_keys remap repeat norepeat -fb nofb bell nobell sel nosel primary noprimary -cursorshape nocursorshape cursorpos nocursorpos cursor -show_cursor noshow_cursor nocursor arrow xfixes noxfixes -xdamage noxdamage xd_area xd_mem alphacut alphafrac -alpharemove noalpharemove alphablend noalphablend -xwarp xwarppointer noxwarp noxwarppointer buttonmap -dragging nodragging wireframe_mode wireframe wf -nowireframe nowf wirecopyrect wcr nowirecopyrect nowcr -scr_area scrollcopyrect scr noscrollcopyrect noscr -pointer_mode pm input_skip input client_input speeds -debug_pointer dp nodebug_pointer nodp debug_keyboard dk -nodebug_keyboard nodk deferupdate defer wait rfbwait -nap nonap sb screen_blank fs gaps grow fuzz snapfb -nosnapfb rawfb progressive rfbport http nohttp httpport +skip_dups noskip_dups add_keysyms noadd_keysyms +clear_mods noclear_mods clear_keys noclear_keys +remap repeat norepeat fb nofb bell nobell sel nosel +primary noprimary cursorshape nocursorshape cursorpos +nocursorpos cursor show_cursor noshow_cursor nocursor +arrow xfixes noxfixes xdamage noxdamage xd_area xd_mem +alphacut alphafrac alpharemove noalpharemove alphablend +noalphablend xwarppointer xwarp noxwarppointer noxwarp +buttonmap dragging nodragging wireframe_mode wireframe +wf nowireframe nowf wirecopyrect wcr nowirecopyrect +nowcr scr_area scr_skip scr_inc scr_keys scr_parms +scrollcopyrect scr noscrollcopyrect noscr pointer_mode +pm input_skip input client_input speeds debug_pointer dp +nodebug_pointer nodp debug_keyboard dk nodebug_keyboard +nodk deferupdate defer wait readtimeout nap nonap +sb screen_blank fs gaps grow fuzz snapfb nosnapfb +rawfb progressive rfbport http nohttp httpport httpdir enablehttpproxy noenablehttpproxy alwaysshared noalwaysshared nevershared noalwaysshared dontdisconnect -nodontdisconnect desktop noremote -.IP -aro= debug_xevents nodebug_xevents debug_xevents -debug_xdamage nodebug_xdamage debug_xdamage -debug_wireframe nodebug_wireframe debug_wireframe -debug_scroll nodebug_scroll debug_scroll display -vncdisplay desktopname http_url auth users rootshift -clipshift scale_str scaled_x scaled_y scale_numer -scale_denom scale_fac scaling_blend scaling_nomult4 -scaling_pad scaling_interpolate inetd privremote -unsafe safer nocmds passwdfile using_shm logfile -o flag rc norc h help V version lastmod bg sigpipe -threads readrate netrate netlatency pipeinput clients -client_count pid ext_xtest ext_xtrap ext_xrecord +nodontdisconnect desktop debug_xevents nodebug_xevents +debug_xevents debug_xdamage nodebug_xdamage +debug_xdamage debug_wireframe nodebug_wireframe +debug_wireframe debug_scroll nodebug_scroll debug_scroll +debug_tiles dbt nodebug_tiles nodbt debug_tiles dbg +nodbg noremote +.IP +aro= display vncdisplay desktopname http_url auth +users rootshift clipshift scale_str scaled_x scaled_y +scale_numer scale_denom scale_fac scaling_blend +scaling_nomult4 scaling_pad scaling_interpolate inetd +privremote unsafe safer nocmds passwdfile using_shm +logfile o flag rc norc h help V version lastmod bg +sigpipe threads readrate netrate netlatency pipeinput +clients client_count pid ext_xtest ext_xtrap ext_xrecord 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 diff --git a/x11vnc/x11vnc.c b/x11vnc/x11vnc.c index 80eef99..c991dda 100644 --- a/x11vnc/x11vnc.c +++ b/x11vnc/x11vnc.c @@ -127,11 +127,6 @@ /* -- x11vnc.h -- */ -/* TODO: autoconf */ -#if 1 -#define LIBVNCSERVER_HAVE_RECORD 1 -#endif - /****************************************************************************/ /* Standard includes and libvncserver */ @@ -164,6 +159,7 @@ * -DSHARED to have the vnc display shared by default. * -DFOREVER to have -forever on by default. * -DNOREPEAT=0 to have -repeat on by default. + * -DADDKEYSYMS=0 to have -noadd_keysyms the default. * * -DREMOTE_DEFAULT=0 to disable remote-control on by default (-yesremote). * -DREMOTE_CONTROL=0 to disable remote-control mechanism completely. @@ -177,7 +173,7 @@ * -DSCROLL_COPYRECT=0 to have -noscrollcopyrect as the default. * -DXDAMAGE=0 to have -noxdamage as the default. * - * -DPOINTER_MODE_DEFAULT={0,1,2,3,4,5} set default -pointer_mode. + * -DPOINTER_MODE_DEFAULT={0,1,2,3,4} set default -pointer_mode. * -DBOLDLY_CLOSE_DISPLAY=0 to not close X DISPLAY under -rawfb. * -DSMALL_FOOTPRINT=1 for smaller binary size (no help, no gui, etc) * use 2 or 3 for even smaller footprint. @@ -367,11 +363,11 @@ Damage xdamage = 0; int xdamage_base_event_type = 0; int xdamage_max_area = 20000; /* pixels */ double xdamage_memory = 1.0; /* in units of NSCAN */ -int xdamage_tile_count; +int xdamage_tile_count, xdamage_direct_count; /* date +'lastmod: %Y-%m-%d' */ -char lastmod[] = "0.7.2 lastmod: 2005-05-02"; +char lastmod[] = "0.7.2 lastmod: 2005-05-14"; int hack_val = 0; /* X display info */ @@ -480,7 +476,6 @@ int scaling_cursor_blend = 1; int scaling_cursor_interpolate = 0; int scale_cursor_numer = 0, scale_cursor_denom = 0; - /* size of the basic tile unit that is polled for changes: */ int tile_x = 32; int tile_y = 32; @@ -491,8 +486,11 @@ unsigned char *tile_has_diff, *tile_tried, *tile_copied; unsigned char *tile_has_xdamage_diff, *tile_row_has_xdamage_diff; /* times of recent events */ -time_t last_event, last_input, last_client = 0; +time_t last_event, last_input = 0, last_client = 0; +time_t last_keyboard_input = 0, last_pointer_input = 0; +double last_keyboard_time = 0.0; double servertime_diff = 0.0; +double x11vnc_start = 0.0; /* last client to move pointer */ rfbClientPtr last_pointer_client = NULL; @@ -508,7 +506,7 @@ int got_user_input = 0; int got_pointer_input = 0; int got_keyboard_input = 0; int urgent_update = 0; -int last_keyboard_input = 0; +int last_keyboard_keycode = 0; rfbKeySym last_keysym = 0; int fb_copy_in_progress = 0; int drag_in_progress = 0; @@ -517,6 +515,7 @@ int do_copy_screen = 0; time_t damage_time = 0; int damage_delay = 0; +int program_pid = 0; char *program_name = NULL; char *program_cmdline = NULL; @@ -542,7 +541,8 @@ int all_clients_initialized(void); void close_all_clients(void); void close_clients(char *); int get_autorepeat_state(void); -void autorepeat(int restore); +int get_initial_autorepeat_state(void); +void autorepeat(int restore, int quiet); char *bitprint(unsigned int, int); void blackout_tiles(void); void solid_bg(int); @@ -559,10 +559,13 @@ void install_fake_fb(int, int, int); void remove_fake_fb(void); int add_keysym(KeySym); -void delete_keycode(KeyCode); -void delete_added_keycodes(void); +void delete_keycode(KeyCode, int); +void delete_added_keycodes(int); +int count_added_keycodes(void); double dtime(double *); +double dtime0(double *); +double dnow(void); void initialize_blackouts(char *); void initialize_blackouts_and_xinerama(void); @@ -584,7 +587,7 @@ void initialize_watch_bell(void); void initialize_xinerama(void); void initialize_xfixes(void); void initialize_xdamage(void); -int valid_window(Window, XWindowAttributes *); +int valid_window(Window, XWindowAttributes *, int); void create_xdamage_if_needed(void); void destroy_xdamage_if_needed(void); void initialize_xrandr(void); @@ -620,20 +623,27 @@ void pointer(int mask, int x, int y, rfbClientPtr client); void pipe_pointer(int mask, int x, int y, rfbClientPtr client); int check_pipeinput(void); void cursor_position(int, int); +void do_button_mask_change(int, int); void parse_wireframe(void); void parse_scroll_copyrect(void); void set_wirecopyrect_mode(char *); void set_scrollcopyrect_mode(char *); -int try_copyrect(Window, int, int, int, int, int, int, int *); +void initialize_scroll_matches(void); +void initialize_scroll_keys(void); +int try_copyrect(Window, int, int, int, int, int, int, int *, sraRegionPtr, + double); void do_copyregion(sraRegionPtr, int, int); int direct_fb_copy(int, int, int, int, int); -int get_wm_frame_pos(int *, int *, int *, int *, int *, int *, Window *); +int get_wm_frame_pos(int *, int *, int *, int *, int *, int *, + Window *, Window *); +Window descend_pointer(int, Window, char *, int); int near_wm_edge(int, int, int, int, int, int); int near_scrollbar_edge(int, int, int, int, int, int); void read_vnc_connect_prop(void); void set_vnc_connect_prop(char *); void fb_push(void); +void fb_push_wait(double); char *process_remote_cmd(char *, int); void rfbPE(long); void rfbCFD(long); @@ -656,6 +666,7 @@ int get_xfixes_cursor(int); void disable_cursor_shape_updates(rfbScreenInfoPtr); void restore_cursor_shape_updates(rfbScreenInfoPtr); int new_fb_size_clients(rfbScreenInfoPtr); +void get_client_regions(int *, int *, int *, int *); void shm_clean(XShmSegmentInfo *, XImage *); void shm_delete(XShmSegmentInfo *); @@ -668,6 +679,10 @@ void set_vnc_desktop_name(void); char *short_kmb(char *); +char **create_str_list(char *); +int match_str_list(char *, char **); + +int link_rate(int *, int *); int get_cmp_rate(void); int get_raw_rate(void); int get_read_rate(void); @@ -715,6 +730,8 @@ int use_solid_bg = 0; /* -solid */ char *solid_str = NULL; char *solid_default = "cyan4"; +#define LATENCY0 20 /* 20ms */ +#define NETRATE0 20 /* 20KB/sec */ char *speeds_str = NULL; /* -speeds TBD */ int measure_speeds = 1; int speeds_net_rate = 0; @@ -723,6 +740,14 @@ int speeds_net_latency = 0; int speeds_net_latency_measured = 0; int speeds_read_rate = 0; int speeds_read_rate_measured = 0; +enum { + LR_UNSET = 0, + LR_UNKNOWN, + LR_DIALUP, + LR_BROADBAND, + LR_LAN +}; + char *rc_rcfile = NULL; /* -rc */ int rc_norc = 0; @@ -790,7 +815,7 @@ int subwin_wait_mapped = 0; int debug_xevents = 0; /* -R debug_xevents:1 */ int debug_xdamage = 0; /* -R debug_xdamage:1 or 2 ... */ int debug_wireframe = 0; -int debug_wireframe2 = 0; +int debug_tiles = 0; int xtrap_input = 0; /* -xtrap for user input insertion */ int xinerama = 0; /* -xinerama */ @@ -840,12 +865,30 @@ char *wireframe_copyrect_default = "always"; char *wireframe_copyrect_default = "never"; #endif int wireframe_in_progress = 0; -Window *stack_list = NULL; -int stack_num = 0; + +typedef struct winattr { + Window win; + int fetched; + int valid; + int x, y; + int width, height; + int depth; + int class; + int backing_store; + int map_state; + int rx, ry; + double time; +} winattr_t; +winattr_t *stack_list = NULL; +int stack_list_len = 0; +int stack_list_num = 0; +winattr_t *stack_clip = NULL; +int stack_clip_len = 0; +int stack_clip_num = 0; /* T+B+L+R,tkey+presist_key,tmouse+persist_mouse */ #ifndef SCROLL_COPYRECT_PARMS -#define SCROLL_COPYRECT_PARMS "0+64+32+32,0.02+0.4,0.08+0.4" +#define SCROLL_COPYRECT_PARMS "0+64+32+32,0.02+0.10+0.9,0.03+0.06+0.5+0.1+5.0" #endif char *scroll_copyrect_str = NULL; #ifndef SCROLL_COPYRECT @@ -861,8 +904,43 @@ char *scroll_copyrect_default = "keys"; #else char *scroll_copyrect_default = "never"; #endif +char *scroll_key_list_str = NULL; +KeySym *scroll_key_list = NULL; +int pointer_queued_sent = 0; + int scrollcopyrect_min_area = 60000; /* minimum rectangle area */ int debug_scroll = 0; +double pointer_flush_delay = 0.0; +double last_scroll_event = 0.0; +int max_scroll_keyrate = 0; +enum scroll_types { + SCR_NONE = 0, + SCR_MOUSE, + SCR_KEY, + SCR_FAIL, + SCR_SUCCESS +}; +int last_scroll_type = SCR_NONE; + +char **scroll_good_all = NULL; +char **scroll_good_key = NULL; +char **scroll_good_mouse = NULL; +char *scroll_good_str = NULL; +char *scroll_good_str0 = +/* "##Firefox-bin," */ +/* "##Gnome-terminal," */ +/* "##XTerm", */ + "##Nomatch" +; + +char **scroll_skip_all = NULL; +char **scroll_skip_key = NULL; +char **scroll_skip_mouse = NULL; +char *scroll_skip_str = NULL; +char *scroll_skip_str0 = +/* "##Konsole," * no problems, known heuristics do not work */ + "##Soffice.bin" /* big problems, no clips, scrolls outside area */ +; #ifndef NOREPEAT #define NOREPEAT 1 @@ -873,8 +951,15 @@ int watch_bell = 1; /* watch for the bell using XKEYBOARD */ int sound_bell = 1; /* actually send it */ int xkbcompat = 0; /* ignore XKEYBOARD extension */ int use_xkb_modtweak = 0; /* -xkb */ +#ifndef SKIPDUPS +#define SKIPDUPS 1 +#endif +int skip_duplicate_key_events = SKIPDUPS; char *skip_keycodes = NULL; -int add_keysyms = 0; /* automatically add keysyms to X server */ +#ifndef ADDKEYSYMS +#define ADDKEYSYMS 1 +#endif +int add_keysyms = ADDKEYSYMS; /* automatically add keysyms to X server */ char *remap_file = NULL; /* -remap */ char *pointer_remap = NULL; @@ -884,7 +969,7 @@ char *pointer_remap = NULL; #endif #define POINTER_MODE_NOFB 2 int pointer_mode = POINTER_MODE_DEFAULT; -int pointer_mode_max = 5; +int pointer_mode_max = 4; int single_copytile = 0; /* use the old way copy_tiles() */ int single_copytile_orig = 0; int single_copytile_count = 0; @@ -948,6 +1033,8 @@ int got_pointer_mode = -1; int got_noviewonly = 0; int got_wirecopyrect = 0; int got_scrollcopyrect = 0; +int got_noxkb = 0; +int got_nomodtweak = 0; /* threaded vs. non-threaded (default) */ #if LIBVNCSERVER_X11VNC_THREADED && ! defined(X11VNC_THREADED) @@ -2494,9 +2581,9 @@ void XTestFakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, down ? "down":"up"); } if (down) { - last_keyboard_input = -key; + last_keyboard_keycode = -key; } else { - last_keyboard_input = key; + last_keyboard_keycode = key; } if (xtrap_input) { @@ -2507,6 +2594,10 @@ void XTestFakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, DEBUG_SKIPPED_INPUT(debug_keyboard, "keyboard: no-XTEST"); return; } + if (debug_keyboard) { + rfbLog("calling XTestFakeKeyEvent(%d, %d) %.4f\n", + key, down, dnow() - x11vnc_start); + } #if LIBVNCSERVER_HAVE_XTEST XTestFakeKeyEvent(dpy, key, down, delay); #endif @@ -2538,6 +2629,10 @@ void XTestFakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, DEBUG_SKIPPED_INPUT(debug_keyboard, "button: no-XTEST"); return; } + if (debug_pointer) { + rfbLog("calling XTestFakeButtonEvent(%d, %d) %.4f\n", + button, is_press, dnow() - x11vnc_start); + } #if LIBVNCSERVER_HAVE_XTEST XTestFakeButtonEvent(dpy, button, is_press, delay); #endif @@ -2564,6 +2659,10 @@ void XTestFakeMotionEvent_wr(Display* dpy, int screen, int x, int y, return XTRAP_FakeMotionEvent_wr(dpy, screen, x, y, delay); } + if (debug_pointer) { + rfbLog("calling XTestFakeMotionEvent(%d, %d) %.4f\n", + x, y, dnow() - x11vnc_start); + } #if LIBVNCSERVER_HAVE_XTEST XTestFakeMotionEvent(dpy, screen, x, y, delay); #endif @@ -2666,20 +2765,20 @@ int XTRAP_GrabControl_wr(Display *dpy, Bool impervious) { return 0; } -void disable_grabserver(Display *dpy) { +void disable_grabserver(Display *in_dpy) { int ok = 0; static int didmsg = 0; if (! xtrap_input) { - if (XTestGrabControl_wr(dpy, True)) { - XTRAP_GrabControl_wr(dpy, False); + if (XTestGrabControl_wr(in_dpy, True)) { + XTRAP_GrabControl_wr(in_dpy, False); if (! didmsg) { rfbLog("GrabServer control via XTEST.\n"); didmsg = 1; } ok = 1; } else { - if (XTRAP_GrabControl_wr(dpy, True)) { + if (XTRAP_GrabControl_wr(in_dpy, True)) { ok = 1; if (! didmsg) { rfbLog("Using DEC-XTRAP for protection" @@ -2689,15 +2788,15 @@ void disable_grabserver(Display *dpy) { } } } else { - if (XTRAP_GrabControl_wr(dpy, True)) { - XTestGrabControl_wr(dpy, False); + if (XTRAP_GrabControl_wr(in_dpy, True)) { + XTestGrabControl_wr(in_dpy, False); if (! didmsg) { rfbLog("GrabServer control via DEC-XTRAP.\n"); didmsg = 1; } ok = 1; } else { - if (XTestGrabControl_wr(dpy, True)) { + if (XTestGrabControl_wr(in_dpy, True)) { ok = 1; if (! didmsg) { rfbLog("DEC-XTRAP XGrabServer " @@ -2735,6 +2834,7 @@ int xrecord_set_by_keys = 0; int xrecord_set_by_mouse = 0; Window xrecord_focus_window = None; Window xrecord_wm_window = None; +Window xrecord_ptr_window = None; void initialize_xrecord(void) { use_xrecord = 0; @@ -2771,8 +2871,39 @@ void initialize_xrecord(void) { int xrecord_skip_keysym(rfbKeySym keysym) { KeySym sym = (KeySym) keysym; + int ok = -1, matched = 0; - if (IsModifierKey(sym)) { + if (scroll_key_list) { + int k, exclude = 0; + if (scroll_key_list[0]) { + exclude = 1; + } + k = 1; + while (scroll_key_list[k] != NoSymbol) { + if (scroll_key_list[k++] == sym) { + matched = 1; + break; + } + } + if (exclude) { + if (matched) { + return 1; + } else { + ok = 1; + } + } else { + if (matched) { + ok = 1; + } else { + ok = 0; + } + } + } + if (ok == 1) { + return 0; + } else if (ok == 0) { + return 1; + } else if (IsModifierKey(sym)) { return 1; } return 0; @@ -2782,6 +2913,29 @@ int xrecord_skip_button(int new, int old) { return 0; } +int xrecord_vi_scroll_keysym(rfbKeySym keysym) { + KeySym sym = (KeySym) keysym; + if (sym == XK_J || sym == XK_j || sym == XK_K || sym == XK_k) { + return 1; /* vi */ + } + if (sym == XK_D || sym == XK_d || sym == XK_U || sym == XK_u) { + return 1; /* Ctrl-d/u */ + } + if (sym == XK_Z || sym == XK_z) { + return 1; /* zz, zt, zb .. */ + } + return 0; +} + +int xrecord_emacs_scroll_keysym(rfbKeySym keysym) { + KeySym sym = (KeySym) keysym; + if (sym == XK_N || sym == XK_n || sym == XK_P || sym == XK_p) { + return 1; /* emacs */ + } + /* Must be some more ... */ + return 0; +} + int xrecord_scroll_keysym(rfbKeySym keysym) { KeySym sym = (KeySym) keysym; /* X11/keysymdef.h */ @@ -2796,19 +2950,88 @@ int xrecord_scroll_keysym(rfbKeySym keysym) { sym == XK_KP_Right) { return 1; /* L/R arrows */ } - if (sym == XK_J || sym == XK_j || sym == XK_K || sym == XK_k) { - return 1; /* vi */ + if (xrecord_vi_scroll_keysym(keysym)) { + return 1; } - if (sym == XK_N || sym == XK_n || sym == XK_P || sym == XK_p) { - return 1; /* emacs */ + if (xrecord_emacs_scroll_keysym(keysym)) { + return 1; } return 0; } +#define SCR_ATTR_CACHE 8 +winattr_t scr_attr_cache[SCR_ATTR_CACHE]; +double attr_cache_max_age = 1.5; + +int lookup_attr_cache(Window win, int *cache_index, int *next_index) { + double dnow, t, oldest; + int i, old_index = -1, count = 0; + Window cwin; + + *cache_index = -1; + *next_index = -1; + + if (win == None) { + return 0; + } + if (attr_cache_max_age == 0.0) { + return 0; + } + + dtime0(&dnow); + for (i=0; i < SCR_ATTR_CACHE; i++) { + + cwin = scr_attr_cache[i].win; + t = scr_attr_cache[i].time; + + if (dnow > t + attr_cache_max_age) { + /* expire it even if it is the one we want */ + scr_attr_cache[i].win = cwin = None; + scr_attr_cache[i].fetched = 0; + scr_attr_cache[i].valid = 0; + } + + if (*next_index == -1 && cwin == None) { + *next_index = i; + } + if (*next_index == -1) { + /* record oldest */ + if (old_index == -1 || t < oldest) { + oldest = t; + old_index = i; + } + } + if (cwin != None) { + count++; + } + if (cwin == win) { + if (*cache_index == -1) { + *cache_index = i; + } else { + /* remove dups */ + scr_attr_cache[i].win = None; + scr_attr_cache[i].fetched = 0; + scr_attr_cache[i].valid = 0; + } + } + } + if (*next_index == -1) { + *next_index = old_index; + } + +if (0) fprintf(stderr, "lookup_attr_cache count: %d\n", count); + if (*cache_index != -1) { + return 1; + } else { + return 0; + } +} + typedef struct scroll_event { Window win, frame; int dx, dy; - int x, y, w, h, t; + int x, y, w, h; + double t; int win_x, win_y, win_w, win_h; int new_x, new_y, new_w, new_h; } scroll_event_t; @@ -2829,8 +3052,7 @@ void record_CA(XPointer ptr, XRecordInterceptData *rec_data) { int good = 1, dx, dy, k=0, i; unsigned int w, h; int dba = 0, db = debug_scroll; - -//dba = 1; + int cache_index, next_index, valid; if (dba || db) { if (rec_data->category == XRecordFromClient) { @@ -2842,7 +3064,7 @@ void record_CA(XPointer ptr, XRecordInterceptData *rec_data) { } } -if (dba || db) fprintf(stderr, "record_CA-%d id_base: 0x%lx ptr: 0x%lx " +if (dba || db > 1) fprintf(stderr, "record_CA-%d id_base: 0x%lx ptr: 0x%lx " "seq: 0x%lx rc: 0x%lx cat: %d swapped: %d 0x%lx/0x%lx\n", k++, rec_data->id_base, (unsigned long) ptr, xrecord_seq, rc_scroll, rec_data->category, rec_data->client_swapped, src, dst); @@ -2850,29 +3072,29 @@ if (dba || db) fprintf(stderr, "record_CA-%d id_base: 0x%lx ptr: 0x%lx " if (! xrecording) { return; } -if (db) fprintf(stderr, "record_CA-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); if (rec_data->id_base == 0) { return; } -if (db) fprintf(stderr, "record_CA-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); if ((XID) ptr != xrecord_seq) { return; } -if (db) fprintf(stderr, "record_CA-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); if (rec_data->category != XRecordFromClient) { return; } -if (db) fprintf(stderr, "record_CA-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); req = (xCopyAreaReq *) rec_data->data; if (req->reqType != X_CopyArea) { return; } -if (db) fprintf(stderr, "record_CA-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); /* @@ -2913,6 +3135,8 @@ short period of time with a painting error: two cursors, one above the other. good = 0; } else if (src != dst) { good = 0; + } else if (scr_ev_cnt >= SCR_EV_MAX) { + good = 0; } dx = dst_x - src_x; @@ -2925,26 +3149,71 @@ short period of time with a painting error: two cursors, one above the other. if (! good) { return; } -if (db) fprintf(stderr, "record_CA-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); - if (! valid_window(src, &attr)) { + /* + * after all of the above succeeds, now contact X server. + * we try to get away with some caching here. + */ + if (lookup_attr_cache(src, &cache_index, &next_index)) { + i = cache_index; + attr.x = scr_attr_cache[i].x; + attr.y = scr_attr_cache[i].y; + attr.width = scr_attr_cache[i].width; + attr.height = scr_attr_cache[i].height; + attr.map_state = scr_attr_cache[i].map_state; + rx = scr_attr_cache[i].rx; + ry = scr_attr_cache[i].ry; + valid = scr_attr_cache[i].valid; + + } else { + valid = valid_window(src, &attr, 1); + + if (valid) { + XTranslateCoordinates(dpy, src, rootwin, 0, 0, + &rx, &ry, &c); + } + if (next_index >= 0) { + i = next_index; + scr_attr_cache[i].win = src; + scr_attr_cache[i].fetched = 1; + scr_attr_cache[i].valid = valid; + scr_attr_cache[i].time = dnow(); + if (valid) { + scr_attr_cache[i].x = attr.x; + scr_attr_cache[i].y = attr.y; + scr_attr_cache[i].width = attr.width; + scr_attr_cache[i].height = attr.height; + scr_attr_cache[i].depth = attr.depth; + scr_attr_cache[i].class = attr.class; + scr_attr_cache[i].backing_store = + attr.backing_store; + scr_attr_cache[i].map_state = attr.map_state; + + scr_attr_cache[i].rx = rx; + scr_attr_cache[i].ry = ry; + } + } + } + + if (! valid) { return; } -if (db) fprintf(stderr, "record_CA-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); if (attr.map_state != IsViewable) { return; } - XTranslateCoordinates(dpy, src, rootwin, 0, 0, &rx, &ry, &c); -if (dba || db) fprintf(stderr, "record_CA-%d *FOUND: src: 0x%lx dx: %d dy: %d " - "x: %d y: %d w: %d h: %d st: %.4f\n", k++, src, dx, dy, src_x, - src_y, w, h, (double) rec_data->server_time/1000.0); - - if (scr_ev_cnt >= SCR_EV_MAX) { - return; - } +if (dba || db) { + double st, dt; + st = (double) rec_data->server_time/1000.0; + dt = (dnow() - servertime_diff) - st; + fprintf(stderr, "record_CA-%d *FOUND_SCROLL: src: 0x%lx dx: %d dy: %d " + "x: %d y: %d w: %d h: %d st: %.4f %.4f %.4f\n", k++, src, dx, dy, + src_x, src_y, w, h, st, dt, dnow() - x11vnc_start); +} i = scr_ev_cnt; @@ -2956,7 +3225,7 @@ if (dba || db) fprintf(stderr, "record_CA-%d *FOUND: src: 0x%lx dx: %d dy: %d " scr_ev[i].y = ry + dst_y; scr_ev[i].w = w; scr_ev[i].h = h; - scr_ev[i].t = (int) rec_data->server_time; + scr_ev[i].t = ((double) rec_data->server_time)/1000.0; scr_ev[i].win_x = rx; scr_ev[i].win_y = ry; scr_ev[i].win_w = attr.width; @@ -3019,11 +3288,9 @@ void record_CW(XPointer ptr, XRecordInterceptData *rec_data) { unsigned tmask; char *data; int dba = 0, db = debug_scroll; + int cache_index, next_index, valid; -//dba = 1; -//db = 1; - - if (1 || db) { + if (db) { if (rec_data->category == XRecordFromClient) { req = (xConfigureWindowReq *) rec_data->data; if (req->reqType == X_ConfigureWindow) { @@ -3032,7 +3299,7 @@ void record_CW(XPointer ptr, XRecordInterceptData *rec_data) { } } -if (dba || db) fprintf(stderr, "record_CW-%d id_base: 0x%lx ptr: 0x%lx " +if (dba || db > 1) fprintf(stderr, "record_CW-%d id_base: 0x%lx ptr: 0x%lx " "seq: 0x%lx rc: 0x%lx cat: %d swapped: %d 0x%lx/0x%lx\n", k++, rec_data->id_base, (unsigned long) ptr, xrecord_seq, rc_scroll, rec_data->category, rec_data->client_swapped, src, dst); @@ -3041,40 +3308,40 @@ if (dba || db) fprintf(stderr, "record_CW-%d id_base: 0x%lx ptr: 0x%lx " if (! xrecording) { return; } -if (db) fprintf(stderr, "record_CW-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); if ((XID) ptr != xrecord_seq) { return; } -if (db) fprintf(stderr, "record_CW-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); if (rec_data->id_base == 0) { return; } -if (db) fprintf(stderr, "record_CW-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); if (rec_data->category == XRecordStartOfData) { index = 0; return; } -if (db) fprintf(stderr, "record_CW-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); if (rec_data->category != XRecordFromClient) { return; } -if (db) fprintf(stderr, "record_CW-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); if (rec_data->client_swapped) { return; } -if (db) fprintf(stderr, "record_CW-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); req = (xConfigureWindowReq *) rec_data->data; if (req->reqType != X_ConfigureWindow) { return; } -if (db) fprintf(stderr, "record_CW-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); tmask = req->mask; @@ -3087,7 +3354,7 @@ if (db) fprintf(stderr, "record_CW-%d\n", k++); /* require no more than these 4 flags */ return; } -if (db) fprintf(stderr, "record_CW-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); f_x = req->mask & CWX; f_y = req->mask & CWY; @@ -3101,12 +3368,12 @@ if (db) fprintf(stderr, "record_CW-%d\n", k++); return; } } -if (db) fprintf(stderr, "record_CW-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); if ( (f_w && !f_h) || (!f_w && f_h) ) { return; } -if (db) fprintf(stderr, "record_CW-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); for (i=0; i<4; i++) { vals[i] = 0; @@ -3134,8 +3401,8 @@ if (db) fprintf(stderr, "record_CW-%d\n", k++); cw_events[i].y = cw_events[j].y; cw_events[i].w = cw_events[j].w; cw_events[i].h = cw_events[j].h; - index = 2; } + index = 2; } if (! f_x && ! f_y) { @@ -3191,7 +3458,7 @@ if (dba || db) fprintf(stderr, " record_CW ind: %d win: 0x%lx x: %d y: %d w: %d if (! good) { return; } -if (db) fprintf(stderr, "record_CW-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); match = 0; for (j=index - 1; j >= 0; j--) { @@ -3206,7 +3473,7 @@ if (db) fprintf(stderr, "record_CW-%d\n", k++); if (match != 3) { return; } -if (db) fprintf(stderr, "record_CW-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); /* @@ -3332,7 +3599,7 @@ if (dba) fprintf(stderr, "%d/%d/%d/%d %d/%d/%d/%d %d/%d/%d/%d\n", x0, y0, w0, if (! good) { return; } -if (db) fprintf(stderr, "record_CW-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); if (dy > 0) { h -= dy; @@ -3360,33 +3627,84 @@ if (db) fprintf(stderr, "record_CW-%d\n", k++); good = 0; } else if (h0 - h2 != nabs(dy)) { good = 0; + } else if (scr_ev_cnt >= SCR_EV_MAX) { + good = 0; } if (! good) { return; } -if (db) fprintf(stderr, "record_CW-%d\n", k++); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - /* geometry OK, now check with the X server: */ - if (! valid_window(win, &attr)) { - return; + /* + * geometry OK. + * after all of the above succeeds, now contact X server. + */ + if (lookup_attr_cache(win, &cache_index, &next_index)) { + i = cache_index; + attr.x = scr_attr_cache[i].x; + attr.y = scr_attr_cache[i].y; + attr.width = scr_attr_cache[i].width; + attr.height = scr_attr_cache[i].height; + attr.map_state = scr_attr_cache[i].map_state; + rx = scr_attr_cache[i].rx; + ry = scr_attr_cache[i].ry; + valid = scr_attr_cache[i].valid; + +if (0) fprintf(stderr, "lookup_attr_cache hit: %2d %2d 0x%lx %d\n", + cache_index, next_index, win, valid); + + } else { + valid = valid_window(win, &attr, 1); + +if (0) fprintf(stderr, "lookup_attr_cache MISS: %2d %2d 0x%lx %d\n", + cache_index, next_index, win, valid); + + if (valid) { + XTranslateCoordinates(dpy, win, rootwin, 0, 0, + &rx, &ry, &c); + } + if (next_index >= 0) { + i = next_index; + scr_attr_cache[i].win = win; + scr_attr_cache[i].fetched = 1; + scr_attr_cache[i].valid = valid; + scr_attr_cache[i].time = dnow(); + if (valid) { + scr_attr_cache[i].x = attr.x; + scr_attr_cache[i].y = attr.y; + scr_attr_cache[i].width = attr.width; + scr_attr_cache[i].height = attr.height; + scr_attr_cache[i].depth = attr.depth; + scr_attr_cache[i].class = attr.class; + scr_attr_cache[i].backing_store = + attr.backing_store; + scr_attr_cache[i].map_state = attr.map_state; + + scr_attr_cache[i].rx = rx; + scr_attr_cache[i].ry = ry; + } + } } -if (db) fprintf(stderr, "record_CW-%d\n", k++); - if (attr.map_state != IsViewable) { + if (! valid) { return; } -if (db) fprintf(stderr, "record_CW-%d\n", k++); - - XTranslateCoordinates(dpy, win, rootwin, 0, 0, &rx, &ry, &c); - -if (dba || db) fprintf(stderr, "record_CW-%d *FOUND: win: 0x%lx dx: %d dy: %d " - "x: %d y: %d w: %d h: %d st: %.4f\n", k++, win, dx, dy, src_x, src_y, - w, h, (double) rec_data->server_time/1000.0); +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - if (scr_ev_cnt >= SCR_EV_MAX) { + if (attr.map_state != IsViewable) { return; } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + +if (dba || db) { + double st, dt; + st = (double) rec_data->server_time/1000.0; + dt = (dnow() - servertime_diff) - st; + fprintf(stderr, "record_CW-%d *FOUND_SCROLL: win: 0x%lx dx: %d dy: %d " + "x: %d y: %d w: %d h: %d st: %.4f dt: %.4f %.4f\n", k++, win, + dx, dy, src_x, src_y, w, h, st, dt, dnow() - x11vnc_start); +} i = scr_ev_cnt; @@ -3398,7 +3716,7 @@ if (dba || db) fprintf(stderr, "record_CW-%d *FOUND: win: 0x%lx dx: %d dy: %d " scr_ev[i].y = ry + dst_y; scr_ev[i].w = w; scr_ev[i].h = h; - scr_ev[i].t = (int) rec_data->server_time; + scr_ev[i].t = ((double) rec_data->server_time)/1000.0; scr_ev[i].win_x = rx; scr_ev[i].win_y = ry; scr_ev[i].win_w = attr.width; @@ -3441,9 +3759,20 @@ if (dba || db) fprintf(stderr, "record_CW-%d *FOUND: win: 0x%lx dx: %d dy: %d " } void record_switch(XPointer ptr, XRecordInterceptData *rec_data) { + static int first = 1; xReq *req; -//fprintf(stderr, "XRecordFreeData-0: %p\n", rec_data); + if (first) { + int i; + for (i=0; i<SCR_ATTR_CACHE; i++) { + scr_attr_cache[i].win = None; + scr_attr_cache[i].fetched = 0; + scr_attr_cache[i].valid = 0; + scr_attr_cache[i].time = 0.0; + } + first = 0; + } + /* should handle control msgs, start/stop/etc */ if (rec_data->category == XRecordStartOfData) { record_CW(ptr, rec_data); @@ -3458,7 +3787,6 @@ void record_switch(XPointer ptr, XRecordInterceptData *rec_data) { } if (rec_data->category != XRecordFromClient) { -//fprintf(stderr, "XRecordFreeData-1: %p\n", rec_data); XRecordFreeData(rec_data); return; } @@ -3472,54 +3800,160 @@ void record_switch(XPointer ptr, XRecordInterceptData *rec_data) { } else { ; } -//fprintf(stderr, "XRecordFreeData-2: %p\n", rec_data); XRecordFreeData(rec_data); } #endif -void xrecord_watch(int start) { +#if LIBVNCSERVER_HAVE_RECORD +void shutdown_record_context(XRecordContext rc, int bequiet, int reopen) { + int ret1, ret2; + + ret1 = XRecordDisableContext(rdpy_ctrl, rc); + if (!ret1 && !bequiet && !quiet) { + rfbLog("XRecordDisableContext(0x%lx) failed.\n", rc); + } + ret2 = XRecordFreeContext(rdpy_ctrl, rc_scroll); + if (!ret2 && ! bequiet && !quiet) { + rfbLog("XRecordFreeContext(0x%lx) failed.\n", rc); + } + XFlush(rdpy_ctrl); + + if (reopen == 2 && ret1 && ret2) { + reopen = 0; /* 2 means reopen only on failure */ + } + if (reopen) { + char *dpystr = DisplayString(dpy); + + XCloseDisplay(rdpy_data); + rdpy_data = XOpenDisplay(dpystr); + + if (! rdpy_data) { + rfbLog("Failed to reopen RECORD data connection:" + "%s\n", dpystr); + rfbLog(" disabling RECORD scroll detection.\n"); + use_xrecord = 0; + return; + } + + XCloseDisplay(rdpy_ctrl); + rdpy_ctrl = XOpenDisplay(dpystr); + + if (! rdpy_ctrl) { + rfbLog("Failed to reopen RECORD control connection:" + "%s\n", dpystr); + rfbLog(" disabling RECORD scroll detection.\n"); + use_xrecord = 0; + return; + } + if (! bequiet && reopen == 2) { + rfbLog("reopened RECORD data and control display" + " connections: %s\n", dpystr); + } + disable_grabserver(rdpy_ctrl); + disable_grabserver(rdpy_data); + } +} +#endif + +char *xerror_string(XErrorEvent *error); +int trap_record_xerror(Display *, XErrorEvent *); +int trapped_record_xerror; +XErrorEvent *trapped_record_xerror_event; + +#define RECORD_ERROR_MSG \ + if (! quiet) { \ + rfbLog("trapped RECORD XError: %s %d/%d/%d (0x%lx)\n", \ + xerror_string(trapped_record_xerror_event), \ + (int) trapped_record_xerror_event->error_code, \ + (int) trapped_record_xerror_event->request_code, \ + (int) trapped_record_xerror_event->minor_code, \ + (int) trapped_record_xerror_event->resourceid); \ + } + +void xrecord_watch(int start, int setby) { Window focus, wm, r, c, clast; + static double create_time = 0.0; + double dnow; + static double last_error = 0.0; int rx, ry, wx, wy; - int depth = 0, i; unsigned int m; - int db = debug_scroll; + int rc, db = debug_scroll; int do_shutdown = 0; + int reopen_dpys = 1; + char name_info[2048]; + XErrorHandler old_handler = NULL; + static Window last_win = None, last_result = None; -//db = 1; + dtime0(&dnow); + if (dnow < last_error + 0.5) { + return; + } #if LIBVNCSERVER_HAVE_RECORD if (! start) { + int shut_reopen = 2; +if (db) fprintf(stderr, "XRECORD OFF: %d/%d %.4f\n", xrecording, setby, dnow - x11vnc_start); xrecording = 0; if (! rc_scroll) { xrecord_focus_window = None; xrecord_wm_window = None; + xrecord_ptr_window = None; rcs_scroll = 0; return; } + + if (! do_shutdown && dnow > create_time + 45) { + /* XXX unstable if we keep a RECORD going forever */ + do_shutdown = 1; + shut_reopen = 1; + } + if (do_shutdown) { -if (db) fprintf(stderr, "=== shutdown-scroll 0x%lx\n", rc_scroll); +if (db > 1) fprintf(stderr, "=== shutdown-scroll 0x%lx\n", rc_scroll); X_LOCK; - if (! XRecordDisableContext(rdpy_ctrl, rc_scroll)) { - rfbLog("XRecordDisableContext(rc_scroll)" - " failed.\n"); - } - if (! XRecordFreeContext(rdpy_ctrl, rc_scroll)) { - rfbLog("XRecordFreeContext(rc_scroll)" - " failed.\n"); - } + trapped_record_xerror = 0; + old_handler = XSetErrorHandler(trap_record_xerror); + + shutdown_record_context(rc_scroll, 0, shut_reopen); + if (! use_xrecord) return; + XRecordProcessReplies(rdpy_data); + + if (trapped_record_xerror) { + RECORD_ERROR_MSG; + last_error = dnow; + } + XSetErrorHandler(old_handler); X_UNLOCK; + rc_scroll = 0; + } else { if (rcs_scroll) { -if (db) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll); +if (db > 1) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll); X_LOCK; + trapped_record_xerror = 0; + old_handler = + XSetErrorHandler(trap_record_xerror); + rcs_scroll = XRecordCurrentClients; XRecordUnregisterClients(rdpy_ctrl, rc_scroll, &rcs_scroll, 1); XRecordDisableContext(rdpy_ctrl, rc_scroll); XFlush(rdpy_ctrl); XRecordProcessReplies(rdpy_data); + + if (trapped_record_xerror) { + RECORD_ERROR_MSG; + + shutdown_record_context(rc_scroll, + 0, reopen_dpys); + if (! use_xrecord) return; + + last_error = dnow; + rc_scroll = 0; + } + XSetErrorHandler(old_handler); X_UNLOCK; } } @@ -3540,9 +3974,11 @@ if (db) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll) */ xrecord_focus_window = None; xrecord_wm_window = None; + xrecord_ptr_window = None; rcs_scroll = 0; return; } +if (db) fprintf(stderr, "XRECORD ON: %d/%d %.4f\n", xrecording, setby, dnow - x11vnc_start); if (xrecording) { return; @@ -3551,27 +3987,38 @@ if (db) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll) if (do_shutdown && rc_scroll) { static int didmsg = 0; /* should not happen... */ - if (!didmsg) { + if (1 || !didmsg) { rfbLog("warning: do_shutdown && rc_scroll\n"); didmsg = 1; } - xrecord_watch(0); + xrecord_watch(0, SCR_NONE); } xrecording = 0; xrecord_focus_window = None; xrecord_wm_window = None; + xrecord_ptr_window = None; xrecord_set_by_keys = 0; xrecord_set_by_mouse = 0; - X_LOCK; - /* get the window with focus and mouse pointer: */ clast = None; focus = None; wm = None; + X_LOCK; +#if 0 + /* + * xrecord_focus_window / focus not currently used... save a + * round trip to the X server for now. + * N.B. our heuristic is inaccurate: if he is scrolling and + * drifts off of the scrollbar onto another application we + * will catch that application, not the starting ones. + * check_xrecord_{keys,mouse} mitigates this somewhat by + * delaying calls to xrecord_watch as much as possible. + */ XGetInputFocus(dpy, &focus, &i); +#endif XQueryPointer(dpy, rootwin, &r, &wm, &rx, &ry, &wx, &wy, &m); if (wm) { @@ -3579,19 +4026,54 @@ if (db) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll) } else { c = rootwin; } - for (i=0; i<3; i++) { - /* descend a bit to avoid wm frames: */ - clast = c; - if (! XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &m)) { - break; + + /* descend a bit to avoid wm frames: */ + if (c != rootwin && c == last_win) { + /* use cached results to avoid roundtrips: */ + clast = last_result; + } else if (scroll_good_all == NULL && scroll_skip_all == NULL) { + /* more efficient if name info not needed. */ + clast = descend_pointer(6, c, NULL, 0); + } else { + char *nm; + int matched_good = 0, matched_skip = 0; + + clast = descend_pointer(6, c, name_info, 2048); +if (db) fprintf(stderr, "name_info: %s\n", name_info); + + nm = name_info; + + if (scroll_good_all) { + matched_good += match_str_list(nm, scroll_good_all); } - if (! c) { - break; + if (setby == SCR_KEY && scroll_good_key) { + matched_good += match_str_list(nm, scroll_good_key); + } + if (setby == SCR_MOUSE && scroll_good_mouse) { + matched_good += match_str_list(nm, scroll_good_mouse); + } + if (scroll_skip_all) { + matched_skip += match_str_list(nm, scroll_skip_all); + } + if (setby == SCR_KEY && scroll_skip_key) { + matched_skip += match_str_list(nm, scroll_skip_key); + } + if (setby == SCR_MOUSE && scroll_skip_mouse) { + matched_skip += match_str_list(nm, scroll_skip_mouse); + } + + if (!matched_good && matched_skip) { + clast = None; } - depth++; } + if (c != rootwin) { + /* cache results for possible use next call */ + last_win = c; + last_result = clast; + } + if (!clast || clast == rootwin) { -if (db) fprintf(stderr, "--- xrecord_watch: skip.\n"); +if (db) fprintf(stderr, "--- xrecord_watch: SKIP.\n"); X_UNLOCK; return; } @@ -3600,73 +4082,123 @@ if (db) fprintf(stderr, "--- xrecord_watch: skip.\n"); rr_scroll[0] = rr_CA; rr_scroll[1] = rr_CW; + /* + * start trapping... there still are some occasional failures + * not yet understood, likely some race condition WRT the + * context being setup. + */ + trapped_record_xerror = 0; + old_handler = XSetErrorHandler(trap_record_xerror); + if (! rc_scroll) { /* do_shutdown case or first time in */ rcs_scroll = (XRecordClientSpec) clast; rc_scroll = XRecordCreateContext(rdpy_ctrl, 0, &rcs_scroll, 1, rr_scroll, 2); + if (rc_scroll) { + dtime0(&create_time); + } else { + rcs_scroll = 0; + } + } else if (! do_shutdown) { if (rcs_scroll) { -if (db) fprintf(stderr, "=2= unreg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll); + /* + * should have been unregistered in xrecord_watch(0)... + */ rcs_scroll = XRecordCurrentClients; XRecordUnregisterClients(rdpy_ctrl, rc_scroll, &rcs_scroll, 1); + +if (db > 1) fprintf(stderr, "=2= unreg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll); + } rcs_scroll = (XRecordClientSpec) clast; -if (db) fprintf(stderr, "=-= reg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll); + +if (db > 1) fprintf(stderr, "=-= reg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll); if (!XRecordRegisterClients(rdpy_ctrl, rc_scroll, 0, &rcs_scroll, 1, rr_scroll, 2)) { - static time_t last_err = 0; - time_t now = time(0); - if (now > last_err + 60) { + if (1 || dnow > last_error + 60) { rfbLog("failed to register client 0x%lx with" " X RECORD context rc_scroll.\n", clast); - last_err = now; } + last_error = dnow; + rcs_scroll = 0; /* continue on for now... */ } } + XFlush(rdpy_ctrl); if (db) fprintf(stderr, "rc_scroll: 0x%lx\n", rc_scroll); + if (trapped_record_xerror) { + RECORD_ERROR_MSG; + } if (! rc_scroll) { + XSetErrorHandler(old_handler); X_UNLOCK; use_xrecord = 0; rfbLog("failed to create X RECORD context rc_scroll.\n"); rfbLog(" switching to -noscrollcopyrect mode.\n"); return; + } else if (! rcs_scroll || trapped_record_xerror) { + /* try again later */ + shutdown_record_context(rc_scroll, 0, reopen_dpys); + if (! use_xrecord) return; + rc_scroll = 0; + last_error = dnow; + + XSetErrorHandler(old_handler); + X_UNLOCK; + + return; } xrecord_focus_window = focus; +#if 0 + /* xrecord_focus_window currently unused. */ if (! xrecord_focus_window) { xrecord_focus_window = clast; } +#endif xrecord_wm_window = wm; if (! xrecord_wm_window) { xrecord_wm_window = clast; } + xrecord_ptr_window = clast; + xrecording = 1; xrecord_seq++; - xrecord_start = 0.0; - dtime(&xrecord_start); - - if (!XRecordEnableContextAsync(rdpy_data, rc_scroll, record_switch, - (XPointer) xrecord_seq)) { - static time_t last_err = 0; - time_t now = time(0); - if (now > last_err + 60) { + dtime0(&xrecord_start); + + rc = XRecordEnableContextAsync(rdpy_data, rc_scroll, record_switch, + (XPointer) xrecord_seq); + + if (!rc || trapped_record_xerror) { + if (1 || dnow > last_error + 60) { rfbLog("failed to enable RECORD context " - "rc_scroll.\n"); - last_err = now; + "rc_scroll: 0x%lx\n", rc_scroll); + if (trapped_record_xerror) { + RECORD_ERROR_MSG; + } } + shutdown_record_context(rc_scroll, 0, reopen_dpys); + if (! use_xrecord) return; + rc_scroll = 0; + last_error = dnow; + xrecording = 0; /* continue on for now... */ } + XSetErrorHandler(old_handler); + + /* XXX this may cause more problems than it solves... */ XFlush(rdpy_data); + X_UNLOCK; #endif } @@ -3734,7 +4266,7 @@ void clean_up_exit (int ret) { if (! dpy) exit(ret); /* raw_rb hack */ /* X keyboard cleanups */ - delete_added_keycodes(); + delete_added_keycodes(0); if (clear_mods == 1) { clear_modifiers(0); @@ -3743,7 +4275,7 @@ void clean_up_exit (int ret) { } if (no_autorepeat) { - autorepeat(1); + autorepeat(1, 0); } if (use_solid_bg) { solid_bg(1); @@ -3783,6 +4315,7 @@ XErrorEvent *trapped_xerror_event; int trapped_xerror = 0; int trapped_xioerror = 0; int trapped_getimage_xerror = 0; +int trapped_record_xerror = 0; int trap_xerror(Display *d, XErrorEvent *error) { trapped_xerror = 1; @@ -3801,6 +4334,12 @@ int trap_getimage_xerror(Display *d, XErrorEvent *error) { return 0; } +int trap_record_xerror(Display *d, XErrorEvent *error) { + trapped_record_xerror = 1; + trapped_record_xerror_event = error; + return 0; +} + void interrupted(int); static int Xerror(Display *d, XErrorEvent *error) { @@ -3850,6 +4389,93 @@ char *xerror_string(XErrorEvent *error) { } } +char *crash_stack_command1 = NULL; +char *crash_stack_command2 = NULL; +char *crash_debug_command = NULL; +int crash_debug = 1; + +void initialize_crash_handler(void) { + int pid = program_pid; + crash_stack_command1 = malloc(1000); + crash_stack_command2 = malloc(1000); + crash_debug_command = malloc(1000); + + snprintf(crash_stack_command1, 500, "echo where > /tmp/gdb.%d;" + " env PATH=$PATH:/usr/local/bin:/usr/sfw/bin:/usr/bin" + " gdb -x /tmp/gdb.%d -batch -n %s %d;" + " rm -f /tmp/gdb.%d", pid, pid, program_name, pid, pid); + snprintf(crash_stack_command2, 500, "pstack %d", program_pid); + + snprintf(crash_debug_command, 500, "gdb %s %d", program_name, pid); +} + +void crash_shell_help(void) { + int pid = program_pid; + fprintf(stderr, "\n"); + fprintf(stderr, " *** Welcome to the x11vnc crash shell! ***\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "PROGRAM: %s PID: %d\n", program_name, pid); + fprintf(stderr, "\n"); + fprintf(stderr, "POSSIBLE DEBUGGER COMMAND:\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " %s\n", crash_debug_command); + fprintf(stderr, "\n"); + fprintf(stderr, "Press \"q\" to quit.\n"); + fprintf(stderr, "Press \"h\" or \"?\" for this help.\n"); + fprintf(stderr, "Press \"s\" to try to run some commands to" + " show a stack trace (gdb/pstack).\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Anything else is passed to -Q query function.\n"); + fprintf(stderr, "\n"); +} + +void crash_shell(void) { + char qry[1000], cmd[1000], line[1000]; + char *str, *p; + + crash_shell_help(); + fprintf(stderr, "\ncrash> "); + while (fgets(line, 1000, stdin) != NULL) { + str = lblanks(line); + + p = str; + while(*p) { + if (*p == '\n') { + *p = '\0'; + } + p++; + } + + if (*str == 'q' && *(str+1) == '\0') { + fprintf(stderr, "quiting.\n"); + return; + } else if (*str == 'h' && *(str+1) == '\0') { + crash_shell_help(); + } else if (*str == '?' && *(str+1) == '\0') { + crash_shell_help(); + } else if (*str == 's' && *(str+1) == '\0') { + sprintf(cmd, "sh -c '(%s) &'", crash_stack_command1); + fprintf(stderr, "\nrunning:\n\t%s\n\n", + crash_stack_command1); + system(cmd); + usleep(1000*1000); + + sprintf(cmd, "sh -c '(%s) &'", crash_stack_command2); + fprintf(stderr, "\nrunning:\n\t%s\n\n", + crash_stack_command2); + system(cmd); + usleep(1000*1000); + } else { + snprintf(qry, 1000, "qry=%s", str); + p = process_remote_cmd(qry, 1); + fprintf(stderr, "\n\nresult:\n%s\n", p); + free(p); + } + + fprintf(stderr, "crash> "); + } +} + /* * General problem handler */ @@ -3888,7 +4514,7 @@ void interrupted (int sig) { } /* X keyboard cleanups */ - delete_added_keycodes(); + delete_added_keycodes(0); if (clear_mods == 1) { clear_modifiers(0); @@ -3896,19 +4522,23 @@ void interrupted (int sig) { clear_keys(); } if (no_autorepeat) { - autorepeat(1); + autorepeat(1, 0); } if (use_solid_bg) { solid_bg(1); } + if (crash_debug) { + crash_shell(); + } + if (sig) { exit(2); } } /* trapping utility to check for a valid window: */ -int valid_window(Window win, XWindowAttributes *attr_ret) { +int valid_window(Window win, XWindowAttributes *attr_ret, int bequiet) { XErrorHandler old_handler; XWindowAttributes attr, *pattr; int ok = 0; @@ -3924,9 +4554,12 @@ int valid_window(Window win, XWindowAttributes *attr_ret) { if (XGetWindowAttributes(dpy, win, pattr)) { ok = 1; } - if (trapped_xerror && trapped_xerror_event && ! quiet) { - rfbLog("trapped XError: %s (0x%lx)\n", - xerror_string(trapped_xerror_event), win); + if (trapped_xerror && trapped_xerror_event) { + if (! quiet && ! bequiet) { + rfbLog("valid_window: trapped XError: %s (0x%lx)\n", + xerror_string(trapped_xerror_event), win); + } + ok = 0; } XSetErrorHandler(old_handler); trapped_xerror = 0; @@ -3940,7 +4573,7 @@ int wait_until_mapped(Window win) { XWindowAttributes attr; while (1) { - if (! valid_window(win, NULL)) { + if (! valid_window(win, NULL, 0)) { if (time(0) > start + waittime) { return 0; } @@ -4370,7 +5003,7 @@ static void client_gone(rfbClientPtr client) { rfbLog("client_count: %d\n", client_count); if (no_autorepeat && client_count == 0) { - autorepeat(1); + autorepeat(1, 0); } if (use_solid_bg && client_count == 0) { solid_bg(1); @@ -5376,7 +6009,6 @@ void check_connect_inputs(void) { */ enum rfbNewClientAction new_client(rfbClientPtr client) { ClientData *cd; - double tmr = 0.0; last_event = last_input = time(0); @@ -5429,12 +6061,14 @@ enum rfbNewClientAction new_client(rfbClientPtr client) { client->clientGoneHook = client_gone; client_count++; + last_keyboard_input = last_pointer_input = time(0); + if (no_autorepeat && client_count == 1 && ! view_only) { /* * first client, turn off X server autorepeat * XXX handle dynamic change of view_only and per-client. */ - autorepeat(0); + autorepeat(0, 0); } if (use_solid_bg && client_count == 1) { solid_bg(0); @@ -5444,8 +6078,7 @@ enum rfbNewClientAction new_client(rfbClientPtr client) { install_padded_fb(pad_geometry); } - dtime(&tmr); - cd->timer = tmr; + cd->timer = dnow(); cd->send_cmp_rate = 0.0; cd->send_raw_rate = 0.0; cd->latency = 0.0; @@ -5625,6 +6258,8 @@ void clear_keys(void) { * keystroke autorepeating as well, it kind of makes sense to shut it * off if no one is at the physical display... */ +static int save_auto_repeat = -1; + int get_autorepeat_state(void) { XKeyboardState kstate; X_LOCK; @@ -5633,10 +6268,16 @@ int get_autorepeat_state(void) { return kstate.global_auto_repeat; } -void autorepeat(int restore) { +int get_initial_autorepeat_state(void) { + if (save_auto_repeat < 0) { + save_auto_repeat = get_autorepeat_state(); + } + return save_auto_repeat; +} + +void autorepeat(int restore, int bequiet) { int global_auto_repeat; XKeyboardControl kctrl; - static int save_auto_repeat = -1; if (raw_fb && ! dpy) return; /* raw_fb hack */ @@ -5657,11 +6298,19 @@ void autorepeat(int restore) { XFlush(dpy); X_UNLOCK; - rfbLog("Restored X server key autorepeat to: %d\n", - save_auto_repeat); + if (! bequiet && ! quiet) { + rfbLog("Restored X server key autorepeat to: %d\n", + save_auto_repeat); + } } else { global_auto_repeat = get_autorepeat_state(); - save_auto_repeat = global_auto_repeat; + if (save_auto_repeat < 0) { + /* + * we only remember the state at startup + * to avoid confusing ourselves later on. + */ + save_auto_repeat = global_auto_repeat; + } X_LOCK; kctrl.auto_repeat_mode = AutoRepeatModeOff; @@ -5669,24 +6318,54 @@ void autorepeat(int restore) { XFlush(dpy); X_UNLOCK; - rfbLog("Disabled X server key autorepeat.\n"); - if (no_repeat_countdown >= 0) { - rfbLog(" you can run the command: 'xset r on' (%d " - "times)\n", no_repeat_countdown+1); - rfbLog(" to force it back on.\n"); + if (! bequiet && ! quiet) { + rfbLog("Disabled X server key autorepeat.\n"); + if (no_repeat_countdown >= 0) { + rfbLog(" to force back on run: 'xset r on' (%d " + "times)\n", no_repeat_countdown+1); + } + } + } +} + +/* + * We periodically delete any keysyms we have added, this is to + * lessen our effect on the X server state if we are terminated abruptly + * and cannot clear them and also to clear out any strange little used + * ones that would just fill up the keymapping. + */ +void check_add_keysyms(void) { + static time_t last_check = 0; + int clear_freq = 300, quiet = 1, count; + time_t now = time(0); + if (now > last_check + clear_freq) { + count = count_added_keycodes(); + /* + * only really delete if they have not typed recently + * and we have added 8 or more. + */ + if (now > last_keyboard_input + 5 && count >= 8) { + X_LOCK; + delete_added_keycodes(quiet); + X_UNLOCK; } + last_check = now; } } static KeySym added_keysyms[0x100]; +/* these are just for rfbLog messages: */ +static KeySym alltime_added_keysyms[1024]; +static int alltime_len = 1024; +static int alltime_num = 0; + int add_keysym(KeySym keysym) { int minkey, maxkey, syms_per_keycode; int kc, n, ret = 0; static int first = 1; KeySym *keymap; - if (raw_fb && ! dpy) return 0; /* raw_fb hack */ if (first) { for (n=0; n < 0x100; n++) { @@ -5694,6 +6373,9 @@ int add_keysym(KeySym keysym) { } first = 0; } + + if (raw_fb && ! dpy) return 0; /* raw_fb hack */ + if (keysym == NoSymbol) { return 0; } @@ -5709,7 +6391,7 @@ int add_keysym(KeySym keysym) { &syms_per_keycode); for (kc = minkey+1; kc <= maxkey; kc++) { - int i, is_empty = 1; + int i, j, didmsg = 0, is_empty = 1; char *str; KeySym new[8]; @@ -5728,7 +6410,7 @@ int add_keysym(KeySym keysym) { new[i] = NoSymbol; } if (add_keysyms == 2) { - new[0] = keysym; + new[0] = keysym; /* XXX remove me */ } else { for(i=0; i < syms_per_keycode; i++) { new[i] = keysym; @@ -5739,9 +6421,25 @@ int add_keysym(KeySym keysym) { XChangeKeyboardMapping(dpy, kc, syms_per_keycode, new, 1); - str = XKeysymToString(keysym); - rfbLog("added missing keysym to X display: %03d 0x%x \"%s\"\n", - kc, keysym, str ? str : "null"); + if (alltime_num >= alltime_len) { + didmsg = 1; /* something weird */ + } else { + for (j=0; j<alltime_num; j++) { + if (alltime_added_keysyms[j] == keysym) { + didmsg = 1; + break; + } + } + } + if (! didmsg) { + str = XKeysymToString(keysym); + rfbLog("added missing keysym to X display: %03d " + "0x%x \"%s\"\n", kc, keysym, str ? str : "null"); + + if (alltime_num < alltime_len) { + alltime_added_keysyms[alltime_num++] = keysym; + } + } XFlush(dpy); added_keysyms[kc] = keysym; @@ -5752,7 +6450,7 @@ int add_keysym(KeySym keysym) { return ret; } -void delete_keycode(KeyCode kc) { +void delete_keycode(KeyCode kc, int bequiet) { int minkey, maxkey, syms_per_keycode, i; KeySym *keymap; KeySym ksym, new[8]; @@ -5767,22 +6465,35 @@ void delete_keycode(KeyCode kc) { for (i=0; i<8; i++) { new[i] = NoSymbol; } + XChangeKeyboardMapping(dpy, kc, syms_per_keycode, new, 1); - ksym = XKeycodeToKeysym(dpy, kc, 0); - str = XKeysymToString(ksym); - rfbLog("deleted keycode from X display: %03d 0x%x \"%s\"\n", - kc, ksym, str ? str : "null"); + if (! bequiet && ! quiet) { + ksym = XKeycodeToKeysym(dpy, kc, 0); + str = XKeysymToString(ksym); + rfbLog("deleted keycode from X display: %03d 0x%x \"%s\"\n", + kc, ksym, str ? str : "null"); + } XFree(keymap); XFlush(dpy); } -void delete_added_keycodes(void) { +int count_added_keycodes(void) { + int kc, count = 0; + for (kc = 0; kc < 0x100; kc++) { + if (added_keysyms[kc] != NoSymbol) { + count++; + } + } + return count; +} + +void delete_added_keycodes(int bequiet) { int kc; for (kc = 0; kc < 0x100; kc++) { if (added_keysyms[kc] != NoSymbol) { - delete_keycode(kc); + delete_keycode(kc, bequiet); added_keysyms[kc] = NoSymbol; } } @@ -5801,15 +6512,159 @@ typedef struct keyremap { static keyremap_t *keyremaps = NULL; +void add_remap(char *line) { + char str1[256], str2[256]; + KeySym ksym1, ksym2; + int isbtn = 0, i; + static keyremap_t *current = NULL; + keyremap_t *remap; + + if (sscanf(line, "%s %s", str1, str2) != 2) { + rfbLog("remap: bad line: %s\n", line); + clean_up_exit(1); + } + if (sscanf(str1, "0x%x", &i) == 1) { + ksym1 = (KeySym) i; + } else { + ksym1 = XStringToKeysym(str1); + } + if (sscanf(str2, "0x%x", &i) == 1) { + ksym2 = (KeySym) i; + } else { + ksym2 = XStringToKeysym(str2); + } + if (ksym2 == NoSymbol) { + int i; + if (sscanf(str2, "Button%d", &i) == 1) { + ksym2 = (KeySym) i; + isbtn = 1; + } + } + if (ksym1 == NoSymbol || ksym2 == NoSymbol) { + rfbLog("warning: skipping bad remap line: %s", line); + return; + } + remap = (keyremap_t *) malloc((size_t) sizeof(keyremap_t)); + remap->before = ksym1; + remap->after = ksym2; + remap->isbutton = isbtn; + remap->next = NULL; + + rfbLog("remapping: (%s, 0x%x) -> (%s, 0x%x) isbtn=%d\n", str1, + ksym1, str2, ksym2, isbtn); + + if (keyremaps == NULL) { + keyremaps = remap; + } else { + current->next = remap; + } + current = remap; +} + +void add_dead_keysyms(char *str) { + char *p, *q; + int i; + char *list[] = { + "g grave dead_grave", + "a acute dead_acute", + "c asciicircum dead_circumflex", + "t asciitilde dead_tilde", + "m macron dead_macron", + "b breve dead_breve", + "D abovedot dead_abovedot", + "d diaeresis dead_diaeresis", + "o degree dead_abovering", + "A doubleacute dead_doubleacute", + "r caron dead_caron", + "e cedilla dead_cedilla", +/* "x XXX-ogonek dead_ogonek", */ +/* "x XXX-belowdot dead_belowdot", */ +/* "x XXX-hook dead_hook", */ +/* "x XXX-horn dead_horn", */ + NULL + }; + + p = str; + + while (*p != '\0') { + if (isspace(*p)) { + *p = '\0'; + } + p++; + } + + if (!strcmp(str, "DEAD")) { + for (i = 0; list[i] != NULL; i++) { + p = list[i] + 2; + add_remap(p); + } + } else if (!strcmp(str, "DEAD=missing")) { + for (i = 0; list[i] != NULL; i++) { + KeySym ksym, ksym2; + int inmap = 0; + + p = strdup(list[i] + 2); + q = strchr(p, ' '); + if (q == NULL) { + free(p); + continue; + } + *q = '\0'; + ksym = XStringToKeysym(p); + *q = ' '; + if (ksym == NoSymbol) { + free(p); + continue; + } + if (XKeysymToKeycode(dpy, ksym)) { + inmap = 1; + } +#if LIBVNCSERVER_HAVE_XKEYBOARD + if (! inmap && xkb_present) { + int kc, grp, lvl; + for (kc = 0; kc < 0x100; kc++) { + for (grp = 0; grp < 4; grp++) { + for (lvl = 0; lvl < 8; lvl++) { + ksym2 = XkbKeycodeToKeysym(dpy, + kc, grp, lvl); + if (ksym2 == NoSymbol) { + continue; + } + if (ksym2 == ksym) { + inmap = 1; + break; + } + } + } + } + } +#endif + if (! inmap) { + add_remap(p); + } + free(p); + } + } else if ((p = strchr(str, '=')) != NULL) { + while (*p != '\0') { + for (i = 0; list[i] != NULL; i++) { + q = list[i]; + if (*p == *q) { + q += 2; + add_remap(q); + break; + } + } + p++; + } + } +} + /* * process the -remap string (file or mapping string) */ void initialize_remap(char *infile) { FILE *in; - char *p, *q, line[256], str1[256], str2[256]; - int i; - KeySym ksym1, ksym2; - keyremap_t *remap, *current; + char *p, *q, line[256]; if (keyremaps != NULL) { /* free last remapping */ @@ -5829,11 +6684,20 @@ void initialize_remap(char *infile) { in = fopen(infile, "r"); if (in == NULL) { /* assume cmd line key1-key2,key3-key4 */ - if (! strchr(infile, '-') || (in = tmpfile()) == NULL) { + if (strstr(infile, "DEAD") == infile) { + ; + } else if (!strchr(infile, '-')) { rfbLog("remap: cannot open: %s\n", infile); rfbLogPerror("fopen"); clean_up_exit(1); } + if ((in = tmpfile()) == NULL) { + rfbLog("remap: cannot open tmpfile for %s\n", infile); + rfbLogPerror("tmpfile"); + clean_up_exit(1); + } + + /* copy in the string to file format */ p = infile; while (*p) { if (*p == '-') { @@ -5849,8 +6713,8 @@ void initialize_remap(char *infile) { fflush(in); rewind(in); } + while (fgets(line, 256, in) != NULL) { - int isbtn = 0; p = lblanks(line); if (*p == '\0') { continue; @@ -5858,50 +6722,16 @@ void initialize_remap(char *infile) { if (strchr(line, '#')) { continue; } - if ( (q = strchr(line, '-')) != NULL) { - /* allow Keysym1-Keysym2 notation */ - *q = ' '; - } - - if (sscanf(line, "%s %s", str1, str2) != 2) { - rfbLog("remap: bad line: %s\n", line); - fclose(in); - clean_up_exit(1); - } - if (sscanf(str1, "0x%x", &i) == 1) { - ksym1 = (KeySym) i; - } else { - ksym1 = XStringToKeysym(str1); - } - if (sscanf(str2, "0x%x", &i) == 1) { - ksym2 = (KeySym) i; - } else { - ksym2 = XStringToKeysym(str2); - } - if (ksym2 == NoSymbol) { - int i; - if (sscanf(str2, "Button%d", &i) == 1) { - ksym2 = (KeySym) i; - isbtn = 1; - } - } - if (ksym1 == NoSymbol || ksym2 == NoSymbol) { - rfbLog("warning: skipping bad remap line: %s", line); + + if (strstr(p, "DEAD") == p) { + add_dead_keysyms(p); continue; } - remap = (keyremap_t *) malloc((size_t) sizeof(keyremap_t)); - remap->before = ksym1; - remap->after = ksym2; - remap->isbutton = isbtn; - remap->next = NULL; - rfbLog("remapping: (%s, 0x%x) -> (%s, 0x%x) isbtn=%d\n", str1, - ksym1, str2, ksym2, isbtn); - if (keyremaps == NULL) { - keyremaps = remap; - } else { - current->next = remap; + if ((q = strchr(line, '-')) != NULL) { + /* allow Keysym1-Keysym2 notation */ + *q = ' '; } - current = remap; + add_remap(p); } fclose(in); } @@ -5928,6 +6758,16 @@ static unsigned int xkbmodifiers[0x100][GRP][LVL]; static int multi_key[0x100], mode_switch[0x100], skipkeycode[0x100]; static int shift_keys[0x100]; +/* + * for trying to order the keycodes to avoid problems, note the + * *first* keycode bound to it. kc_vec will be a permutation + * of 1...256 to get them in the preferred order. + */ +static int kc_vec[0x100]; +static int kc1_shift, kc1_control, kc1_caplock, kc1_alt; +static int kc1_meta, kc1_numlock, kc1_super, kc1_hyper; +static int kc1_mode_switch, kc1_iso_level3_shift, kc1_multi_key; + #if !LIBVNCSERVER_HAVE_XKEYBOARD /* empty functions for no xkb */ @@ -5935,9 +6775,250 @@ static void initialize_xkb_modtweak(void) {} static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { } +static void switch_to_xkb_if_better(void) {} #else +static void switch_to_xkb_if_better(void) { + KeySym keysym, *keymap; + int miss_noxkb[256], miss_xkb[256], missing_noxkb = 0, missing_xkb = 0; + int i, j, k, n, minkey, maxkey, syms_per_keycode; + int syms_gt_4 = 0; + int kc, grp, lvl; + + /* non-alphanumeric on us keyboard */ + KeySym must_have[] = { + XK_exclam, + XK_at, + XK_numbersign, + XK_dollar, + XK_percent, +/* XK_asciicircum, */ + XK_ampersand, + XK_asterisk, + XK_parenleft, + XK_parenright, + XK_underscore, + XK_plus, + XK_minus, + XK_equal, + XK_bracketleft, + XK_bracketright, + XK_braceleft, + XK_braceright, + XK_bar, + XK_backslash, + XK_semicolon, +/* XK_apostrophe, */ + XK_colon, + XK_quotedbl, + XK_comma, + XK_period, + XK_less, + XK_greater, + XK_slash, + XK_question, +/* XK_asciitilde, */ +/* XK_grave, */ + NoSymbol + }; + + if (! use_modifier_tweak || got_noxkb) { + return; + } + if (use_xkb_modtweak) { + /* already using it */ + return; + } + + XDisplayKeycodes(dpy, &minkey, &maxkey); + + keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), + &syms_per_keycode); + + /* handle alphabetic char with only one keysym (no upper + lower) */ + for (i = minkey; i <= maxkey; i++) { + KeySym lower, upper; + /* 2nd one */ + keysym = keymap[(i - minkey) * syms_per_keycode + 1]; + if (keysym != NoSymbol) { + continue; + } + /* 1st one */ + keysym = keymap[(i - minkey) * syms_per_keycode + 0]; + if (keysym == NoSymbol) { + continue; + } + XConvertCase(keysym, &lower, &upper); + if (lower != upper) { + keymap[(i - minkey) * syms_per_keycode + 0] = lower; + keymap[(i - minkey) * syms_per_keycode + 1] = upper; + } + } + + k = 0; + while (must_have[k] != NoSymbol) { + int gotit = 0; + KeySym must = must_have[k]; + for (i = minkey; i <= maxkey; i++) { + for (j = 0; j < syms_per_keycode; j++) { + keysym = keymap[(i-minkey) * syms_per_keycode + j]; + if (j >= 4) { + if (k == 0 && keysym != NoSymbol) { + /* for k=0 count the high keysyms */ + syms_gt_4++; + if (debug_keyboard > 1) { + char *str = XKeysymToString(keysym); + fprintf(stderr, "- high keysym mapping" + ": at %3d j=%d " + "'%s'\n", i, j, str ? str:"null"); + } + } + continue; + } + if (keysym == must) { + if (debug_keyboard > 1) { + char *str = XKeysymToString(must); + fprintf(stderr, "- at %3d j=%d found " + "'%s'\n", i, j, str ? str:"null"); + } + /* n.b. do not break, see syms_gt_4 above. */ + gotit = 1; + } + } + } + if (! gotit) { + if (debug_keyboard > 1) { + char *str = XKeysymToString(must); + KeyCode kc = XKeysymToKeycode(dpy, must); + fprintf(stderr, "- did not find 0x%lx '%s'\t" + "Ks2Kc: %d\n", must, str ? str:"null", kc); + if (kc != None) { + int j2; + for(j2=0; j2<syms_per_keycode; j2++) { + keysym = keymap[(kc-minkey) * + syms_per_keycode + j2]; + fprintf(stderr, " %d=0x%lx", + j2, keysym); + } + fprintf(stderr, "\n"); + } + } + missing_noxkb++; + miss_noxkb[k] = 1; + } else { + miss_noxkb[k] = 0; + } + k++; + } + n = k; + + XFree(keymap); + if (missing_noxkb == 0 && syms_gt_4 >= 8) { + rfbLog("XKEYBOARD: number of keysyms per keycode %d " + "is greater\n", syms_per_keycode); + rfbLog(" than 4 and %d keysyms are mapped above 4.\n", + syms_gt_4); + rfbLog(" Automatically switching to -xkb mode.\n"); + rfbLog(" If this makes the key mapping worse you can\n"); + rfbLog(" disable it with the \"-noxkb\" option.\n"); + rfbLog(" Also, remember \"-remap DEAD\" for accenting" + " characters.\n"); + + use_xkb_modtweak = 1; + return; + + } else if (missing_noxkb == 0) { + rfbLog("XKEYBOARD: all %d \"must have\" keysyms accounted" + " for.\n", n); + rfbLog(" Not automatically switching to -xkb mode.\n"); + rfbLog(" If some keys still cannot be typed, try using" + " -xkb.\n"); + rfbLog(" Also, remember \"-remap DEAD\" for accenting" + " characters.\n"); + return; + } + + for (k=0; k<n; k++) { + miss_xkb[k] = 1; + } + + for (kc = 0; kc < 0x100; kc++) { + for (grp = 0; grp < GRP; grp++) { + for (lvl = 0; lvl < LVL; lvl++) { + /* look up the Keysym, if any */ + keysym = XkbKeycodeToKeysym(dpy, kc, grp, lvl); + if (keysym == NoSymbol) { + continue; + } + for (k=0; k<n; k++) { + if (keysym == must_have[k]) { + miss_xkb[k] = 0; + } + } + } + } + } + + for (k=0; k<n; k++) { + if (miss_xkb[k]) { + missing_xkb++; + } + } + + rfbLog("\n"); + if (missing_xkb < missing_noxkb) { + rfbLog("XKEYBOARD:\n"); + rfbLog("Switching to -xkb mode to recover these keysyms:\n"); + } else { + rfbLog("XKEYBOARD: \"must have\" keysyms better accounted" + " for\n"); + rfbLog("under -noxkb mode: not switching to -xkb mode:\n"); + } + + rfbLog(" xkb noxkb Keysym (\"X\" means present)\n"); + rfbLog(" --- ----- -----------------------------\n"); + for (k=0; k<n; k++) { + char *xx, *xn, *name; + + keysym = must_have[k]; + if (keysym == NoSymbol) { + continue; + } + if (!miss_xkb[k] && !miss_noxkb[k]) { + continue; + } + if (miss_xkb[k]) { + xx = " "; + } else { + xx = " X "; + } + if (miss_noxkb[k]) { + xn = " "; + } else { + xn = " X "; + } + name = XKeysymToString(keysym); + rfbLog(" %s %s 0x%lx %s\n", xx, xn, keysym, + name ? name : "null"); + } + rfbLog("\n"); + + if (missing_xkb < missing_noxkb) { + rfbLog(" If this makes the key mapping worse you can\n"); + rfbLog(" disable it with the \"-noxkb\" option.\n"); + rfbLog("\n"); + + use_xkb_modtweak = 1; + + } else { + rfbLog(" If some keys still cannot be typed, try using" + " -xkb.\n"); + rfbLog(" Also, remember \"-remap DEAD\" for accenting" + " characters.\n"); + } +} + /* sets up all the keymapping info via Xkb API */ static void initialize_xkb_modtweak(void) { @@ -6021,6 +7102,19 @@ xkbmodifiers[] For the KeySym bound to this (keycode,group,level) store kc_max = 0; kc_min = 0x100; + /* first keycode for a modifier type (multi_key too) */ + kc1_shift = -1; + kc1_control = -1; + kc1_caplock = -1; + kc1_alt = -1; + kc1_meta = -1; + kc1_numlock = -1; + kc1_super = -1; + kc1_hyper = -1; + kc1_mode_switch = -1; + kc1_iso_level3_shift = -1; + kc1_multi_key = -1; + /* * loop over all possible (keycode, group, level) triples * and record what we find for it: @@ -6058,6 +7152,62 @@ xkbmodifiers[] For the KeySym bound to this (keycode,group,level) store shift_keys[kc] = lvl+1; } + if (ks == XK_Shift_L || ks == XK_Shift_R) { + if (kc1_shift == -1) { + kc1_shift = kc; + } + } + if (ks == XK_Control_L || ks == XK_Control_R) { + if (kc1_control == -1) { + kc1_control = kc; + } + } + if (ks == XK_Caps_Lock || ks == XK_Caps_Lock) { + if (kc1_caplock == -1) { + kc1_caplock = kc; + } + } + if (ks == XK_Alt_L || ks == XK_Alt_R) { + if (kc1_alt == -1) { + kc1_alt = kc; + } + } + if (ks == XK_Meta_L || ks == XK_Meta_R) { + if (kc1_meta == -1) { + kc1_meta = kc; + } + } + if (ks == XK_Num_Lock) { + if (kc1_numlock == -1) { + kc1_numlock = kc; + } + } + if (ks == XK_Super_L || ks == XK_Super_R) { + if (kc1_super == -1) { + kc1_super = kc; + } + } + if (ks == XK_Hyper_L || ks == XK_Hyper_R) { + if (kc1_hyper == -1) { + kc1_hyper = kc; + } + } + if (ks == XK_Mode_switch) { + if (kc1_mode_switch == -1) { + kc1_mode_switch = kc; + } + } + if (ks == XK_ISO_Level3_Shift) { + if (kc1_iso_level3_shift == -1) { + kc1_iso_level3_shift = kc; + } + } + if (ks == XK_Multi_key) { /* not a modifier.. */ + if (kc1_multi_key == -1) { + kc1_multi_key = kc; + } + } + /* * record maximum extent for group/level indices * and keycode range: @@ -6160,6 +7310,23 @@ xkbmodifiers[] For the KeySym bound to this (keycode,group,level) store } /* + * kc_vec will be used in some places to find modifiers, etc + * we apply some permutations to it as workarounds. + */ + for (kc = 0; kc < 0x100; kc++) { + kc_vec[kc] = kc; + } + + if (kc1_mode_switch != -1 && kc1_iso_level3_shift != -1) { + if (kc1_mode_switch < kc1_iso_level3_shift) { + /* we prefer XK_ISO_Level3_Shift: */ + kc_vec[kc1_mode_switch] = kc1_iso_level3_shift; + kc_vec[kc1_iso_level3_shift] = kc1_mode_switch; + } + } + /* any more? need to watch for undoing the above. */ + + /* * process the user supplied -skip_keycodes string. * This is presumably a list if "ghost" keycodes, the X server * thinks they exist, but they do not. ghosts can lead to @@ -6197,21 +7364,28 @@ xkbmodifiers[] For the KeySym bound to this (keycode,group,level) store static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { - int kc, grp, lvl, i; + int kc, grp, lvl, i, kci; int kc_f[0x100], grp_f[0x100], lvl_f[0x100], state_f[0x100], found; + int ignore_f[0x100]; unsigned int state; + /* these are used for finding modifiers, etc */ XkbStateRec kbstate; int got_kbstate = 0; int Kc_f, Grp_f, Lvl_f; + static int Kc_last_down = -1; + static KeySym Ks_last_down = NoSymbol; X_LOCK; if (debug_keyboard) { char *str = XKeysymToString(keysym); - if (debug_keyboard > 1) fprintf(stderr, "\n"); + if (debug_keyboard > 1) { + rfbLog("----------start-xkb_tweak_keyboard (%s) " + "--------\n", down ? "DOWN" : "UP"); + } rfbLog("xkb_tweak_keyboard: %s keysym=0x%x \"%s\"\n", down ? "down" : "up", (int) keysym, str ? str : "null"); @@ -6219,7 +7393,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, /* * set everything to not-yet-found. - * these "found" arrays (*_f) let us dyanamically consider the + * these "found" arrays (*_f) let us dynamically consider the * one-to-many Keysym -> Keycode issue. we set the size at 256, * but of course only very few will be found. */ @@ -6228,6 +7402,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, grp_f[i] = -1; lvl_f[i] = -1; state_f[i] = -1; + ignore_f[i] = -1; } found = 0; @@ -6278,6 +7453,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, grp_f[found] = grp; lvl_f[found] = lvl; state_f[found] = state; + ignore_f[found] = xkbignore[kc][grp][lvl]; found++; } } @@ -6327,15 +7503,86 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, } /* - * we could optimize here if found > 1 + * we try to optimize here if found > 1 * e.g. minimize lvl or grp, or other things to give * "safest" scenario to simulate the keystrokes. - * but for now we just take the first one we found. */ - Kc_f = kc_f[0]; - Grp_f = grp_f[0]; - Lvl_f = lvl_f[0]; - state = state_f[0]; + + if (found > 1) { + if (down) { + int l, score[0x100]; + int best, best_score = -1; + /* need to break the tie... */ + if (! got_kbstate) { + XkbGetState(dpy, XkbUseCoreKbd, &kbstate); + got_kbstate = 1; + } + for (l=0; l < found; l++) { + int myscore = 0, b = 0x1, i; + int curr, curr_state = kbstate.mods; + int need, need_state = state_f[l]; + int ignore_state = ignore_f[l]; + + /* see how many modifiers need to be changed */ + for (i=0; i<8; i++) { + curr = b & curr_state; + need = b & need_state; + if (! (b & ignore_state)) { + ; + } else if (curr == need) { + ; + } else { + myscore++; + } + b = b << 1; + } + myscore *= 100; + + /* throw in some minimization of lvl too: */ + myscore += 2*lvl_f[l] + grp_f[l]; + + score[l] = myscore; + if (debug_keyboard > 1) { + fprintf(stderr, " *** score for " + "keycode %03d: %4d\n", + kc_f[l], myscore); + } + } + for (l=0; l < found; l++) { + int myscore = score[l]; + if (best_score == -1 || myscore < best_score) { + best = l; + best_score = myscore; + } + } + Kc_f = kc_f[best]; + Grp_f = grp_f[best]; + Lvl_f = lvl_f[best]; + state = state_f[best]; + + } else { + Kc_f = -1; + if (keysym == Ks_last_down) { + int l; + for (l=0; l < found; l++) { + if (Kc_last_down == kc_f[l]) { + Kc_f = Kc_last_down; + break; + } + } + } + + if (Kc_f == -1) { + /* hope for the best... XXX check mods */ + Kc_f = kc_f[0]; + } + } + } else { + Kc_f = kc_f[0]; + Grp_f = grp_f[0]; + Lvl_f = lvl_f[0]; + state = state_f[0]; + } if (debug_keyboard && found > 1) { int l; @@ -6348,7 +7595,8 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, str = XKeysymToString(XKeycodeToKeysym(dpy,kc_f[l],0)); fprintf(stderr, " \"%s\"", str ? str : "null"); } - fprintf(stderr, ", using first one: %03d\n", Kc_f); + fprintf(stderr, ", picked this one: %03d (last down: %03d)\n", + Kc_f, Kc_last_down); } if (down) { @@ -6362,9 +7610,14 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, KeySym ks; Bool dn; + /* remember these to aid the subsequent up case: */ + Ks_last_down = keysym; + Kc_last_down = Kc_f; + if (! got_kbstate) { /* get the current modifier state if we haven't yet */ XkbGetState(dpy, XkbUseCoreKbd, &kbstate); + got_kbstate = 1; } /* @@ -6475,77 +7728,80 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, /* * Again, an inefficient loop, this time just * looking for modifiers... + * + * note the use of kc_vec to prefer XK_ISO_Level3_Shift + * over XK_Mode_switch. */ - for (kc = kc_min; kc <= kc_max; kc++) { - for (grp = 0; grp < grp_max+1; grp++) { - for (lvl = 0; lvl < lvl_max+1; lvl++) { - int skip = 1, dbmsg = 0; + for (kci = kc_min; kci <= kc_max; kci++) { + for (grp = 0; grp < grp_max+1; grp++) { + for (lvl = 0; lvl < lvl_max+1; lvl++) { + int skip = 1, dbmsg = 0; - ms = xkbmodifiers[kc][grp][lvl]; - if (! ms || ms != b) { - continue; - } + kc = kc_vec[kci]; - if (skipkeycode[kc] && debug_keyboard) { - fprintf(stderr, " xxx skipping " - "keycode: %d G%d/L%d\n", - kc, grp+1, lvl+1); - } - if (skipkeycode[kc]) { - continue; - } + ms = xkbmodifiers[kc][grp][lvl]; + if (! ms || ms != b) { + continue; + } - ks = xkbkeysyms[kc][grp][lvl]; - if (! ks) { - continue; - } + if (skipkeycode[kc] && debug_keyboard) { + fprintf(stderr, " xxx skipping keycode:" + " %d G%d/L%d\n", kc, grp+1, lvl+1); + } + if (skipkeycode[kc]) { + continue; + } - if (ks == XK_Shift_L) { - skip = 0; - } else if (ks == XK_Shift_R) { - skip = 0; - } else if (ks == XK_Mode_switch) { - skip = 0; - } else if (ks == XK_ISO_Level3_Shift) { - skip = 0; - } - /* - * Alt, Meta, Control, Super, - * Hyper, Num, Caps are skipped. - * - * XXX need more work on Locks, - * and non-standard modifiers. - * (e.g. XF86_Next_VMode using - * Ctrl+Alt) - */ - if (debug_keyboard > 1) { - char *str = XKeysymToString(ks); - int kt = keystate[kc]; - fprintf(stderr, " === for " - "mod=%s found kc=%03d/G%d" - "/L%d it is %d %s skip=%d " - "(%s)\n", bitprint(b,8), kc, - grp+1, lvl+1, kt, kt ? - "down" : "up ", skip, - str ? str : "null"); - } + ks = xkbkeysyms[kc][grp][lvl]; + if (! ks) { + continue; + } - if (! skip && needmods[i] != - keystate[kc] && sentmods[i] == 0) { - sentmods[i] = kc; - dbmsg = 1; - } + if (ks == XK_Shift_L) { + skip = 0; + } else if (ks == XK_Shift_R) { + skip = 0; + } else if (ks == XK_Mode_switch) { + skip = 0; + } else if (ks == XK_ISO_Level3_Shift) { + skip = 0; + } + /* + * Alt, Meta, Control, Super, + * Hyper, Num, Caps are skipped. + * + * XXX need more work on Locks, + * and non-standard modifiers. + * (e.g. XF86_Next_VMode using + * Ctrl+Alt) + */ + if (debug_keyboard > 1) { + char *str = XKeysymToString(ks); + int kt = keystate[kc]; + fprintf(stderr, " === for mod=%s " + "found kc=%03d/G%d/L%d it is %d " + "%s skip=%d (%s)\n", bitprint(b,8), + kc, grp+1, lvl+1, kt, kt ? + "down" : "up ", skip, str ? + str : "null"); + } - if (debug_keyboard > 1 && dbmsg) { - int nm = needmods[i]; - fprintf(stderr, " >>> we " - "choose kc=%03d=0x%02x to " - "change it to: %d %s\n", kc, - kc, nm, nm ? "down" : "up"); - } - + if (! skip && needmods[i] != + keystate[kc] && sentmods[i] == 0) { + sentmods[i] = kc; + dbmsg = 1; + } + + if (debug_keyboard > 1 && dbmsg) { + int nm = needmods[i]; + fprintf(stderr, " >>> we choose " + "kc=%03d=0x%02x to change it to: " + "%d %s\n", kc, kc, nm, nm ? + "down" : "up"); } + } + } } } for (i=0; i<8; i++) { @@ -6852,8 +8108,12 @@ void initialize_modtweak(void) { for (j = 0; j < syms_per_keycode; j++) { if (debug_keyboard) { char *sym; - sym = XKeysymToString(XKeycodeToKeysym(dpy, - i, j)); +#if 0 + sym =XKeysymToString(XKeycodeToKeysym(dpy,i,j)); +#else + keysym = keymap[(i-minkey)*syms_per_keycode+j]; + sym = XKeysymToString(keysym); +#endif fprintf(stderr, "%-18s ", sym ? sym : "null"); if (j == syms_per_keycode - 1) { fprintf(stderr, "\n"); @@ -6863,7 +8123,8 @@ void initialize_modtweak(void) { /* * Something wacky in the keymapping. * Ignore these non Shift/AltGr chords - * for now... + * for now... n.b. we try to automatically + * switch to -xkb for this case. */ continue; } @@ -7082,6 +8343,11 @@ void pipe_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { check_pipeinput(); } +typedef struct keyevent { + rfbKeySym sym; + rfbBool down; + double time; +} keyevent_t; /* * key event handler. See the above functions for contortions for * running under -modtweak. @@ -7092,22 +8358,110 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { KeyCode k; int isbutton = 0; allowed_input_t input; + time_t now = time(0); + double dnow; + static rfbBool last_down; + static rfbKeySym last_keysym; + + dtime0(&dnow); if (debug_keyboard) { char *str; X_LOCK; str = XKeysymToString(keysym); - rfbLog("keyboard(%s, 0x%x \"%s\")\n", down ? "down":"up", - (int) keysym, str ? str : "null"); + rfbLog("keyboard(%s, 0x%x \"%s\") %.4f\n", down ? "down":"up", + (int) keysym, str ? str : "null", dnow - x11vnc_start); X_UNLOCK; } + if (skip_duplicate_key_events) { + if (keysym == last_keysym && down == last_down) { + if (debug_keyboard) { + rfbLog("skipping dup key event: %d 0x%x\n", + down, keysym); + } + return; + } + } + + last_down = down; + last_keysym = keysym; + last_keyboard_time = dnow; + + if (0 && max_scroll_keyrate) { + /* XXX not working... */ + static int hlen = 256, hidx = 0; + static keyevent_t history[256]; + static rfbKeySym last_down_skip_keysym = None; + double key_dt, keytimes[256]; + int idx, i, nrep = 0, skip = 0; + + if (!down) { + if (last_down_skip_keysym != None) { + if (keysym == last_down_skip_keysym) { + skip = 1; + } + } + } else { + if (last_scroll_type == SCR_KEY && + dnow < last_scroll_event + 1.0) { + key_dt = 1.0/max_scroll_keyrate; +if (0) fprintf(stderr, "key_dt: %.4f\n", key_dt); + for (i=0; i<hlen; i++) { + idx = hidx - i - 1; + if (idx < 0) idx += hlen; + + if (history[idx].sym != keysym) { + break; + } + if (dnow > history[idx].time + 1.5) { + break; + } + if (history[idx].down == down) { + +if (0) fprintf(stderr, "key_dt: %.4f %d %d %.4f\n", key_dt, history[idx].sym, + history[idx].down, history[idx].time - x11vnc_start); + + keytimes[nrep++] = + history[idx].time; + } + } + if (nrep > 0) { + idx = hidx - 1; + if (idx < 0) idx += hlen; + if (dnow < keytimes[0] + key_dt) { + skip = 1; + } + } + } + } + if (skip) { + rfbLog("--- scroll keyrate skipping 0x%lx %s rep:%d " + "%.4f\n", keysym, down ? "down":"up", nrep, + down ? dnow - keytimes[0] : dnow - x11vnc_start); + if (down) { + last_down_skip_keysym = keysym; + } + return; + } + last_down_skip_keysym = None; + + history[hidx].sym = keysym; + history[hidx].time = dnow; + history[hidx].down = down; + if (++hidx >= hlen) { + hidx = 0; + } + } + if (pipeinput_fh != NULL) { pipe_keyboard(down, keysym, client); if (! pipeinput_tee) { if (! view_only || raw_fb) { /* raw_fb hack */ last_keyboard_client = client; - last_event = last_input = time(0); + last_event = last_input = now; + last_keyboard_input = now; + last_keysym = keysym; got_user_input++; got_keyboard_input++; @@ -7125,7 +8479,9 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { } last_keyboard_client = client; - last_event = last_input = time(0); + last_event = last_input = now; + last_keyboard_input = now; + last_keysym = keysym; got_user_input++; got_keyboard_input++; @@ -7155,8 +8511,28 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { } } + if (use_xrecord && ! xrecording && down) { + if (scaling && ! got_scrollcopyrect) { + ; + } else if (!strcmp(scroll_copyrect, "never")) { + ; + } else if (!strcmp(scroll_copyrect, "mouse")) { + ; + } else if (! xrecord_skip_keysym(keysym)) { + snapshot_stack_list(0, 0.25); + xrecord_watch(1, SCR_KEY); + xrecord_set_by_keys = 1; + } else { + if (debug_scroll) { + char *str = XKeysymToString(keysym); + rfbLog("xrecord_skip_keysym: %s\n", + str ? str : "NoSymbol"); + } + } + } + if (isbutton) { - int button = (int) keysym; + int mask, button = (int) keysym; if (! down) { return; /* nothing to send */ } @@ -7164,33 +8540,22 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { rfbLog("keyboard(): remapping keystroke to button %d" " click\n", button); } - if (button < 1 || button > num_buttons) { - rfbLog("keyboard(): ignoring mouse button out of " - "bounds: %d\n", button); - return; - } + X_LOCK; - XTestFakeButtonEvent_wr(dpy, button, True, CurrentTime); - XTestFakeButtonEvent_wr(dpy, button, False, CurrentTime); + /* + * This in principle can be a little dicey... i.e. even + * remap the button click to keystroke sequences! + * Usually just will simulate the button click. + */ + mask = 1<<(button-1); + do_button_mask_change(mask, button); /* down */ + mask = 0; + do_button_mask_change(mask, button); /* up */ XFlush(dpy); X_UNLOCK; return; } - if (use_xrecord && ! xrecording && down) { - if (scaling && ! got_scrollcopyrect) { - ; - } else if (!strcmp(scroll_copyrect, "never")) { - ; - } else if (!strcmp(scroll_copyrect, "mouse")) { - ; - } else if (! xrecord_skip_keysym(keysym)) { - snapshot_stack_list(0, 0.25); - xrecord_watch(1); - xrecord_set_by_keys = 1; - } - } - if (use_modifier_tweak) { modifier_tweak_keyboard(down, keysym, client); X_LOCK; @@ -7480,50 +8845,105 @@ void initialize_pointer_map(char *pointer_remap) { */ void snapshot_stack_list(int free_only, double allowed_age) { static double last_snap = 0.0, last_sync = 0.0, last_free = 0.0; - double now = 0.0, xsync_max = 0.25; + double now, xsync_max = 0.25; + int num, rc, i; Window r, w; + Window *list; - dtime(&now); + if (! stack_list) { + stack_list = (winattr_t *) malloc(256*sizeof(winattr_t)); + stack_list_num = 0; + stack_list_len = 256; + } + + dtime0(&now); if (free_only) { - if (stack_list) { - X_LOCK; - XFree(stack_list); - X_UNLOCK; - stack_list = NULL; - stack_num = 0; - last_free = now; - } + stack_list_num = 0; + last_free = now; return; } - if (stack_list && now < last_snap + allowed_age) { + if (stack_list_num && now < last_snap + allowed_age) { return; } - if (stack_list) { - XFree(stack_list); - stack_list = NULL; - stack_num = 0; - last_free = now; - } + stack_list_num = 0; + last_free = now; X_LOCK; - if (now > last_sync + xsync_max) { + if (0 && now > last_sync + xsync_max) { XSync(dpy, False); last_sync = now; } + rc = XQueryTree(dpy, rootwin, &r, &w, &list, &num); + X_UNLOCK; - if (! XQueryTree(dpy, rootwin, &r, &w, &stack_list, &stack_num)) { - stack_list = NULL; - stack_num = 0; + if (! rc) { + stack_list_num = 0; last_free = now; last_snap = 0.0; - } else { - last_snap = now; + return; } + + last_snap = now; + if (num > stack_list_len) { + int n = 2*num; + free(stack_list); + stack_list = (winattr_t *) malloc(n*sizeof(winattr_t)); + stack_list_len = n; + } + for (i=0; i<num; i++) { + stack_list[i].win = list[i]; + stack_list[i].fetched = 0; + stack_list[i].valid = 0; + stack_list[i].time = now; + } + stack_list_num = num; + + X_LOCK; + XFree(list); X_UNLOCK; } +void update_stack_list(void) { + int k; + double now; + XWindowAttributes attr; + + if (! stack_list) { + return; + } + if (! stack_list_num) { + return; + } + + dtime0(&now); + + for (k=0; k < stack_list_num; k++) { + Window win = stack_list[k].win; + if (!valid_window(win, &attr, 1)) { + stack_list[k].valid = 0; + } else { + stack_list[k].valid = 1; + stack_list[k].x = attr.x; + stack_list[k].y = attr.y; + stack_list[k].width = attr.width; + stack_list[k].height = attr.height; + stack_list[k].depth = attr.depth; + stack_list[k].class = attr.class; + stack_list[k].backing_store = attr.backing_store; + stack_list[k].map_state = attr.map_state; + + /* root_x, root_y not used for stack_list usage: */ + stack_list[k].rx = -1; + stack_list[k].ry = -1; + } + stack_list[k].fetched = 1; + stack_list[k].time = now; + } +if (0) fprintf(stderr, "update_stack_list[%d]: %.4f %.4f\n", stack_list_num, now - x11vnc_start, dtime(&now)); +} + /* * Send a pointer position event to the X server. */ @@ -7554,18 +8974,78 @@ static void update_x11_pointer_position(int x, int y) { /* change the cursor shape if necessary */ set_cursor(x, y, get_which_cursor()); - last_event = last_input = time(0); + last_event = last_input = last_pointer_input = time(0); +} + +void do_button_mask_change(int mask, int button) { + int mb, k, i = button-1; + + /* + * this expands to any pointer_map button -> keystrokes + * remappings. Usually just k=0 and we send one button event. + */ + for (k=0; k < MAX_BUTTON_EVENTS; k++) { + int bmask = (mask & (1<<i)); + + if (pointer_map[i+1][k].end) { + break; + } + + if (pointer_map[i+1][k].button) { + /* send button up or down */ + + mb = pointer_map[i+1][k].button; + if ((num_buttons && mb > num_buttons) || mb < 1) { + rfbLog("ignoring mouse button out of " + "bounds: %d>%d mask: 0x%x -> 0x%x\n", + mb, num_buttons, button_mask, mask); + continue; + } + if (debug_pointer) { + rfbLog("pointer(): sending button %d" + " %s (event %d)\n", mb, bmask + ? "down" : "up", k+1); + } + XTestFakeButtonEvent_wr(dpy, mb, (mask & (1<<i)) + ? True : False, CurrentTime); + } else { + /* send keysym up or down */ + KeyCode key = pointer_map[i+1][k].keycode; + int up = pointer_map[i+1][k].up; + int down = pointer_map[i+1][k].down; + + if (! bmask) { + /* do not send keysym on button up */ + continue; + } + if (debug_pointer) { + rfbLog("pointer(): sending button %d " + "down as keycode 0x%x (event %d)\n", + i+1, key, k+1); + rfbLog(" down=%d up=%d " + "keysym: %s\n", down, up, + XKeysymToString(XKeycodeToKeysym( + dpy, key, 0))); + } + if (down) { + XTestFakeKeyEvent_wr(dpy, key, True, + CurrentTime); + } + if (up) { + XTestFakeKeyEvent_wr(dpy, key, False, + CurrentTime); + } + } + } } /* * Send a pointer button event to the X server. */ static void update_x11_pointer_mask(int mask) { - int i, mb; - int xr_mouse = 1; - int snapped = 0; + int snapped, xr_mouse = 1, i; - last_event = last_input = time(0); + last_event = last_input = last_pointer_input = time(0); if (raw_fb && ! dpy) return; /* raw_fb hack */ @@ -7580,35 +9060,56 @@ static void update_x11_pointer_mask(int mask) { } if (mask && use_xrecord && ! xrecording && xr_mouse) { - static int px, py, x, y, w, h, ok; - Window frame; + static int px, py, x, y, w, h, got_wm_frame; + static XWindowAttributes attr; + Window frame = None, mwin = None; int skip = 0; if (!button_mask) { if (get_wm_frame_pos(&px, &py, &x, &y, &w, &h, - &frame)) { - ok = 1; + &frame, &mwin)) { + got_wm_frame = 1; +if (debug_scroll > 1) fprintf(stderr, "wm_win: 0x%lx\n", mwin); + if (mwin != None) { + if (!valid_window(mwin, &attr, 1)) { + mwin = None; + } + } } else { - ok = 0; + got_wm_frame = 0; } } - if (ok) { - if (! near_scrollbar_edge(x, y, w, h, px, py)) { - skip = 1; - } - if (near_wm_edge(x, y, w, h, px, py)) { + if (got_wm_frame) { + if (wireframe && near_wm_edge(x, y, w, h, px, py)) { /* step out of wireframe's way */ skip = 1; + } else { + int ok = 0; + if (near_scrollbar_edge(x, y, w, h, px, py)) { + ok = 1; + } + if (! ok && mwin != None) { + int w = attr.width; + int h = attr.height; + if (h > 10 * w || w > 10 * h) { +if (debug_scroll > 1) fprintf(stderr, "internal scrollbar: %dx%d\n", w, h); + ok = 1; + } + } + if (! ok) { + skip = 1; + } } } if (! skip) { - snapshot_stack_list(0, 0.25); + xrecord_watch(1, SCR_MOUSE); + snapshot_stack_list(0, 0.50); snapped = 1; - xrecord_watch(1); if (button_mask) { xrecord_set_by_mouse = 1; } else { + update_stack_list(); xrecord_set_by_mouse = 2; } } @@ -7625,71 +9126,18 @@ static void update_x11_pointer_mask(int mask) { } X_LOCK; + /* look for buttons that have be clicked or released: */ for (i=0; i < MAX_BUTTONS; i++) { if ( (button_mask & (1<<i)) != (mask & (1<<i)) ) { - int k; if (debug_pointer) { rfbLog("pointer(): mask change: mask: 0x%x -> " "0x%x button: %d\n", button_mask, mask,i+1); } - for (k=0; k < MAX_BUTTON_EVENTS; k++) { - int bmask = (mask & (1<<i)); - - if (pointer_map[i+1][k].end) { - break; - } - - if (pointer_map[i+1][k].button) { - /* sent button up or down */ - mb = pointer_map[i+1][k].button; - if ((num_buttons && mb > num_buttons) - || mb < 1) { - rfbLog("ignoring mouse button out of " - "bounds: %d>%d mask: 0x%x -> 0x%x\n", - mb, num_buttons, button_mask, mask); - continue; - } - if (debug_pointer) { - rfbLog("pointer(): sending button %d" - " %s (event %d)\n", mb, bmask - ? "down" : "up", k+1); - } - XTestFakeButtonEvent_wr(dpy, mb, (mask & (1<<i)) - ? True : False, CurrentTime); - } else { - /* sent keysym up or down */ - KeyCode key = pointer_map[i+1][k].keycode; - int up = pointer_map[i+1][k].up; - int down = pointer_map[i+1][k].down; - - if (! bmask) { - /* do not send keysym on button up */ - continue; - } - if (debug_pointer) { - rfbLog("pointer(): sending button %d " - "down as keycode 0x%x (event %d)\n", - i+1, key, k+1); - rfbLog(" down=%d up=%d " - "keysym: %s\n", down, up, - XKeysymToString(XKeycodeToKeysym( - dpy, key, 0))); - } - if (down) { - XTestFakeKeyEvent_wr(dpy, key, True, - CurrentTime); - } - if (up) { - XTestFakeKeyEvent_wr(dpy, key, False, - CurrentTime); - } - } - } + do_button_mask_change(mask, i+1); /* button # is i+1 */ } } - X_UNLOCK; /* @@ -7769,10 +9217,13 @@ void pipe_pointer(int mask, int x, int y, rfbClientPtr client) { */ void pointer(int mask, int x, int y, rfbClientPtr client) { allowed_input_t input; - int sent = 0; + int sent = 0, buffer_it = 0; if (debug_pointer && mask >= 0) { static int show_motion = -1; + static double last_pointer = 0.0; + double tnow, dt; + static int last_x, last_y; if (show_motion == -1) { if (getenv("X11VNC_DB_NOMOTION")) { show_motion = 0; @@ -7780,10 +9231,17 @@ void pointer(int mask, int x, int y, rfbClientPtr client) { show_motion = 1; } } + dtime0(&tnow); + tnow -= x11vnc_start; + dt = tnow - last_pointer; + last_pointer = tnow; if (show_motion) { - rfbLog("pointer(mask: 0x%x, x:%4d, y:%4d)\n", - mask, x, y); + rfbLog("pointer(mask: 0x%x, x:%4d, y:%4d) " + "dx: %3d dy: %3d dt: %.4f t: %.4f\n", mask, x, y, + x - last_x, y - last_y, dt, tnow); } + last_x = x; + last_y = y; } if (scaling) { @@ -7814,15 +9272,16 @@ void pointer(int mask, int x, int y, rfbClientPtr client) { if (view_only) { return; } - get_allowed_input(client, &input); - if (! input.motion && ! input.button) { - return; - } if (mask >= 0) { /* * mask = -1 is a special case call from scan_for_updates() * to flush the event queue; there is no real pointer event. */ + get_allowed_input(client, &input); + if (! input.motion && ! input.button) { + return; + } + got_user_input++; got_pointer_input++; last_pointer_client = client; @@ -7834,7 +9293,7 @@ void pointer(int mask, int x, int y, rfbClientPtr client) { * See check_user_input() for the more complicated things we do * in the non-threaded case. */ - if (use_threads && pointer_mode != 1) { + if ((use_threads && pointer_mode != 1) || pointer_flush_delay > 0.0) { # define NEV 32 /* storage for the event queue */ static int mutex_init = 0; @@ -7849,6 +9308,15 @@ void pointer(int mask, int x, int y, rfbClientPtr client) { mutex_init = 1; } + if (pointer_flush_delay > 0.0) { + maxwait = pointer_flush_delay; + } + if (mask >= 0) { + if (fb_copy_in_progress || pointer_flush_delay > 0.0) { + buffer_it = 1; + } + } + LOCK(pointerMutex); /* @@ -7863,7 +9331,7 @@ void pointer(int mask, int x, int y, rfbClientPtr client) { * not suspend work in the other libvncserver threads. * Maybe that is a possibility with a mutex... */ - if (fb_copy_in_progress && mask >= 0) { + if (buffer_it) { /* * mask = -1 is an all-clear signal from * scan_for_updates(). @@ -7886,8 +9354,8 @@ void pointer(int mask, int x, int y, rfbClientPtr client) { } UNLOCK(pointerMutex); if (debug_pointer) { - rfbLog("pointer(): deferring event " - "%d\n", i); + rfbLog("pointer(): deferring event %d" + " %.4f\n", i, tmr - x11vnc_start); } return; } @@ -7895,33 +9363,57 @@ void pointer(int mask, int x, int y, rfbClientPtr client) { /* time to send the queue */ for (i=0; i<nevents; i++) { + int sent = 0; + if (mask < 0 && client != NULL) { + /* hack to only push the latest event */ + if (i < nevents - 1) { + if (debug_pointer) { + rfbLog("- skip deferred event:" + " %d\n", i); + } + continue; + } + } if (debug_pointer) { - rfbLog("pointer(): sending event %d\n", i+1); + rfbLog("pointer(): sending event %d %.4f\n", + i+1, dnow() - x11vnc_start); } if (ev[i][1] >= 0) { update_x11_pointer_position(ev[i][1], ev[i][2]); + sent = 1; } if (ev[i][0] >= 0) { update_x11_pointer_mask(ev[i][0]); + sent = 1; + } + + if (sent) { + pointer_queued_sent++; } } if (nevents && dt > maxwait) { - X_LOCK; - if (dpy) { /* raw_fb hack */ + if (dpy) { /* raw_fb hack */ + if (mask < 0) { + if (debug_pointer) { + rfbLog("pointer(): calling XFlush " + "%.4f\n", dnow() - x11vnc_start); + } + X_LOCK; XFlush(dpy); + X_UNLOCK; } - X_UNLOCK; + } } nevents = 0; /* reset everything */ - tmr = 0.0; dt = 0.0; - dtime(&tmr); + dtime0(&tmr); UNLOCK(pointerMutex); } if (mask < 0) { /* -1 just means flush the event queue */ - if (debug_pointer > 1) { - rfbLog("pointer(): flush only.\n"); + if (debug_pointer) { + rfbLog("pointer(): flush only. %.4f\n", + dnow() - x11vnc_start); } return; } @@ -7948,6 +9440,14 @@ void pointer(int mask, int x, int y, rfbClientPtr client) { X_LOCK; XFlush(dpy); X_UNLOCK; + } else if (buffer_it) { + if (debug_pointer) { + rfbLog("pointer(): calling XFlush+" + "%.4f\n", dnow() - x11vnc_start); + } + X_LOCK; + XFlush(dpy); + X_UNLOCK; } } @@ -8329,7 +9829,7 @@ int handle_subwin_resize(char *msg) { if (! subwin) { return 0; /* hmmm... */ } - if (! valid_window(subwin, NULL)) { + if (! valid_window(subwin, NULL, 0)) { rfbLog("subwin 0x%lx went away!\n", subwin); X_UNLOCK; clean_up_exit(1); @@ -8821,7 +10321,7 @@ void sync_tod_with_servertime() { XEvent xev; char diff[64]; static int seq = 0; - int i; + int i, db = 0; if (! servertime) { servertime = XInternAtom(dpy, "X11VNC_SERVERTIME_DIFF", False); @@ -8840,19 +10340,18 @@ void sync_tod_with_servertime() { PropModeReplace, (unsigned char *) diff, strlen(diff)); XSync(dpy, False); - for (i=0; i<10; i++) { + for (i=0; i < 10; i++) { int k, got = 0; - for (k = 0; k<5; k++) { + for (k=0; k < 5; k++) { while (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { if (xev.xproperty.atom == servertime) { - double now = 0.0, stime; + double stime; - dtime(&now); stime = (double) xev.xproperty.time; stime = stime/1000.0; - servertime_diff = now - stime; - if (0) rfbLog("set servertime_diff: " + servertime_diff = dnow() - stime; + if (db) rfbLog("set servertime_diff: " "%.6f\n", servertime_diff); got = 1; } @@ -8865,6 +10364,73 @@ void sync_tod_with_servertime() { } } +void check_autorepeat() { + static time_t last_check = 0; + time_t now = time(0); + int autorepeat_is_on, autorepeat_initially_on, idle_timeout = 300; + static int idle_reset = 0; + + if (! no_autorepeat || ! client_count) { + return; + } + if (now <= last_check + 1) { + return; + } + last_check = now; + + autorepeat_is_on = get_autorepeat_state(); + autorepeat_initially_on = get_initial_autorepeat_state(); + + if (view_only) { + if (! autorepeat_is_on) { + autorepeat(1, 1); + } + return; + } + + if (now > last_keyboard_input + idle_timeout) { + /* autorepeat should be on when idle */ + if (! autorepeat_is_on && autorepeat_initially_on) { + static time_t last_msg = 0; + static int cnt = 0; + if (now > last_msg + idle_timeout && cnt++ < 5) { + rfbLog("idle keyboard: turning X autorepeat" + " back on.\n"); + last_msg = now; + } + autorepeat(1, 1); + idle_reset = 1; + } + } else { + if (idle_reset) { + static time_t last_msg = 0; + static int cnt = 0; + if (now > last_msg + idle_timeout && cnt++ < 5) { + rfbLog("active keyboard: turning X autorepeat" + " off.\n"); + last_msg = now; + } + autorepeat(0, 1); + idle_reset = 0; + + } else if (no_repeat_countdown && autorepeat_is_on) { + int n = no_repeat_countdown - 1; + if (n >= 0) { + rfbLog("Battling with something for " + "-norepeat!! (%d resets left)\n", n); + } else { + rfbLog("Battling with something for " + "-norepeat!!\n"); + } + if (no_repeat_countdown > 0) { + no_repeat_countdown--; + } + autorepeat(1, 0); + autorepeat(0, 0); + } + } +} + /* * This routine is periodically called to check for selection related * and other X11 events and respond to them as needed. @@ -8917,30 +10483,6 @@ void check_xevents(void) { } } - if (no_autorepeat && have_clients && no_repeat_countdown) { - static time_t last_check = 0; - if (now > last_check + 1 && ! view_only) { - last_check = now; - X_UNLOCK; - if (get_autorepeat_state() != 0) { - int n = no_repeat_countdown - 1; - if (n >= 0) { - rfbLog("Battling with something for " - "-norepeat!! (%d resets left)\n",n); - } else { - rfbLog("Battling with something for " - "-norepeat!!\n"); - } - if (no_repeat_countdown > 0) { - no_repeat_countdown--; - } - autorepeat(1); - autorepeat(0); - } - X_LOCK; - } - } - if (now > last_call+1) { /* we only check these once a second or so. */ int n = 0; @@ -9920,7 +11462,7 @@ char *process_remote_cmd(char *cmd, int stringonly) { ok = 1; } if (ok) { - if (twin && ! valid_window(twin, NULL)) { + if (twin && ! valid_window(twin, NULL, 0)) { rfbLog("skipping invalid sub-window: 0x%lx\n", twin); } else { @@ -9957,7 +11499,7 @@ char *process_remote_cmd(char *cmd, int stringonly) { ok = 1; } if (ok) { - if (twin && ! valid_window(twin, NULL)) { + if (twin && ! valid_window(twin, NULL, 0)) { rfbLog("skipping invalid sub-window: 0x%lx\n", twin); } else { @@ -9982,7 +11524,8 @@ char *process_remote_cmd(char *cmd, int stringonly) { } subwin_wait_mapped = 0; - } else if (strstr(p, "clip") == p) { + } else if (!strcmp(p, "clip") || + strstr(p, "clip:") == p) { /* skip-cmd-list */ COLON_CHECK("clip:") if (query) { snprintf(buf, bufn, "ans=%s%s%s", p, co, @@ -10865,6 +12408,7 @@ char *process_remote_cmd(char *cmd, int stringonly) { goto qry; } rfbLog("remote_cmd: enabling -nomodtweak mode.\n"); + got_nomodtweak = 1; use_modifier_tweak = 0; } else if (!strcmp(p, "xkb")) { @@ -10899,6 +12443,8 @@ char *process_remote_cmd(char *cmd, int stringonly) { } rfbLog("remote_cmd: disabling -xkb modtweak mode.\n"); use_xkb_modtweak = 0; + got_noxkb = 1; + initialize_modtweak(); } else if (strstr(p, "skip_keycodes") == p) { COLON_CHECK("skip_keycodes:") @@ -10924,6 +12470,23 @@ char *process_remote_cmd(char *cmd, int stringonly) { skip_keycodes = strdup(p); initialize_modtweak(); + } else if (!strcmp(p, "skip_dups")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + skip_duplicate_key_events); + goto qry; + } + rfbLog("remote_cmd: enabling -skip_dups mode\n"); + skip_duplicate_key_events = 1; + } else if (!strcmp(p, "noskip_dups")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + !skip_duplicate_key_events); + goto qry; + } + rfbLog("remote_cmd: disabling -skip_dups mode\n"); + skip_duplicate_key_events = 0; + } else if (!strcmp(p, "add_keysyms")) { if (query) { snprintf(buf, bufn, "ans=%s:%d", p, add_keysyms); @@ -11025,7 +12588,7 @@ char *process_remote_cmd(char *cmd, int stringonly) { goto qry; } rfbLog("remote_cmd: enabling -repeat mode.\n"); - autorepeat(1); /* restore initial setting */ + autorepeat(1, 0); /* restore initial setting */ no_autorepeat = 0; } else if (!strcmp(p, "norepeat")) { @@ -11039,7 +12602,7 @@ char *process_remote_cmd(char *cmd, int stringonly) { no_repeat_countdown = 2; } if (client_count && ! view_only) { - autorepeat(0); /* disable if any clients */ + autorepeat(0, 0); /* disable if any clients */ } } else if (!strcmp(p, "fb")) { @@ -11426,15 +12989,15 @@ char *process_remote_cmd(char *cmd, int stringonly) { setup_cursors_and_push(); } - } else if (strstr(p, "xwarp") == p || strstr(p, "xwarppointer") == p) { + } else if (strstr(p, "xwarppointer") == p || strstr(p, "xwarp") == p) { if (query) { snprintf(buf, bufn, "ans=%s:%d", p, use_xwarppointer); goto qry; } rfbLog("remote_cmd: turning on xwarppointer mode.\n"); use_xwarppointer = 1; - } else if (strstr(p, "noxwarp") == p || - strstr(p, "noxwarppointer") == p) { + } else if (strstr(p, "noxwarppointer") == p || + strstr(p, "noxwarp") == p) { if (query) { snprintf(buf, bufn, "ans=%s:%d", p, !use_xwarppointer); goto qry; @@ -11585,6 +13148,78 @@ char *process_remote_cmd(char *cmd, int stringonly) { rfbLog("remote_cmd: changed -scr_area to: %d\n", scrollcopyrect_min_area); + } else if (strstr(p, "scr_skip") == p) { + char *s = scroll_skip_str; + COLON_CHECK("scr_skip:") + if (!s || *s == '\0') s = scroll_skip_str0; + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, NONUL(s)); + goto qry; + } + p += strlen("scr_skip:"); + if (scroll_skip_str) { + free(scroll_skip_str); + } + + scroll_skip_str = strdup(p); + rfbLog("remote_cmd: changed -scr_skip to: %s\n", + scroll_skip_str); + initialize_scroll_matches(); + } else if (strstr(p, "scr_inc") == p) { + char *s = scroll_good_str; + if (!s || *s == '\0') s = scroll_good_str0; + COLON_CHECK("scr_inc:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, NONUL(s)); + goto qry; + } + p += strlen("scr_inc:"); + if (scroll_good_str) { + free(scroll_good_str); + } + + scroll_good_str = strdup(p); + rfbLog("remote_cmd: changed -scr_inc to: %s\n", + scroll_good_str); + initialize_scroll_matches(); + } else if (strstr(p, "scr_keys") == p) { + COLON_CHECK("scr_keys:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(scroll_key_list_str)); + goto qry; + } + p += strlen("scr_keys:"); + if (scroll_key_list_str) { + free(scroll_key_list_str); + } + + scroll_key_list_str = strdup(p); + rfbLog("remote_cmd: changed -scr_keys to: %s\n", + scroll_key_list_str); + initialize_scroll_keys(); + + } else if (strstr(p, "scr_parms") == p) { + COLON_CHECK("scr_parms:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + scroll_copyrect_str ? scroll_copyrect_str + : SCROLL_COPYRECT_PARMS); + goto qry; + } + p += strlen("scr_parms:"); + if (*p) { + if (scroll_copyrect_str) { + free(scroll_copyrect_str); + } + set_scrollcopyrect_mode("always"); + scroll_copyrect_str = strdup(p); + parse_scroll_copyrect(); + } + rfbLog("remote_cmd: set -scr_parms %s.\n", + NONUL(scroll_copyrect_str)); + got_scrollcopyrect = 1; + } else if (strstr(p, "scrollcopyrect") == p) { COLON_CHECK("scrollcopyrect:") if (query) { @@ -11598,7 +13233,8 @@ char *process_remote_cmd(char *cmd, int stringonly) { rfbLog("remote_cmd: changed -scrollcopyrect mode " "to: %s\n", NONUL(scroll_copyrect)); got_scrollcopyrect = 1; - } else if (strstr(p, "scr") == p) { + } else if (!strcmp(p, "scr") || + strstr(p, "scr:") == p) { /* skip-cmd-list */ COLON_CHECK("scr:") if (query) { snprintf(buf, bufn, "ans=%s%s%s", p, co, @@ -11781,24 +13417,20 @@ char *process_remote_cmd(char *cmd, int stringonly) { rfbLog("remote_cmd: setting wait %d -> %d ms.\n", waitms, w); waitms = w; - } else if (strstr(p, "rfbwait") == p) { + } else if (strstr(p, "readtimeout") == p) { int w, orig = rfbMaxClientWait; - COLON_CHECK("rfbwait:") + COLON_CHECK("readtimeout:") if (query) { snprintf(buf, bufn, "ans=%s%s%d", p, co, - rfbMaxClientWait); + rfbMaxClientWait/1000); goto qry; } - p += strlen("rfbwait:"); - w = atoi(p); - if (w < 0) w = 0; + p += strlen("readtimeout:"); + w = atoi(p) * 1000; + if (w <= 0) w = 0; rfbLog("remote_cmd: setting rfbMaxClientWait %d -> " - "%d ms.\n", orig, w); + "%d msec.\n", orig, w); rfbMaxClientWait = w; - if (screen) { - /* current unused by libvncserver: */ - screen->maxClientWait = w; - } } else if (!strcmp(p, "nap")) { if (query) { @@ -12098,7 +13730,8 @@ char *process_remote_cmd(char *cmd, int stringonly) { rfbLog("turning off dontdisconnect.\n"); screen->dontDisconnect = 0; - } else if (strstr(p, "desktop") == p) { + } else if (!strcmp(p, "desktop") || + strstr(p, "desktop:") == p) { /* skip-cmd-list */ COLON_CHECK("desktop:") if (query) { snprintf(buf, bufn, "ans=%s%s%s", p, co, @@ -12114,15 +13747,6 @@ char *process_remote_cmd(char *cmd, int stringonly) { rfbLog("remote_cmd: setting desktop name to %s\n", rfb_desktop_name); - } else if (!strcmp(p, "noremote")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, - !accept_remote_cmds); - goto qry; - } - rfbLog("remote_cmd: disabling remote commands.\n"); - accept_remote_cmds = 0; /* cannot be turned back on. */ - } else if (!strcmp(p, "debug_xevents")) { if (query) { snprintf(buf, bufn, "ans=%s:%d", p, debug_xevents); @@ -12221,6 +13845,46 @@ char *process_remote_cmd(char *cmd, int stringonly) { debug_scroll = atoi(p); rfbLog("set debug_scroll to: %d\n", debug_scroll); + } else if (!strcmp(p, "debug_tiles") || !strcmp(p, "dbt")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, debug_tiles); + goto qry; + } + debug_tiles = 1; + rfbLog("set debug_tiles to: %d\n", debug_tiles); + } else if (!strcmp(p, "nodebug_tiles") || !strcmp(p, "nodbt")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !debug_tiles); + goto qry; + } + debug_tiles = 0; + rfbLog("set debug_tiles to: %d\n", debug_tiles); + } else if (strstr(p, "debug_tiles") == p) { + COLON_CHECK("debug_tiles:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, + debug_tiles); + goto qry; + } + p += strlen("debug_tiles:"); + debug_tiles = atoi(p); + rfbLog("set debug_tiles to: %d\n", debug_tiles); + + } else if (!strcmp(p, "dbg")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, crash_debug); + goto qry; + } + crash_debug = 1; + rfbLog("set crash_debug to: %d\n", crash_debug); + } else if (!strcmp(p, "nodbg")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !crash_debug); + goto qry; + } + crash_debug = 0; + rfbLog("set crash_debug to: %d\n", crash_debug); + } else if (strstr(p, "hack") == p) { /* skip-cmd-list */ COLON_CHECK("hack:") if (query) { @@ -12231,6 +13895,15 @@ char *process_remote_cmd(char *cmd, int stringonly) { hack_val = atoi(p); rfbLog("set hack_val to: %d\n", hack_val); + } else if (!strcmp(p, "noremote")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + !accept_remote_cmds); + goto qry; + } + rfbLog("remote_cmd: disabling remote commands.\n"); + accept_remote_cmds = 0; /* cannot be turned back on. */ + } else if (query) { /* read-only variables that can only be queried: */ @@ -12469,7 +14142,23 @@ void record_desired_xdamage_rect(int x, int y, int w, int h) { int dt_x, dt_y, nt_x1, nt_y1, nt_x2, nt_y2, nt; int ix, iy, cnt = 0; int area = w*h, always_accept = 0; - int use_direct_fb_copy = 0; /* TBD: not working yet */ + /* + * XXX: not working yet, slow and overlaps with scan_display() + * probably slow because tall skinny rectangles very inefficient + * in general and in direct_fb_copy() (100X slower then horizontal). + */ + int use_direct_fb_copy = 0; + int wh_min, wh_max; + static int first = 1, udfb = 0; + if (first) { + if (getenv("XD_DFC")) { + udfb = 1; + } + first = 0; + } + if (udfb) { + use_direct_fb_copy = 1; + } if (xdamage_max_area <= 0) { always_accept = 1; @@ -12482,7 +14171,15 @@ void record_desired_xdamage_rect(int x, int y, int w, int h) { dt_x = w / tile_x; dt_y = h / tile_y; - if (!always_accept && dt_y >= 3 && area > 2000) { + if (w < h) { + wh_min = w; + wh_max = h; + } else { + wh_min = h; + wh_max = w; + } + + if (!always_accept && dt_y >= 3 && area > 4000) { /* * if it is real it should be caught by a normal scanline * poll, but we might as well keep if small (tall line?). @@ -12493,6 +14190,13 @@ void record_desired_xdamage_rect(int x, int y, int w, int h) { if (use_direct_fb_copy) { X_UNLOCK; direct_fb_copy(x, y, x + w, y + h, 1); + xdamage_direct_count++; + X_LOCK; + } else if (0 && wh_min < tile_x/4 && wh_max > 30 * wh_min) { + /* try it for long, skinny rects, XXX still no good */ + X_UNLOCK; + direct_fb_copy(x, y, x + w, y + h, 1); + xdamage_direct_count++; X_LOCK; } else { nt_x1 = nfix( (x)/tile_x, ntiles_x); @@ -12510,8 +14214,8 @@ void record_desired_xdamage_rect(int x, int y, int w, int h) { cnt++; if (! tile_has_xdamage_diff[nt]) { XD_des++; + tile_has_xdamage_diff[nt] = 1; } - tile_has_xdamage_diff[nt] = 1; /* not used: */ tile_row_has_xdamage_diff[iy] = 1; xdamage_tile_count++; @@ -12525,39 +14229,113 @@ void record_desired_xdamage_rect(int x, int y, int w, int h) { } } -void collect_xdamage(int scancnt) { +void add_region_xdamage(sraRegionPtr new_region) { + sraRegionPtr reg; + int prev_tick, nreg; + + if (! xdamage_regions) { + return; + } + + nreg = (xdamage_memory * NSCAN) + 1; + prev_tick = xdamage_ticker - 1; + if (prev_tick < 0) { + prev_tick = nreg - 1; + } + + reg = xdamage_regions[prev_tick]; + if (reg != NULL) { + sraRgnOr(reg, new_region); + } +} + +void clear_xdamage_mark_region(sraRegionPtr markregion, int flush) { +#if LIBVNCSERVER_HAVE_LIBXDAMAGE + XEvent ev; + sraRegionPtr tmpregion; + + if (! xdamage_present || ! use_xdamage) { + return; + } + if (! xdamage) { + return; + } + if (! xdamage_base_event_type) { + return; + } + + X_LOCK; + if (flush) { + XFlush(dpy); + } + while (XCheckTypedEvent(dpy, xdamage_base_event_type+XDamageNotify, &ev)) { + ; + } + /* clear the whole damage region */ + XDamageSubtract(dpy, xdamage, None, None); + X_UNLOCK; + + if (! markregion) { + /* NULL means mark the whole display */ + tmpregion = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + add_region_xdamage(tmpregion); + sraRgnDestroy(tmpregion); + } else { + add_region_xdamage(markregion); + } +#endif +} + +int collect_xdamage(int scancnt, int call) { #if LIBVNCSERVER_HAVE_LIBXDAMAGE XDamageNotifyEvent *dev; XEvent ev; sraRegionPtr tmpregion; sraRegionPtr reg; static int rect_count = 0; - int nreg, ccount = 0, dcount = 0; + int nreg, ccount = 0, dcount = 0, ecount = 0; static time_t last_rpt = 0; time_t now; int x, y, w, h, x2, y2; int i, dup, next, dup_max = 0; #define DUPSZ 16 int dup_x[DUPSZ], dup_y[DUPSZ], dup_w[DUPSZ], dup_h[DUPSZ]; + double tm, dt; if (! xdamage_present || ! use_xdamage) { - return; + return 0; } if (! xdamage) { - return; + return 0; } if (! xdamage_base_event_type) { - return; + return 0; } + dtime0(&tm); + nreg = (xdamage_memory * NSCAN) + 1; - xdamage_ticker = (xdamage_ticker+1) % nreg; - reg = xdamage_regions[xdamage_ticker]; - sraRgnMakeEmpty(reg); + + if (call == 0) { + xdamage_ticker = (xdamage_ticker+1) % nreg; + xdamage_direct_count = 0; + reg = xdamage_regions[xdamage_ticker]; + sraRgnMakeEmpty(reg); + } else { + reg = xdamage_regions[xdamage_ticker]; + } + X_LOCK; +if (0) XFlush(dpy); +if (0) XEventsQueued(dpy, QueuedAfterFlush); while (XCheckTypedEvent(dpy, xdamage_base_event_type+XDamageNotify, &ev)) { - /* TODO max cut off time in this loop? */ + /* + * TODO max cut off time in this loop? + * Could check QLength and if huge just mark the whole + * screen. + */ + ecount++; if (ev.type != xdamage_base_event_type + XDamageNotify) { break; } @@ -12646,9 +14424,22 @@ void collect_xdamage(int scancnt) { ccount++; } /* clear the whole damage region for next time. XXX check */ - XDamageSubtract(dpy, xdamage, None, None); + if (call == 1) { + XDamageSubtract(dpy, xdamage, None, None); + } X_UNLOCK; + if (0 && xdamage_direct_count) { + fb_push(); + } + + dt = dtime(&tm); + if ((debug_tiles > 1 && ecount) || (debug_tiles && ecount > 200) + || debug_xdamage > 1) { + fprintf(stderr, "collect_xdamage(%d): %.4f t: %.4f ev/dup/accept" + "/direct %d/%d/%d/%d\n", call, dt, tm - x11vnc_start, ecount, + dcount, ccount, xdamage_direct_count); + } now = time(0); if (! last_rpt) { last_rpt = now; @@ -12659,7 +14450,7 @@ void collect_xdamage(int scancnt) { if (XD_tot) { rat = ((double) XD_skip)/XD_tot; } - if (debug_xdamage) { + if (debug_tiles || debug_xdamage) { fprintf(stderr, "xdamage: == scanline skip/tot: " "%04d/%04d =%.3f rects: %d desired: %d\n", XD_skip, XD_tot, rat, rect_count, XD_des); @@ -12672,6 +14463,7 @@ void collect_xdamage(int scancnt) { last_rpt = now; } #endif + return 0; } int xdamage_hint_skip(int y) { @@ -13558,8 +15350,6 @@ typedef struct win_str_info { * cursor. So far only used to detect if mouse is on root background or not. * (returns 0 in that case, 1 otherwise). * - * It seems impossible to do, but if the actual cursor could ever be - * determined we might want to hash that info on window ID or something... */ void tree_descend_cursor(int *depth, Window *w, win_str_info_t *winfo) { Window r, c; @@ -14531,7 +16321,7 @@ void set_colormap(int reset) { while (c && tries++ < 16) { /* XXX XQueryTree somehow? */ XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &m); - if (c && XGetWindowAttributes(dpy, c, &attr)) { + if (valid_window(c, &attr, 0)) { if (attr.colormap && attr.map_installed) { cmap = attr.colormap; vis = attr.visual; @@ -15389,7 +17179,7 @@ XImage *initialize_xdisplay_fb(void) { if (subwin_wait_mapped) { wait_until_mapped(subwin); } - if (!valid_window((Window) subwin, NULL)) { + if (!valid_window((Window) subwin, NULL, 0)) { rfbLog("invalid sub-window: 0x%lx\n", subwin); X_UNLOCK; clean_up_exit(1); @@ -15515,7 +17305,7 @@ XImage *initialize_xdisplay_fb(void) { } if (! quiet) { - rfbLog("default visual ID: 0x%x\n", + rfbLog("Default visual ID: 0x%x\n", (int) XVisualIDFromVisual(default_visual)); } @@ -16393,7 +18183,7 @@ void solid_cde(char *color) { if (! twin) { twin = rootwin; } - if (! valid_window(twin, NULL)) { + if (! valid_window(twin, NULL, 0)) { continue; } @@ -17236,6 +19026,7 @@ static int scan_in_progress = 0; typedef struct tile_change_region { /* start and end lines, along y, of the changed area inside a tile. */ unsigned short first_line, last_line; + short first_x, last_x; /* info about differences along edges. */ unsigned short left_diff, right_diff; unsigned short top_diff, bot_diff; @@ -17604,12 +19395,12 @@ void initialize_polling_images(void) { * glued together. Ultimately, this information in a single hint is sent * to libvncserver rather than sending each tile separately. */ -static void create_tile_hint(int x, int y, int th, hint_t *hint) { +static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint) { int w = dpy_x - x; int h = dpy_y - y; - if (w > tile_x) { - w = tile_x; + if (w > tw) { + w = tw; } if (h > th) { h = th; @@ -17621,12 +19412,12 @@ static void create_tile_hint(int x, int y, int th, hint_t *hint) { hint->h = h; } -static void extend_tile_hint(int x, int y, int th, hint_t *hint) { +static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint) { int w = dpy_x - x; int h = dpy_y - y; - if (w > tile_x) { - w = tile_x; + if (w > tw) { + w = tw; } if (h > th) { h = th; @@ -17663,7 +19454,7 @@ static void save_hint(hint_t hint, int loc) { */ static void hint_updates(void) { hint_t hint; - int x, y, i, n, ty, th; + int x, y, i, n, ty, th, tx, tw; int hint_count = 0, in_run = 0; for (y=0; y < ntiles_y; y++) { @@ -17673,13 +19464,21 @@ static void hint_updates(void) { if (tile_has_diff[n]) { ty = tile_region[n].first_line; th = tile_region[n].last_line - ty + 1; + + tx = tile_region[n].first_x; + tw = tile_region[n].last_x - tx + 1; + if (tx < 0) { + tx = 0; + tw = tile_x; + } + if (! in_run) { - create_tile_hint( x * tile_x, - y * tile_y + ty, th, &hint); + create_tile_hint( x * tile_x + tx, + y * tile_y + ty, tw, th, &hint); in_run = 1; } else { - extend_tile_hint( x * tile_x, - y * tile_y + ty, th, &hint); + extend_tile_hint( x * tile_x + tx, + y * tile_y + ty, tw, th, &hint); } } else { if (in_run) { @@ -18388,6 +20187,7 @@ static int copy_tiles(int tx, int ty, int nt) { int w1, w2, dx1, dx2; /* tmps for normal and short tiles */ int pixelsize = bpp/8; int first_min, last_max; + int first_x = -1, last_x = -1; char *src, *dst, *s_src, *s_dst, *m_src, *m_dst; char *h_src, *h_dst; @@ -18427,7 +20227,7 @@ static int copy_tiles(int tx, int ty, int nt) { /* * If there are blackouts and this tile is completely covered * no need to poll screen or do anything else.. - * n.b. we are int single copy_tile mode: nt=1 + * n.b. we are in single copy_tile mode: nt=1 */ tile_has_diff[n] = 0; return(0); @@ -18445,7 +20245,7 @@ static int copy_tiles(int tx, int ty, int nt) { /* * If there are blackouts and this tile is partially covered * we should re-black-out the portion. - * n.b. we are int single copy_tile mode: nt=1 + * n.b. we are in single copy_tile mode: nt=1 */ int x1, x2, y1, y2, b; int w, s, fill = 0; @@ -18644,6 +20444,27 @@ static int copy_tiles(int tx, int ty, int nt) { 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); + if (nt == 1) { + /* + * optimization for tall skinny lines, e.g. wm + * frame. try to find first_x and last_x + * we like to think the above memcpy leaves the + * data we use below in the cache... (but it + * could be two 128 byte segments at 32bpp) + */ + int k, kx; + kx = pixelsize; + for (k=0; k<size_x; k++) { + if (memcmp(s_dst + k*kx, s_src + k*kx, kx)) { + if (first_x == -1 || k < first_x) { + first_x = k; + } + if (last_x == -1 || k > last_x) { + last_x = k; + } + } + } + } s_src += tile_row[nt]->bytes_per_line; s_dst += main_bytes_per_line; } @@ -18659,6 +20480,9 @@ static int copy_tiles(int tx, int ty, int nt) { tile_region[n+s].first_line = first_line[t]; tile_region[n+s].last_line = last_line[t]; + tile_region[n+s].first_x = first_x; + tile_region[n+s].last_x = last_x; + tile_region[n+s].top_diff = 0; tile_region[n+s].bot_diff = 0; if ( first_line[t] < tile_fuzz ) { @@ -19075,7 +20899,7 @@ int copy_snap(void) { int pixelsize = bpp/8; char *fbp; int i, y, block_size; - double dt = 0.0; + double dt; static int first = 1; if (! fs_factor) { @@ -19090,7 +20914,7 @@ int copy_snap(void) { fbp = snap_fb; y = 0; - dtime(&dt); + dtime0(&dt); X_LOCK; /* screen may be too big for 1 shm area, so broken into fs_factor */ @@ -19210,7 +21034,7 @@ static void ping_clients(int tile_cnt) { if (rfbMaxClientWait < 20000) { rfbMaxClientWait = 20000; - rfbLog("reset rfbMaxClientWait to %d ms.\n", + rfbLog("reset rfbMaxClientWait to %d msec.\n", rfbMaxClientWait); } if (tile_cnt) { @@ -19490,7 +21314,7 @@ int scan_for_updates(int count_only) { set_colormap(0); } if (use_xdamage) { - collect_xdamage(scan_count); + collect_xdamage(scan_count, 0); } } @@ -19506,6 +21330,14 @@ int scan_for_updates(int count_only) { tile_count = scan_display(scanlines[scan_count], 0); SCAN_FATAL(tile_count); + /* + * we do the XDAMAGE here too since after scan_display() + * there is a better chance we have received the events from + * the server. + */ + if (use_xdamage) { + collect_xdamage(scan_count, 1); + } if (count_only) { scan_in_progress = 0; fb_copy_in_progress = 0; @@ -19518,10 +21350,12 @@ int scan_for_updates(int count_only) { if (tile_has_diff[i]) { continue; } - if (tile_has_xdamage_diff[i] == 1) { - tile_has_xdamage_diff[i] = 2; + if (tile_has_xdamage_diff[i]) { tile_has_diff[i] = 1; - tile_count++; + if (tile_has_xdamage_diff[i] == 1) { + tile_has_xdamage_diff[i] = 2; + tile_count++; + } } } } @@ -19667,7 +21501,7 @@ int scan_for_updates(int count_only) { } } - hint_updates(); /* use krfb/x0rfbserver hints algorithm */ + hint_updates(); /* use x0rfbserver hints algorithm */ /* Work around threaded rfbProcessClientMessage() calls timeouts */ if (use_threads) { @@ -19984,23 +21818,133 @@ void do_gui(char *opts) { * user input handling heuristics */ +Window descend_pointer(int depth, Window start, char *name_info, int len) { + Window r, c, clast; + int i, rx, ry, wx, wy; + int written = 0, filled = 0; + char *store = NULL; + unsigned int m; + static XClassHint *classhint = NULL; + static char *nm_cache = NULL; + static int nm_cache_len = 0; + static Window prev_start = None; + + if (! classhint) { + classhint = XAllocClassHint(); + } + + if (! nm_cache) { + nm_cache = (char *)malloc(1024); + nm_cache_len = 1024; + nm_cache[0] = '\0'; + } + if (name_info && nm_cache_len < len) { + if (nm_cache) { + free(nm_cache); + } + nm_cache_len = 2*len; + nm_cache = (char *)malloc(nm_cache_len); + } + + if (name_info) { + if (start != None && start == prev_start) { + store = NULL; + strncpy(name_info, nm_cache, len); + } else { + store = name_info; + name_info[0] = '\0'; + } + } + + if (start != None) { + c = start; + if (name_info) { + prev_start = start; + } + } else { + c = rootwin; + } + + for (i=0; i<depth; i++) { + clast = c; + if (store && ! filled) { + char *name; + if (XFetchName(dpy, clast, &name)) { + int l = strlen(name); + if (written + l+2 < len) { + strcat(store, "^^"); + written += 2; + strcat(store, name); + written += l; + XFree(name); + } else { + filled = 1; + } + } + } + if (store && classhint && ! filled) { + classhint->res_name = NULL; + classhint->res_class = NULL; + if (XGetClassHint(dpy, clast, classhint)) { + char *p; + int l = 0; + if (classhint->res_class) { + l += strlen(classhint->res_class); + } + if (classhint->res_name) { + l += strlen(classhint->res_name); + } + if (written + l+4 < len) { + strcat(store, "##"); + p = classhint->res_class; + if (p) { + strcat(store, p); + XFree(p); + } + strcat(store, "++"); + p = classhint->res_name; + if (p) { + strcat(store, p); + XFree(p); + } + written += l+4; + } else { + filled = 1; + } + } + } + if (! XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &m)) { + break; + } + if (! c) { + break; + } + } + if (start != None && name_info) { + strncpy(nm_cache, name_info, nm_cache_len); + } + + return clast; +} + /* * For -wireframe: find the direct child of rootwin that has the * pointer, assume that is the WM frame that contains the application * (i.e. wm reparents the app toplevel) return frame position and size * if successful. */ - -int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h, Window *win) { +int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h, + Window *frame, Window *win) { Window r, c; XWindowAttributes attr; Bool ret; int rootx, rooty, wx, wy; unsigned int mask; - ret = XQueryPointer(dpy, rootwin, &r, &c, &rootx, &rooty, &wx, &wy, &mask); + ret = XQueryPointer(dpy, rootwin, &r, &c, &rootx, &rooty, &wx, &wy, + &mask); - *win = c; + *frame = c; /* current pointer position is returned too */ *px = rootx; @@ -20012,19 +21956,19 @@ int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h, Window *w } /* child window position and size */ - if (! valid_window(c, &attr)) { + if (! valid_window(c, &attr, 1)) { return 0; } -#if 0 - XTranslateCoordinates(dpy, c, rootwin, 0, 0, &rootx, &rooty, &c2); - *x = rootx; - *y = rooty; -#endif + *x = attr.x; *y = attr.y; *w = attr.width; *h = attr.height; + if (win != NULL) { + *win = descend_pointer(5, c, NULL, 0); + } + return 1; } @@ -20032,7 +21976,10 @@ static int defer_update_nofb = 6; /* defer a shorter time under -nofb */ int scrollcopyrect_top, scrollcopyrect_bot; int scrollcopyrect_left, scrollcopyrect_right; -double scr_key_time, scr_key_persist, scr_mouse_time, scr_mouse_persist; +double scr_key_time, scr_key_persist; +double scr_mouse_time, scr_mouse_persist, scr_mouse_maxtime; +double scr_mouse_pointer_delay; +double scr_key_bdpush_time, scr_mouse_bdpush_time; void parse_scroll_copyrect_str(char *scr) { char *p, *str; @@ -20074,20 +22021,25 @@ void parse_scroll_copyrect_str(char *scr) { /* key scrolling timing heuristics. */ if ((str = part[1]) != NULL) { - double t1, t2; - if (sscanf(str, "%lf+%lf", &t1, &t2) == 2) { + double t1, t2, t3; + if (sscanf(str, "%lf+%lf+%lf", &t1, &t2, &t3) == 3) { scr_key_time = t1; scr_key_persist = t2; + scr_key_bdpush_time = t3; } free(str); } /* mouse scrolling timing heuristics. */ if ((str = part[2]) != NULL) { - double t1, t2; - if (sscanf(str, "%lf+%lf", &t1, &t2) == 2) { + double t1, t2, t3, t4, t5; + if (sscanf(str, "%lf+%lf+%lf+%lf+%lf", &t1, &t2, &t3, &t4, + &t5) == 5) { scr_mouse_time = t1; scr_mouse_persist = t2; + scr_mouse_bdpush_time = t3; + scr_mouse_pointer_delay = t4; + scr_mouse_maxtime = t5; } free(str); } @@ -20303,6 +22255,240 @@ void set_scrollcopyrect_mode(char *str) { } } +int match_str_list(char *str, char **list) { + int i = 0, matched = 0; + + if (! list) { + return matched; + } + while (list[i] != NULL) { + if (!strcmp(list[i], "*")) { + matched = 1; + break; + } else if (strstr(str, list[i])) { + matched = 1; + break; + } + i++; + } + return matched; +} + +char **create_str_list(char *cslist) { + int i, n; + char *p, *str = strdup(cslist); + char **list = NULL; + + n = 1; + p = str; + while (*p != '\0') { + if (*p == ',') { + n++; + } + p++; + } + + list = (char **) malloc((n+1)*sizeof(char *)); + for(i=0; i < n+1; i++) { + list[i] = NULL; + } + + p = strtok(str, ","); + i = 0; + while (p && i < n) { + list[i++] = strdup(p); + p = strtok(NULL, ","); + } + free(str); + + return list; +} + +void initialize_scroll_keys(void) { + char *str, *p; + int i, nkeys = 0, saw_builtin = 0; + int ks_max = 2 * 0xFFFF; + + if (scroll_key_list) { + free(scroll_key_list); + scroll_key_list = NULL; + } + if (! scroll_key_list_str || *scroll_key_list_str == '\0') { + return; + } + + if (strstr(scroll_key_list_str, "builtin")) { + int k; + /* add in number of keysyms builtin gives */ + for (k=1; k<ks_max; k++) { + if (xrecord_scroll_keysym((rfbKeySym) k)) { + nkeys++; + } + } + } + + nkeys++; /* first key, i.e. no commas. */ + p = str = strdup(scroll_key_list_str); + while(*p) { + if (*p == ',') { + nkeys++; /* additional key. */ + } + p++; + } + + nkeys++; /* exclude/include 0 element */ + nkeys++; /* trailing NoSymbol */ + + scroll_key_list = (KeySym *)malloc(nkeys*sizeof(KeySym)); + for (i=0; i<nkeys; i++) { + scroll_key_list[i] = NoSymbol; + } + if (*str == '-') { + scroll_key_list[0] = 1; + p = strtok(str+1, ","); + } else { + p = strtok(str, ","); + } + i = 1; + while (p) { + if (!strcmp(p, "builtin")) { + int k; + if (saw_builtin) { + p = strtok(NULL, ","); + continue; + } + saw_builtin = 1; + for (k=1; k<ks_max; k++) { + if (xrecord_scroll_keysym((rfbKeySym) k)) { + scroll_key_list[i++] = (rfbKeySym) k; + } + } + } else { + int in; + if (sscanf(p, "%d", &in) == 1) { + scroll_key_list[i++] = (rfbKeySym) in; + } else if (sscanf(p, "0x%x", &in) == 1) { + scroll_key_list[i++] = (rfbKeySym) in; + } else if (XStringToKeysym(p) != NoSymbol) { + scroll_key_list[i++] = XStringToKeysym(p); + } else { + rfbLog("initialize_scroll_keys: skip unknown " + "keysym: %s\n", p); + } + } + p = strtok(NULL, ","); + } + free(str); +} + +void destroy_str_list(char **list) { + int i = 0; + if (! list) { + return; + } + while (list[i] != NULL) { + free(list[i++]); + } + free(list); +} + +void initialize_scroll_matches(void) { + char *str, *imp = "__IMPOSSIBLE_STR__"; + int i, n, nkey, nmouse; + + destroy_str_list(scroll_good_all); + scroll_good_all = NULL; + destroy_str_list(scroll_good_key); + scroll_good_key = NULL; + destroy_str_list(scroll_good_mouse); + scroll_good_mouse = NULL; + + destroy_str_list(scroll_skip_all); + scroll_skip_all = NULL; + destroy_str_list(scroll_skip_key); + scroll_skip_key = NULL; + destroy_str_list(scroll_skip_mouse); + scroll_skip_mouse = NULL; + + /* scroll_good: */ + if (scroll_good_str != NULL && *scroll_good_str != '\0') { + str = scroll_good_str; + } else { + str = scroll_good_str0; + } + scroll_good_all = create_str_list(str); + + nkey = 0; + nmouse = 0; + n = 0; + while (scroll_good_all[n] != NULL) { + char *s = scroll_good_all[n++]; + if (strstr(s, "KEY:") == s) nkey++; + if (strstr(s, "MOUSE:") == s) nmouse++; + } + if (nkey++) { + scroll_good_key = (char **)malloc(nkey*sizeof(char *)); + for (i=0; i<nkey; i++) scroll_good_key[i] = NULL; + } + if (nmouse++) { + scroll_good_mouse = (char **)malloc(nmouse*sizeof(char *)); + for (i=0; i<nmouse; i++) scroll_good_mouse[i] = NULL; + } + nkey = 0; + nmouse = 0; + for (i=0; i<n; i++) { + char *s = scroll_good_all[i]; + if (strstr(s, "KEY:") == s) { + scroll_good_key[nkey++] = strdup(s+strlen("KEY:")); + free(s); + scroll_good_all[i] = strdup(imp); + } else if (strstr(s, "MOUSE:") == s) { + scroll_good_mouse[nmouse++]=strdup(s+strlen("MOUSE:")); + free(s); + scroll_good_all[i] = strdup(imp); + } + } + + /* scroll_skip: */ + if (scroll_skip_str != NULL && *scroll_skip_str != '\0') { + str = scroll_skip_str; + } else { + str = scroll_skip_str0; + } + scroll_skip_all = create_str_list(str); + + nkey = 0; + nmouse = 0; + n = 0; + while (scroll_skip_all[n] != NULL) { + char *s = scroll_skip_all[n++]; + if (strstr(s, "KEY:") == s) nkey++; + if (strstr(s, "MOUSE:") == s) nmouse++; + } + if (nkey++) { + scroll_skip_key = (char **)malloc(nkey*sizeof(char *)); + for (i=0; i<nkey; i++) scroll_skip_key[i] = NULL; + } + if (nmouse++) { + scroll_skip_mouse = (char **)malloc(nmouse*sizeof(char *)); + for (i=0; i<nmouse; i++) scroll_skip_mouse[i] = NULL; + } + nkey = 0; + nmouse = 0; + for (i=0; i<n; i++) { + char *s = scroll_skip_all[i]; + if (strstr(s, "KEY:") == s) { + scroll_skip_key[nkey++] = strdup(s+strlen("KEY:")); + free(s); + scroll_skip_all[i] = strdup(imp); + } else if (strstr(s, "MOUSE:") == s) { + scroll_skip_mouse[nmouse++]=strdup(s+strlen("MOUSE:")); + free(s); + scroll_skip_all[i] = strdup(imp); + } + } +} + typedef struct saveline { int x0, y0, x1, y1; int shift; @@ -20564,7 +22750,11 @@ int direct_fb_copy(int x1, int y1, int x2, int y2, int mark) { char *src, *dst; int y, pixelsize = bpp/8; int xmin = -1, xmax = -1, ymin = -1, ymax = -1; - int do_cmp = 0; + int do_cmp = 2; + double tm; + int db = 0; + +if (db) dtime0(&tm); x1 = nfix(x1, dpy_x); y1 = nfix(y1, dpy_y); @@ -20663,36 +22853,149 @@ int direct_fb_copy(int x1, int y1, int x2, int y2, int mark) { if (mark) { mark_rect_as_modified(xmin, ymin, xmax, ymax, 1); } + +if (db) { + fprintf(stderr, "direct_fb_copy: %dx%d+%d+%d - %d %.4f\n", + x2 - x1, y2 - y1, x1, y1, mark, dtime(&tm)); +} + return 1; } +int do_bdpush(Window wm_win, int x0, int y0, int w0, int h0, int bdx, + int bdy, int bdskinny) { + + XWindowAttributes attr; + sraRectangleIterator *iter; + sraRect rect; + sraRegionPtr frame, whole, tmpregion; + int tx1, ty1, tx2, ty2; + static Window last_wm_win = None; + static int last_x, last_y, last_w, last_h; + int do_fb_push = 0; + int db = debug_scroll; + + + if (wm_win == last_wm_win) { + attr.x = last_x; + attr.y = last_y; + attr.width = last_w; + attr.height = last_h; + } else { + if (!valid_window(wm_win, &attr, 1)) { + return do_fb_push; + } + last_wm_win = wm_win; + last_x = attr.x; + last_y = attr.y; + last_w = attr.width; + last_h = attr.height; + } +if (db > 1) fprintf(stderr, "BDP %d %d %d %d %d %d %d %d %d %d %d\n", + x0, y0, w0, h0, bdx, bdy, bdskinny, last_x, last_y, last_w, last_h); + + /* wm frame: */ + tx1 = attr.x; + ty1 = attr.y; + tx2 = attr.x + attr.width; + ty2 = attr.y + attr.height; + + whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + frame = sraRgnCreateRect(tx1, ty1, tx2, ty2); + sraRgnAnd(frame, whole); + + /* scrolling window: */ + tmpregion = sraRgnCreateRect(x0, y0, x0 + w0, y0 + h0); + sraRgnAnd(tmpregion, whole); + + sraRgnSubtract(frame, tmpregion); + sraRgnDestroy(tmpregion); + + if (!sraRgnEmpty(frame)) { + double dt = 0.0, dm; + dtime0(&dm); + iter = sraRgnGetIterator(frame); + while (sraRgnIteratorNext(iter, &rect)) { + tx1 = rect.x1; + ty1 = rect.y1; + tx2 = rect.x2; + ty2 = rect.y2; + + if (bdskinny > 0) { + int ok = 0; + if (nabs(ty2-ty1) <= bdskinny) { + ok = 1; + } + if (nabs(tx2-tx1) <= bdskinny) { + ok = 1; + } + if (! ok) { + continue; + } + } + + if (bdx >= 0) { + if (bdx < tx1 || tx2 <= bdx) { + continue; + } + } + if (bdy >= 0) { + if (bdy < ty1 || ty2 <= bdy) { + continue; + } + } + + direct_fb_copy(tx1, ty1, tx2, ty2, 1); + + do_fb_push++; + dt += dtime(&dm); +if (db > 1) fprintf(stderr, " BDP(%d,%d-%d,%d) dt: %.4f\n", tx1, ty1, tx2, ty2, dt); + } + sraRgnReleaseIterator(iter); + } + sraRgnDestroy(whole); + sraRgnDestroy(frame); + + return do_fb_push; +} + #define PUSH_TEST(n) \ if (n) { \ - double dt = 0.0, tm = 0.0; dtime(&tm); \ + double dt = 0.0, tm; dtime0(&tm); \ fprintf(stderr, "PUSH---\n"); \ while (dt < 2.0) { rfbPE(50000); dt += dtime(&tm); } \ fprintf(stderr, "---PUSH\n"); \ } -int push_scr_ev(double bdpush) { +int push_scr_ev(double max_age, int type, int bdpush, int bdx, int bdy, + int bdskinny) { Window frame, win, win0; int x, y, w, h, wx, wy, ww, wh, dx, dy; int x0, y0, w0, h0; int nx, ny, nw, nh; - int dret = 1, obscured; + int dret = 1, do_fb_push = 0, obscured; int ev, ev_tot = scr_ev_cnt; - double st, dnow = 0.0; - int db = 0, rrate = get_read_rate(); - sraRegionPtr backfill, whole, tmpregion; - XWindowAttributes attr; - -//db = 1; - - dtime(&dnow); + double tm, dt, st, dnow, waittime = 0.125; + int db = debug_scroll, rrate = get_read_rate(); + sraRegionPtr backfill, whole, tmpregion, tmpregion2; + int link, latency, netrate; if (ev_tot == 0) { return dret; } + + link = link_rate(&latency, &netrate); + + if (link == LR_DIALUP) { + waittime *= 5; + } else if (link == LR_BROADBAND) { + waittime *= 3; + } else if (latency > 80 || netrate < 40) { + waittime *= 3; + } + + dtime0(&dnow); + backfill = sraRgnCreate(); whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); @@ -20719,10 +23022,10 @@ int push_scr_ev(double bdpush) { ny = scr_ev[ev].new_y; nw = scr_ev[ev].new_w; nh = scr_ev[ev].new_h; - st = (double) scr_ev[ev].t/1000.0; + st = scr_ev[ev].t; - if (dabs((dnow - servertime_diff) - st) > 0.15) { -if (db) fprintf(stderr, "push_scr_ev: TOO OLD: %.4f\n", (dnow - servertime_diff) - st); + if (dabs((dnow - servertime_diff) - st) > max_age) { +if (db) fprintf(stderr, "push_scr_ev: TOO OLD: %.4f :: (%.4f - %.4f) - %.4f \n", (dnow - servertime_diff) - st, dnow, servertime_diff, st); dret = 0; break; } else { @@ -20749,17 +23052,17 @@ if (db) fprintf(stderr, "push_scr_ev: NEGATIVE h/w: %d %d %d %d\n", w, h, ww, wh break; } -if (db) fprintf(stderr, "push_scr_ev: got: %d x: %4d y: %3d" - " w: %4d h: %3d dx: %d dy: %d %dx%d+%d+%d win: 0x%lx\n", - ev, x, y, w, h, dx, dy, w, h, x, y, win); +if (db > 1) fprintf(stderr, "push_scr_ev: got: %d x: %4d y: %3d" + " w: %4d h: %3d dx: %d dy: %d %dx%d+%d+%d win: 0x%lx\n", + ev, x, y, w, h, dx, dy, w, h, x, y, win); -if (db) fprintf(stderr, "------------ got: %d x: %4d y: %3d" - " w: %4d h: %3d %dx%d+%d+%d\n", - ev, wx, wy, ww, wh, ww, wh, wx, wy); +if (db > 1) fprintf(stderr, "------------ got: %d x: %4d y: %3d" + " w: %4d h: %3d %dx%d+%d+%d\n", + ev, wx, wy, ww, wh, ww, wh, wx, wy); -if (db) fprintf(stderr, "------------ got: %d x: %4d y: %3d" - " w: %4d h: %3d %dx%d+%d+%d\n", - ev, nx, ny, nw, nh, nw, nh, nx, ny); +if (db > 1) fprintf(stderr, "------------ got: %d x: %4d y: %3d" + " w: %4d h: %3d %dx%d+%d+%d\n", + ev, nx, ny, nw, nh, nw, nh, nx, ny); frame = None; if (xrecord_wm_window) { @@ -20778,15 +23081,65 @@ if (db) fprintf(stderr, "------------ got: %d x: %4d y: %3d" frame = win; } - if (try_copyrect(frame, x, y, w, h, dx, dy, &obscured)) { - fb_push(); + dtime0(&tm); + + tmpregion = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + tmpregion2 = sraRgnCreateRect(wx, wy, wx+ww, wy+wh); + sraRgnAnd(tmpregion2, whole); + sraRgnSubtract(tmpregion, tmpregion2); + sraRgnDestroy(tmpregion2); + + /* do the wm frame just incase the above is bogus too. */ + if (frame && frame != win) { + int k, gotk = -1; + for (k = stack_list_num - 1; k >= 0; k--) { + if (stack_list[k].win == frame && + stack_list[k].fetched && + stack_list[k].valid && + stack_list[k].map_state == IsViewable) { + gotk = k; + break; + } + } + if (gotk != -1) { + int tx1, ty1, tx2, ty2; + tx1 = stack_list[gotk].x; + ty1 = stack_list[gotk].y; + tx2 = tx1 + stack_list[gotk].width; + ty2 = ty1 + stack_list[gotk].height; + tmpregion2 = sraRgnCreateRect(tx1,ty1,tx2,ty2); + sraRgnAnd(tmpregion2, whole); + sraRgnSubtract(tmpregion, tmpregion2); + sraRgnDestroy(tmpregion2); + } + } + + /* + * XXX Need to also clip: + * children of win + * siblings of win higher in stacking order. + * ignore for now... probably will make some apps + * act very strangely. + */ + + if (try_copyrect(frame, x, y, w, h, dx, dy, &obscured, + tmpregion, waittime)) { + last_scroll_type = type; + dtime0(&last_scroll_event); + + do_fb_push++; urgent_update = 1; + sraRgnDestroy(tmpregion); + PUSH_TEST(0); } else { dret = 0; + sraRgnDestroy(tmpregion); break; } + dt = dtime(&tm); +if (0) fprintf(stderr, " try_copyrect dt: %.4f\n", dt); if (ev > 0) { sraRgnOffset(backfill, dx, dy); @@ -20799,12 +23152,12 @@ PUSH_TEST(0); sraRgnDestroy(tmpregion); } + /* try to update the backfill region (new window contents) */ if (dret != 0) { double est, win_area = 0.0, area = 0.0; sraRectangleIterator *iter; sraRect rect; int tx1, ty1, tx2, ty2; - double tm = 0.0, dt = 0.0; tmpregion = sraRgnCreateRect(x0, y0, x0 + w0, y0 + h0); sraRgnAnd(tmpregion, whole); @@ -20837,7 +23190,7 @@ PUSH_TEST(0); est = (area * (bpp/8)) / (1000000.0 * rrate); if (db) fprintf(stderr, " area %.1f win_area %.1f est: %.4f", area, win_area, est); - if (area > 0.85 * win_area) { + if (area > 0.90 * win_area) { if (db) fprintf(stderr, " AREA_TOO_MUCH"); dret = 0; } else if (est > 0.6) { @@ -20846,7 +23199,7 @@ if (db) fprintf(stderr, " EST_TOO_LARGE"); } else if (area <= 0.0) { ; } else { - dtime(&tm); + dtime0(&tm); iter = sraRgnGetIterator(backfill); while (sraRgnIteratorNext(iter, &rect)) { tx1 = rect.x1; @@ -20861,13 +23214,12 @@ if (db) fprintf(stderr, " EST_TOO_LARGE"); dtime(&tm); if (db) fprintf(stderr, " DFC(%d,%d-%d,%d)", tx1, ty1, tx2, ty2); direct_fb_copy(tx1, ty1, tx2, ty2, 1); - dt = dtime(&tm); - fb_push(); + do_fb_push++; PUSH_TEST(0); } dt = dtime(&tm); sraRgnReleaseIterator(iter); -if (db) fprintf(stderr, " dt: %.4f", dt); +if (0) fprintf(stderr, " dfc---- dt: %.4f", dt); } if (db && dret) fprintf(stderr, " **** dret=%d", dret); @@ -20875,82 +23227,50 @@ if (db && !dret) fprintf(stderr, " ---- dret=%d", dret); if (db) fprintf(stderr, "\n"); } -if (db || bdpush > 0.0) fprintf(stderr, "BDPUSH-TIME: %.3f 0x%lx\n", bdpush, xrecord_wm_window); - - if (bdpush > 0.0 && xrecord_wm_window != None && - valid_window(xrecord_wm_window, &attr)) { - - double wm_area = 0.0, win_area = 0.0, d_area; - sraRectangleIterator *iter; - sraRect rect; - sraRegionPtr frame; - int tx1, ty1, tx2, ty2; - - /* wm frame: */ - tx1 = attr.x; - ty1 = attr.y; - tx2 = attr.x + attr.width; - ty2 = attr.y + attr.height; - - frame = sraRgnCreateRect(tx1, ty1, tx2, ty2); - sraRgnAnd(frame, whole); - - iter = sraRgnGetIterator(frame); - while (sraRgnIteratorNext(iter, &rect)) { - tx1 = rect.x1; - ty1 = rect.y1; - tx2 = rect.x2; - ty2 = rect.y2; - - wm_area += (tx2 - tx1)*(ty2 - ty1); - } - sraRgnReleaseIterator(iter); - - /* scrolling window: */ - tmpregion = sraRgnCreateRect(x0, y0, x0 + w0, y0 + h0); - sraRgnAnd(tmpregion, whole); +if (db && bdpush) fprintf(stderr, "BDPUSH-TIME: 0x%lx\n", xrecord_wm_window); - iter = sraRgnGetIterator(tmpregion); - while (sraRgnIteratorNext(iter, &rect)) { - tx1 = rect.x1; - ty1 = rect.y1; - tx2 = rect.x2; - ty2 = rect.y2; - - win_area += (tx2 - tx1)*(ty2 - ty1); - } - sraRgnReleaseIterator(iter); - - d_area = wm_area - win_area; - sraRgnSubtract(frame, tmpregion); - sraRgnDestroy(tmpregion); - -if (db) fprintf(stderr, "d_area: %.4f wm_area: %.4f\n", d_area, wm_area); + if (bdpush && xrecord_wm_window != None) { + int x, y, w, h; + x = scr_ev[0].x; + y = scr_ev[0].y; + w = scr_ev[0].w; + h = scr_ev[0].h; + do_fb_push += do_bdpush(xrecord_wm_window, x, y, w, h, + bdx, bdy, bdskinny); + } - if (d_area >= 0.0 && d_area < bdpush * wm_area && - !sraRgnEmpty(frame)) { - double dt = 0.0, dm = 0.0; - dtime(&dm); - iter = sraRgnGetIterator(frame); - while (sraRgnIteratorNext(iter, &rect)) { - tx1 = rect.x1; - ty1 = rect.y1; - tx2 = rect.x2; - ty2 = rect.y2; - direct_fb_copy(tx1, ty1, tx2, ty2, 1); - fb_push(); - dt += dtime(&dm); -if (db) fprintf(stderr, " BDP(%d,%d-%d,%d) dt: %.4f\n", tx1, ty1, tx2, ty2, dt); - } - sraRgnReleaseIterator(iter); - } - sraRgnDestroy(frame); + if (do_fb_push) { + dtime0(&tm); + fb_push(); + dt = dtime(&tm); +if (0) fprintf(stderr, " fb_push dt: %.4f", dt); } sraRgnDestroy(backfill); sraRgnDestroy(whole); return dret; } + +void get_client_regions(int *req, int *mod, int *cpy, int *num) { + + rfbClientIteratorPtr i; + rfbClientPtr cl; + + *req = 0; + *mod = 0; + *cpy = 0; + *num = 0; + + i = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(i)) ) { + *req += sraRgnCountRects(cl->requestedRegion); + *mod += sraRgnCountRects(cl->modifiedRegion); + *cpy += sraRgnCountRects(cl->copyRegion); + *num += 1; + } + rfbReleaseClientIterator(i); +} + /* * Wrapper to apply the rfbDoCopyRegion taking into account if scaling * is being done. Note that copyrect under the scaling case is often @@ -20962,13 +23282,22 @@ void do_copyregion(sraRegionPtr region, int dx, int dy) { int Bpp = bpp/8; int x1, y1, x2, y2, w, stride; int sx1, sy1, sx2, sy2, sdx, sdy; + int req, mod, cpy, ncli; char *dst, *src; if (!scaling || rfb_fb == main_fb) { + /* normal case */ + get_client_regions(&req, &mod, &cpy, &ncli); +if (debug_scroll > 1) fprintf(stderr, "<<<-rfbDoCopyRect req: %d mod: %d cpy: %d\n", req, mod, cpy); rfbDoCopyRegion(screen, region, dx, dy); + + get_client_regions(&req, &mod, &cpy, &ncli); +if (debug_scroll > 1) fprintf(stderr, ">>>-rfbDoCopyRect req: %d mod: %d cpy: %d\n", req, mod, cpy); + return; } + /* rarer case, we need to call rfbDoCopyRect with scaled xy */ stride = dpy_x * Bpp; iter = sraRgnGetIterator(region); @@ -21012,65 +23341,45 @@ void do_copyregion(sraRegionPtr region, int dx, int dy) { sraRgnReleaseIterator(iter); } -void fb_push0(int first_ms, int loop_ms, int loop_max) { - int t; - fb_update_sent(NULL); - rfbPE(1000 * first_ms); /* long select */ - for (t=0; t<loop_max; t++) { - if (fb_update_sent(NULL)) { -if (1 || debug_wireframe) fprintf(stderr, "FB_UPDATE_SENT: t=%d\n", t); +void fb_push_wait(double max_wait) { + double tm, dt = 0.0; + int req, mod, cpy, ncli; + + dtime0(&tm); + while (dt < max_wait) { + rfbCFD(0); + get_client_regions(&req, &mod, &cpy, &ncli); + if (! cpy) { break; } - rfbPE(1000 * loop_ms); /* short selects. */ - } -} - -void get_client_regions(int *req, int *mod, int *cpy) { - - rfbClientIteratorPtr i; - rfbClientPtr cl; - - *req = 0; - *mod = 0; - *cpy = 0; - - i = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(i)) ) { - *req += sraRgnCountRects(cl->requestedRegion); - *mod += sraRgnCountRects(cl->modifiedRegion); - *cpy += sraRgnCountRects(cl->copyRegion); + usleep(1000); + fb_push(); + dt += dtime(&tm); } - rfbReleaseClientIterator(i); } void fb_push(void) { char *httpdir = screen->httpDir; int defer = screen->deferUpdateTime; - int i, req0, mod0, cpy0, req1, mod1, cpy1; - int db = 0; -//db = 1; + int req0, mod0, cpy0, req1, mod1, cpy1, ncli; + int db = (debug_scroll || debug_wireframe); screen->httpDir = NULL; screen->deferUpdateTime = 0; - get_client_regions(&req0, &mod0, &cpy0); +if (db) get_client_regions(&req0, &mod0, &cpy0, &ncli); rfbPE(0); screen->httpDir = httpdir; screen->deferUpdateTime = defer; - get_client_regions(&req1, &mod1, &cpy1); -if (db) fprintf(stderr, "\nFB_push: req: %d/%d mod: %d/%d cpy: %d/%d\n", - req0, req1, mod0, mod1, cpy0, cpy1); +if (db) { + get_client_regions(&req1, &mod1, &cpy1, &ncli); + fprintf(stderr, "\nFB_push: req: %d/%d mod: %d/%d cpy: %d/%d %.4f\n", + req0, req1, mod0, mod1, cpy0, cpy1, dnow() - x11vnc_start); +} - for (i = 0; i < 0; i++) { - get_client_regions(&req0, &mod0, &cpy0); - rfbCFD(1000); - get_client_regions(&req1, &mod1, &cpy1); -if (db) fprintf(stderr, "-------: req: %d/%d mod: %d/%d cpy: %d/%d\n", - req0, req1, mod0, mod1, cpy0, cpy1); - } } /* @@ -21092,36 +23401,168 @@ int crfix(int x, int dx, int Lx) { return x; } +typedef struct scroll_result { + Window win; + double time; + int result; +} scroll_result_t; + +#define SCR_RESULTS_MAX 64 +scroll_result_t scroll_results[SCR_RESULTS_MAX]; + +int scrollability(Window win, int set) { + double oldest = -1.0; + int i, index = -1, next_index = -1; + + if (win == None) { + return 0; + } + if (set == SCR_NONE) { + /* lookup case */ + for (i=0; i<SCR_RESULTS_MAX; i++) { + if (win == scroll_results[i].win) { + return scroll_results[i].result; + } + } + return 0; + } + + for (i=0; i<SCR_RESULTS_MAX; i++) { + if (oldest == -1.0 || scroll_results[i].time < oldest) { + next_index = i; + oldest = scroll_results[i].time; + } + if (win == scroll_results[i].win) { + index = i; + break; + } + } + + if (set == SCR_SUCCESS) { + set = 1; + } else if (set == SCR_FAIL) { + set = -1; + } else { + set = 0; + } + if (index == -1) { + scroll_results[next_index].win = win; + scroll_results[next_index].time = dnow(); + scroll_results[next_index].result = set; + } else { + if (scroll_results[index].result == 1) { + /* + * once a success, always a success, until they + * forget about us... + */ + set = 1; + } else { + scroll_results[index].time = dnow(); + scroll_results[index].result = set; + } + } + return set; +} + +int eat_pointer(int max_ptr_eat, int keep) { + int i, count = 0, gp = got_pointer_input; + + for (i=0; i<max_ptr_eat; i++) { + rfbCFD(0); + if (got_pointer_input > gp) { + count++; +if (0) fprintf(stderr, "GP*-%d\n", i); + gp = got_pointer_input; + } else if (i > keep) { + break; + } + } + return count; +} + +void set_bdpush(int type, double *last_bdpush, int *pushit) { + double now, delay = 0.0; + int link, latency, netrate; + + *pushit = 0; + + if (type == SCR_MOUSE) { + delay = scr_mouse_bdpush_time; + } else if (type == SCR_KEY) { + delay = scr_key_bdpush_time; + } + + link = link_rate(&latency, &netrate); + if (link == LR_DIALUP) { + delay *= 1.5; + } else if (link == LR_BROADBAND) { + delay *= 1.25; + } + + dtime0(&now); + if (delay > 0.0 && now > *last_bdpush + delay) { + *pushit = 1; + *last_bdpush = now; + } +} + +void mark_for_xdamage(int x, int y, int w, int h) { + int tx1, ty1, tx2, ty2; + sraRegionPtr tmpregion; + + if (! use_xdamage) { + return; + } + + tx1 = nfix(x, dpy_x); + ty1 = nfix(y, dpy_y); + tx2 = nfix(x + w, dpy_x); + ty2 = nfix(y + h, dpy_y); + + tmpregion = sraRgnCreateRect(tx1, ty1, tx2, ty2); + add_region_xdamage(tmpregion); + sraRgnDestroy(tmpregion); +} + int check_xrecord_keys(void) { - double spin = 0.0, tm = 0.0; - int gk, gk0, extra_keys = 0, ret = 0; - int db = debug_scroll; - int get_out = 1, got_one = 0, flush1 = 0, flush2 = 0; + static int last_wx, last_wy, last_ww, last_wh; + double spin = 0.0, tm, tnow; + int scr_cnt = 0, input = 0, scroll_rep; + int get_out, got_one = 0, flush1 = 0, flush2 = 0; + int gk, gk0, ret = 0, db = debug_scroll; + int fail = 0; + int link, latency, netrate; + static double last_key_scroll = 0.0; static double persist_start = 0.0; - double this_scroll, scroll_persist = scr_key_persist; + static double last_bdpush = 0.0; + static int persist_count = 0; + double last_scroll, scroll_persist = scr_key_persist; double spin_fac = 1.0, scroll_fac = 2.0; - double max_spin, tnow = 0.0; - -//db = 1; + double max_spin; + double max_long_spin = 0.3; + get_out = 1; if (got_keyboard_input) { get_out = 0; } - dtime(&tnow); + dtime0(&tnow); if (tnow < last_key_scroll + scroll_persist) { get_out = 0; } if (get_out) { persist_start = 0.0; - xrecord_watch(0); + persist_count = 0; + last_bdpush = 0.0; + if (xrecording) { + xrecord_watch(0, SCR_KEY); + } return 0; } -if (db) fprintf(stderr, "xrecord_set_by_mouse: %d\n", xrecord_set_by_mouse); -if (db) fprintf(stderr, "xrecord_set_by_keys: %d\n", xrecord_set_by_keys); + scroll_rep = scrollability(xrecord_ptr_window, SCR_NONE) + 1; max_spin = scr_key_time; @@ -21130,25 +23571,36 @@ if (db) fprintf(stderr, "xrecord_set_by_keys: %d\n", xrecord_set_by_keys); } else if (xrecord_scroll_keysym(last_keysym)) { spin_fac = scroll_fac; } + if (max_spin > max_long_spin) { + max_spin = max_long_spin; + } + + /* XXX use this somehow */ + link = link_rate(&latency, &netrate); gk = gk0 = got_keyboard_input; - dtime(&tm); + dtime0(&tm); -if (db) fprintf(stderr, "check_xrecord: LOOP: scr_ev_cnt: %d max: %.3f\n", +if (db) fprintf(stderr, "check_xrecord_keys: BEGIN LOOP: scr_ev_cnt: %d max: %.3f\n", scr_ev_cnt, max_spin); while (1) { if (scr_ev_cnt) { got_one = 1; - this_scroll = 0.0; - dtime(&this_scroll); + + scrollability(xrecord_ptr_window, SCR_SUCCESS); + scroll_rep = 2; + + dtime0(&last_scroll); + last_key_scroll = last_scroll; + scr_cnt++; break; } X_LOCK; - XFlush(dpy); flush1 = 1; + XFlush(dpy); X_UNLOCK; if (use_threads) { @@ -21158,207 +23610,421 @@ if (db) fprintf(stderr, "check_xrecord: LOOP: scr_ev_cnt: %d max: %.3f\n", } spin += dtime(&tm); - if (spin >= max_spin * spin_fac) { -if (db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin, - max_spin * spin_fac); - break; - } - + X_LOCK; if (got_keyboard_input > gk) { gk = got_keyboard_input; + input++; if (xrecord_scroll_keysym(last_keysym)) { spin_fac = scroll_fac; } - extra_keys++; -if (db) fprintf(stderr, "check_xrecord: more keys: %d %.3f\n", extra_keys, - spin); +if (db) fprintf(stderr, "check_xrecord: more keys: %.3f\n", spin); flush2 = 1; - } - - X_LOCK; - if (flush2) { XFlush(dpy); } XRecordProcessReplies(rdpy_data); X_UNLOCK; - } -if (db) fprintf(stderr, " f1: %d f2: %d spin: %.4f\n", flush1, flush2, spin); - /* since we've flushed it, we might as well avoid -input_skip */ - if (flush1 || flush2) { - got_keyboard_input = 0; - got_pointer_input = 0; + if (spin >= max_spin * spin_fac) { +if (db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin, + max_spin * spin_fac); + fail = 1; + break; + } } if (scr_ev_cnt) { - int dret; - double bdpush = 0.0; - static double last_border_push = 0.0; - - if (persist_start > 0.0 && - this_scroll > last_border_push + 1.00) { - bdpush = 0.0; - last_border_push = this_scroll; + int dret, ev = scr_ev_cnt - 1; + int bdx, bdy, bdskinny, bdpush = 0; + + last_wx = scr_ev[ev].win_x; + last_wy = scr_ev[ev].win_y; + last_ww = scr_ev[ev].win_w; + last_wh = scr_ev[ev].win_h; + + /* assume scrollbar on rhs: */ + bdx = last_wx + last_ww + 3; + bdy = last_wy + last_wh/2; + bdskinny = 32; + + if (persist_start == 0.0) { + bdpush = 0; + } else { + set_bdpush(SCR_KEY, &last_bdpush, &bdpush); } - dret = push_scr_ev(bdpush); + + dret = push_scr_ev(0.25, SCR_KEY, bdpush, bdx, bdy, bdskinny); + ret = 1 + dret; scr_ev_cnt = 0; } + if ((got_one && ret < 2) || persist_count) { + mark_for_xdamage(last_wx, last_wy, last_ww, last_wh); + } + + if (fail) { + scrollability(xrecord_ptr_window, SCR_FAIL); + } + if (xrecording) { if (ret < 2) { - xrecord_watch(0); + xrecord_watch(0, SCR_KEY); } } - if (this_scroll > 0.0) { - last_key_scroll = this_scroll; - } - if (ret == 2) { if (persist_start == 0.0) { dtime(&persist_start); + last_bdpush = persist_start; } } else { persist_start = 0.0; + last_bdpush = 0.0; + } + + /* since we've flushed it, we might as well avoid -input_skip */ + if (flush1 || flush2) { + got_keyboard_input = 0; + got_pointer_input = 0; } return ret; } int check_xrecord_mouse(void) { - double spin = 0.0, tm = 0.0; - int gp, gp0, ret = 0; - int db = debug_scroll; - int flush1 = 0, flush2 = 0; - int get_out = 1, got_one = 0; + static int last_wx, last_wy, last_ww, last_wh; + double spin = 0.0, tm, tnow; + int i, scr_cnt = 0, input = 0, scroll_rep; + int get_out, got_one = 0, flush1 = 0, flush2 = 0; + int gp, gp0, ret = 0, db = debug_scroll; + int gk, gk0; + int fail = 0; + int link, latency, netrate; + + int start_x, start_y, last_x, last_y; static double last_mouse_scroll = 0.0; - static double persist_start = 0.0; - double this_scroll, scroll_persist = scr_mouse_persist; - double spin_fac = 1.0; - double max_spin, tnow = 0.0; - -//db = 1; - + double last_scroll; + double max_spin[3], max_long[3], persist[3]; + double flush1_time = 0.01; + static double last_flush = 0.0; + double last_bdpush = 0.0, button_up_time = 0.0; + int button_mask_save; + int already_down = 0, max_ptr_eat = 20; + static int want_back_in = 0; + int came_back_in; + + get_out = 1; if (button_mask) { get_out = 0; } - dtime(&tnow); - if (tnow < last_mouse_scroll + scroll_persist) { + if (want_back_in) { get_out = 0; - } + } + dtime0(&tnow); +if (0) fprintf(stderr, "check_xrecord_mouse: IN xrecording: %d\n", xrecording); if (get_out) { - persist_start = 0.0; - xrecord_watch(0); + if (xrecording) { + xrecord_watch(0, SCR_MOUSE); + } return 0; } -if (db) fprintf(stderr, "xrecord_set_by_mouse: %d\n", xrecord_set_by_mouse); -if (db) fprintf(stderr, "xrecord_set_by_keys: %d\n", xrecord_set_by_keys); + scroll_rep = scrollability(xrecord_ptr_window, SCR_NONE) + 1; - max_spin = scr_mouse_time; + if (button_mask_prev) { + already_down = 1; + } + if (want_back_in) { + came_back_in = 1; + } else { + came_back_in = 0; + } + want_back_in = 0; - if (tnow < last_mouse_scroll + scroll_persist) { - max_spin = 1.25*(tnow - last_mouse_scroll); + /* + * set up times for the various "reputations" + * + * 0 => -1, has been tried but never found a scroll. + * 1 => 0, has not been tried. + * 2 => +1, has been tried and found a scroll. + */ + + /* first spin-out time (no events) */ + max_spin[0] = 1*scr_mouse_time; + max_spin[1] = 2*scr_mouse_time; + max_spin[2] = 4*scr_mouse_time; + if (!already_down) { + for (i=0; i<3; i++) { + max_spin[i] *= 1.5; + } + } + + /* max time between events */ + persist[0] = 1*scr_mouse_persist; + persist[1] = 2*scr_mouse_persist; + persist[2] = 4*scr_mouse_persist; + + /* absolute max time in the loop */ + max_long[0] = scr_mouse_maxtime; + max_long[1] = scr_mouse_maxtime; + max_long[2] = scr_mouse_maxtime; + + pointer_flush_delay = scr_mouse_pointer_delay; + + /* slow links: */ + link = link_rate(&latency, &netrate); + if (link == LR_DIALUP) { + for (i=0; i<3; i++) { + max_spin[i] *= 2.0; + } + pointer_flush_delay *= 2; + } else if (link == LR_BROADBAND) { + pointer_flush_delay *= 2; } gp = gp0 = got_pointer_input; - dtime(&tm); + gk = gk0 = got_keyboard_input; + dtime0(&tm); -if (db) fprintf(stderr, "check_xrecord: LOOP: scr_ev_cnt: %d max: %.3f\n", - scr_ev_cnt, max_spin); + /* + * this is used for border pushes (bdpush) to guess location + * of scrollbar (region rects containing this point are pushed). + */ + last_x = start_x = cursor_x; + last_y = start_y = cursor_y; - while (1) { +if (db) fprintf(stderr, "check_xrecord_mouse: BEGIN LOOP: scr_ev_cnt: %d max: %.3f\n", + scr_ev_cnt, max_spin[scroll_rep]); + while (1) { + double spin_check; if (scr_ev_cnt) { + int dret, ev = scr_ev_cnt - 1; + int bdpush = 0, bdx, bdy, bdskinny; + double tm, dt; + got_one = 1; - this_scroll = 0.0; - dtime(&this_scroll); - break; + scrollability(xrecord_ptr_window, SCR_SUCCESS); + scroll_rep = 2; + + scr_cnt++; + + dtime0(&last_scroll); + last_mouse_scroll = last_scroll; + + if (last_bdpush == 0.0) { + last_bdpush = last_scroll; + } + + bdx = start_x; + bdy = start_y; + bdskinny = 32; + + set_bdpush(SCR_MOUSE, &last_bdpush, &bdpush); + + dtime0(&tm); + + dret = push_scr_ev(0.35, SCR_MOUSE, bdpush, bdx, + bdy, bdskinny); + ret = 1 + dret; + + dt = dtime(&tm); + +if (db) fprintf(stderr, " dret: %d scr_ev_cnt: %d dt: %.4f\n", + dret, scr_ev_cnt, dt); + + last_wx = scr_ev[ev].win_x; + last_wy = scr_ev[ev].win_y; + last_ww = scr_ev[ev].win_w; + last_wh = scr_ev[ev].win_h; + scr_ev_cnt = 0; + + if (! dret) { + break; + } + if (0 && button_up_time > 0.0) { + /* we only take 1 more event with button up */ +if (db) fprintf(stderr, "check_xrecord: BUTTON_UP_SCROLL: %.3f\n", spin); + break; + } } - X_LOCK; - XFlush(dpy); - flush1 = 1; - X_UNLOCK; + + if (! flush1) { + if (! already_down || (!scr_cnt && spin>flush1_time)) { + flush1 = 1; + X_LOCK; + XFlush(dpy); + X_UNLOCK; + dtime0(&last_flush); + } + } if (use_threads) { usleep(1000); } else { rfbCFD(1000); + rfbCFD(0); } spin += dtime(&tm); - if (spin >= max_spin * spin_fac) { -if (db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin, - max_spin * spin_fac); - break; - } if (got_pointer_input > gp) { - gp = got_pointer_input; flush2 = 1; + input += eat_pointer(max_ptr_eat, 1); + gp = got_pointer_input; } - - X_LOCK; - if (flush2) { - XFlush(dpy); + if (got_keyboard_input > gk) { + gk = got_keyboard_input; + input++; } + X_LOCK; XRecordProcessReplies(rdpy_data); X_UNLOCK; - if (! button_mask) { - break; + + if (! input) { + spin_check = 1.5 * max_spin[scroll_rep]; + } else { + spin_check = max_spin[scroll_rep]; } - } -if (db) fprintf(stderr, " f1: %d f2: %d\n", flush1, flush2); - /* since we've flushed it, we might as well avoid -input_skip */ - if (flush1 || flush2) { - got_keyboard_input = 0; - got_pointer_input = 0; - } + if (button_up_time > 0.0) { + if (tm > button_up_time + max_spin[scroll_rep]) { +if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-BUTTON_UP: %.3f/%.3f\n", spin, tm - button_up_time); + break; + } + } else if (!scr_cnt) { + if (spin >= spin_check) { - if (scr_ev_cnt) { - int dret; - double bdpush = 0.0; - static double last_border_push = 0.0; +if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-1: %.3f/%.3f\n", spin, spin_check); + fail = 1; + break; + } + } else { + if (tm >= last_scroll + persist[scroll_rep]) { - if (persist_start > 0.0 && - this_scroll > last_border_push + 1.00) { - bdpush = 0.0; - last_border_push = this_scroll; +if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-2: %.3f/%.3f\n", spin, tm - last_scroll); + break; + } + } + if (spin >= max_long[scroll_rep]) { + +if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-3: %.3f/%.3f\n", spin, max_long[scroll_rep]); + break; } - dret = push_scr_ev(bdpush); + if (! button_mask) { - dret = 0; + int doflush = 0; + if (button_up_time > 0.0) { + ; + } else if (came_back_in) { + dtime0(&button_up_time); + doflush = 1; + } else if (last_x == cursor_x && last_y == cursor_y) { +if (db) fprintf(stderr, "check_xrecord: BUTTON-UP: %.3f/%.3f %d/%d %d/%d\n", spin, max_long[scroll_rep], last_x, last_y, cursor_x, cursor_y); + break; + } else { +if (db) fprintf(stderr, "check_xrecord: BUTTON-UP-KEEP-GOING: %.3f/%.3f %d/%d %d/%d\n", spin, max_long[scroll_rep], last_x, last_y, cursor_x, cursor_y); + doflush = 1; + dtime0(&button_up_time); + } + if (doflush) { + flush1 = 1; + X_LOCK; + XFlush(dpy); + X_UNLOCK; + dtime0(&last_flush); + } } - ret = 1 + dret; - scr_ev_cnt = 0; + + last_x = cursor_x; + last_y = cursor_y; } - if (xrecording) { - if (ret < 2) { - xrecord_watch(0); - } + if (fail) { + scrollability(xrecord_ptr_window, SCR_FAIL); } - if (this_scroll > 0.0) { - last_mouse_scroll = this_scroll; + if (got_one) { + mark_for_xdamage(last_wx, last_wy, last_ww, last_wh); } - if (ret == 2) { - if (persist_start == 0.0) { - dtime(&persist_start); + /* flush any remaining pointer events. */ + button_mask_save = button_mask; + pointer_queued_sent = 0; + last_x = cursor_x; + last_y = cursor_y; + pointer(-1, 0, 0, NULL); + pointer_flush_delay = 0.0; + + if (xrecording && pointer_queued_sent && button_mask_save && + (last_x != cursor_x || last_y != cursor_y) ) { +if (db) fprintf(stderr, " pointer() push yields events on: ret=%d\n", ret); + if (ret == 2) { +if (db) fprintf(stderr, " we decide to send ret=3\n"); + want_back_in = 1; + ret = 3; + flush2 = 1; + } else { + if (ret) { + ret = 1; + } else { + ret = 0; + } + xrecord_watch(0, SCR_MOUSE); } } else { - persist_start = 0.0; + if (ret) { + ret = 1; + } else { + ret = 0; + } + if (xrecording) { + xrecord_watch(0, SCR_MOUSE); + } } - return ret; + if (flush2) { + X_LOCK; + XFlush(dpy); + XFlush(rdpy_ctrl); + X_UNLOCK; + + flush2 = 1; + dtime0(&last_flush); + +if (db) fprintf(stderr, "FLUSH-2\n"); + } + + /* since we've flushed it, we might as well avoid -input_skip */ + if (flush1 || flush2) { + got_keyboard_input = 0; + got_pointer_input = 0; + } + + if (ret) { + return ret; + } else if (scr_cnt) { + return 1; + } else { + return 0; + } } int check_xrecord(void) { - int watch_keys = 0, watch_mouse = 0; + int watch_keys = 0, watch_mouse = 0, consider_mouse; + static int first = 1; + static int mouse_wants_back_in = 0; + + if (first) { + int i; + for (i=0; i<SCR_RESULTS_MAX; i++) { + scroll_results[i].win = None; + scroll_results[i].time = 0.0; + scroll_results[i].result = 0; + } + first = 0; + } if (! use_xrecord) { return 0; @@ -21367,6 +24033,8 @@ int check_xrecord(void) { return 0; } +if (0) fprintf(stderr, "check_xrecord: IN xrecording: %d\n", xrecording); + if (! xrecording) { return 0; } @@ -21380,11 +24048,26 @@ int check_xrecord(void) { watch_mouse = 1; } - if (watch_mouse && button_mask && xrecord_set_by_mouse) { - return check_xrecord_mouse(); + if (button_mask || mouse_wants_back_in) { + consider_mouse = 1; + } else { + consider_mouse = 0; + } +if (0) fprintf(stderr, "check_xrecord: button_mask: %d mouse_wants_back_in: %d\n", button_mask, mouse_wants_back_in); + + if (watch_mouse && consider_mouse && xrecord_set_by_mouse) { + int ret = check_xrecord_mouse(); + if (ret == 3) { + mouse_wants_back_in = 1; + } else { + mouse_wants_back_in = 0; + } + return ret; } else if (watch_keys && xrecord_set_by_keys) { + mouse_wants_back_in = 0; return check_xrecord_keys(); } else { + mouse_wants_back_in = 0; return 0; } } @@ -21404,13 +24087,26 @@ int check_xrecord(void) { } int try_copyrect(Window frame, int x, int y, int w, int h, int dx, int dy, - int *obscured) { + int *obscured, sraRegionPtr extra_clip, double max_wait) { static int dt_bad = 0; static time_t dt_bad_check = 0; int x1, y1, x2, y2, sent_copyrect = 0; + int req, mod, cpy, ncli; + double tm, dt; DB_SET + get_client_regions(&req, &mod, &cpy, &ncli); + if (cpy) { + /* one is still pending... try to force it out: */ + fb_push_wait(max_wait); + + get_client_regions(&req, &mod, &cpy, &ncli); + } + if (cpy) { + return 0; + } + *obscured = 0; /* * XXX KDE and xfce do some weird things with the @@ -21430,7 +24126,7 @@ int try_copyrect(Window frame, int x, int y, int w, int h, int dx, int dy, dt_bad_check = time(0); } - if (dt_bad) { + if (0 && dt_bad) { sraRegionPtr rect; /* send the whole thing... */ x1 = crfix(nfix(x, dpy_x), dx, dpy_x); @@ -21445,9 +24141,11 @@ int try_copyrect(Window frame, int x, int y, int w, int h, int dx, int dy, sent_copyrect = 1; *obscured = 1; /* set to avoid an aggressive push */ - } else if (stack_list) { + } else if (stack_list_num || dt_bad) { int k, tx1, tx2, ty1, ty2; - sraRegionPtr moved_win, tmp_win; + sraRegionPtr moved_win, tmp_win, whole; + sraRectangleIterator *iter; + sraRect rect; int saw_me = 0; int orig_x, orig_y; XWindowAttributes attr; @@ -21465,18 +24163,25 @@ if (db2) fprintf(stderr, "moved_win: %4d %3d, %4d %3d 0x%lx ---\n", moved_win = sraRgnCreateRect(tx1, ty1, tx2, ty2); + dtime0(&tm); + X_LOCK; /* * loop over the stack, top to bottom until we * find our wm frame: */ - for (k = stack_num - 1; k >= 0; k--) { - Window swin = stack_list[k]; + for (k = stack_list_num - 1; k >= 0; k--) { + Window swin; + + if (dt_bad) { + break; + } + + swin = stack_list[k].win; if (swin == frame) { if (db2) { -saw_me = 1; -fprintf(stderr, " ----------\n"); +saw_me = 1; fprintf(stderr, " ----------\n"); } else { break; } @@ -21486,9 +24191,37 @@ fprintf(stderr, " ----------\n"); if (swin == None) { continue; } - if (!valid_window(swin, &attr)) { + if (! stack_list[k].fetched || + stack_list[k].time > tm + 2.0) { + if (!valid_window(swin, &attr, 1)) { + stack_list[k].valid = 0; + } else { + stack_list[k].valid = 1; + stack_list[k].x = attr.x; + stack_list[k].y = attr.y; + stack_list[k].width = attr.width; + stack_list[k].height = attr.height; + stack_list[k].depth = attr.depth; + stack_list[k].class = attr.class; + stack_list[k].backing_store = + attr.backing_store; + stack_list[k].map_state = + attr.map_state; + } + stack_list[k].fetched = 1; + stack_list[k].time = tm; + } + if (!stack_list[k].valid) { continue; } + + attr.x = stack_list[k].x; + attr.y = stack_list[k].y; + attr.depth = stack_list[k].depth; + attr.width = stack_list[k].width; + attr.height = stack_list[k].height; + attr.map_state = stack_list[k].map_state; + if (attr.map_state != IsViewable) { continue; } @@ -21523,8 +24256,7 @@ if (db2) fprintf(stderr, " : clips it.\n"); /* * next, subtract from the initial window rectangle - * anything that woul - * window rectangle + * anything that would clip it. */ /* clip the window to the visible screen: */ @@ -21544,6 +24276,51 @@ if (db2 && saw_me) continue; } X_UNLOCK; + if (extra_clip && ! sraRgnEmpty(extra_clip)) { + whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + + iter = sraRgnGetIterator(extra_clip); + while (sraRgnIteratorNext(iter, &rect)) { + /* clip the window to the visible screen: */ + tx1 = rect.x1; + ty1 = rect.y1; + tx2 = rect.x2; + ty2 = rect.y2; + tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2); + sraRgnAnd(tmp_win, whole); + + /* see if window clips us: */ + if (sraRgnAnd(tmp_win, moved_win)) { + *obscured = 1; + } + sraRgnDestroy(tmp_win); + + /* subtract it from our region: */ + tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2); + sraRgnSubtract(moved_win, tmp_win); + sraRgnDestroy(tmp_win); + + /* + * next, subtract from the initial window rectangle + * anything that would clip it. + */ + tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2); + sraRgnOffset(tmp_win, -dx, -dy); + + /* clip the window to the visible screen: */ + sraRgnAnd(tmp_win, whole); + + /* subtract it from our region: */ + sraRgnSubtract(moved_win, tmp_win); + sraRgnDestroy(tmp_win); + } + sraRgnReleaseIterator(iter); + sraRgnDestroy(whole); + } + + dt = dtime(&tm); +if (db2) fprintf(stderr, " stack_work dt: %.4f\n", dt); + if (*obscured && !strcmp(wireframe_copyrect, "top")) { ; /* cannot send CopyRegion */ } else if (! sraRgnEmpty(moved_win)) { @@ -21558,8 +24335,11 @@ if (db2 && saw_me) continue; /* now send the CopyRegion: */ if (! sraRgnEmpty(shifted_region)) { -if (db2) fprintf(stderr, "do_copyregion: %d %d %d %d dx: %d dy: %d\n", tx1, ty1, tx2, ty2, dx, dy); + dtime0(&tm); do_copyregion(shifted_region, dx, dy); + dt = dtime(&tm); +if (db2) fprintf(stderr, "do_copyregion: %d %d %d %d dx: %d dy: %d dt: %.4f\n", + tx1, ty1, tx2, ty2, dx, dy, dt); sent_copyrect = 1; } sraRgnDestroy(shifted_region); @@ -21662,7 +24442,7 @@ int check_wireframe(void) { int box_x, box_y, box_w, box_h; int orig_cursor_x, orig_cursor_y, g; int already_down = 0, win_gone = 0, win_unmapped = 0; - double spin = 0.0, tm = 0.0, last_ptr, last_draw; + double spin = 0.0, tm, last_ptr, last_draw; int frame_changed = 0, drew_box = 0, got_2nd_pointer = 0; int special_t1 = 0, break_reason = 0; static double first_dt_ave = 0.0; @@ -21695,7 +24475,7 @@ if (db) fprintf(stderr, "\n*** button down!! x: %d y: %d\n", cursor_x, cursor_ * makes when it reparents the toplevel window. */ X_LOCK; - if (! get_wm_frame_pos(&px, &py, &x, &y, &w, &h, &frame)) { + if (! get_wm_frame_pos(&px, &py, &x, &y, &w, &h, &frame, NULL)) { if (db) fprintf(stderr, "NO get_wm_frame_pos: 0x%lx\n", frame); X_UNLOCK; return 0; @@ -21731,11 +24511,11 @@ if (db) fprintf(stderr, "INTERIOR\n"); } if (! wireframe_str || !strcmp(wireframe_str, WIREFRAME_PARMS)) { - int latency = get_net_latency(); - int netrate = get_net_rate(); + int link, latency, netrate; static int didmsg = 0; - if (latency > 100 || netrate < 10) { /* 100ms, 10KB/sec */ + link = link_rate(&latency, &netrate); + if (link == LR_DIALUP || link == LR_BROADBAND) { /* slow link, e.g. dialup, increase timeouts: */ first_event_spin *= 2.0; frame_changed_spin *= 2.0; @@ -21763,10 +24543,10 @@ if (db) fprintf(stderr, "INTERIOR\n"); * see if we can reuse the stack list (pause * with button down) */ - if (stack_list) { + if (stack_list_num) { int k, got_me = 0; - for (k = stack_num -1; k >=0; k--) { - if (frame == stack_list[k]) { + for (k = stack_list_num -1; k >=0; k--) { + if (frame == stack_list[k].win) { got_me = 1; break; } @@ -21777,7 +24557,7 @@ if (db) fprintf(stderr, "INTERIOR\n"); snapshot_stack_list(0, age); } } - if (! stack_list) { + if (! stack_list_num) { snapshot_stack_list(0, 0.0); } } @@ -21801,7 +24581,7 @@ if (db) fprintf(stderr, "INTERIOR\n"); box_w = w; box_h = h; - dtime(&tm); + dtime0(&tm); last_draw = spin; @@ -21884,11 +24664,6 @@ if (db) fprintf(stderr, " ++pointer event!! [%02d] dt: %.3f x: %d y: %d mas /* periodically try to let the wm get moving: */ if (!frame_changed && got_2nd_pointer % 4 == 0) { -#if 0 - X_LOCK; - XSync(dpy, False); - X_UNLOCK; -#endif if (got_2nd_pointer == 0) { usleep(50 * 1000); } else { @@ -21907,14 +24682,14 @@ if (db) fprintf(stderr, " ++pointer event!! [%02d] dt: %.3f x: %d y: %d mas X_LOCK; if (! get_wm_frame_pos(&px, &py, &x, &y, &w, &h, - &frame)) { + &frame, NULL)) { frame = 0x0; if (db) fprintf(stderr, "NO get_wm_frame_pos: 0x%lx\n", frame); } if (frame != orig_frame) { /* see if our original frame is still there */ - if (!valid_window(orig_frame, &attr)) { + if (!valid_window(orig_frame, &attr, 1)) { X_UNLOCK; /* our window frame went away! */ win_gone = 1; @@ -22027,12 +24802,8 @@ if (db || db2) fprintf(stderr, "NO button_mask\n"); if (! drew_box) { /* nice try, but no move or resize detected. cleanup. */ - if (stack_list) { - X_LOCK; - XFree(stack_list); - X_UNLOCK; - stack_list = NULL; - stack_num = 0; + if (stack_list_num) { + stack_list_num = 0; } wireframe_in_progress = 0; return 0; @@ -22072,23 +24843,24 @@ if (db || db2) fprintf(stderr, "NO button_mask\n"); } /* try to flush the wireframe removal: */ - fb_push(); + fb_push_wait(0.1); /* try to send a clipped copyrect of translation: */ sent_copyrect = try_copyrect(frame, x, y, w, h, dx, dy, - &obscured); + &obscured, NULL, 0.15); +if (db) fprintf(stderr, "send_copyrect: %d\n", sent_copyrect); if (sent_copyrect) { /* try to push the changes to viewers: */ if (! obscured) { - fb_push(); + fb_push_wait(0.1); } else { - fb_push(); + fb_push_wait(0.1); } } } - if (stack_list) { + if (stack_list_num) { /* clean up stack_list for next time: */ if (break_reason == 1 || break_reason == 2) { /* @@ -22097,17 +24869,19 @@ if (db || db2) fprintf(stderr, "NO button_mask\n"); */ last_save_stacklist = time(0); } else { - X_LOCK; - XFree(stack_list); - X_UNLOCK; - stack_list = NULL; - stack_num = 0; + stack_list_num = 0; } } /* final push (for -nowirecopyrect) */ rfbPE(1000); wireframe_in_progress = 0; + urgent_update = 1; + if (use_xdamage) { + /* DAMAGE can queue ~1000 rectangles for a move */ + clear_xdamage_mark_region(NULL, 1); + } + return 1; } @@ -22133,13 +24907,13 @@ if (db || db2) fprintf(stderr, "NO button_mask\n"); static void check_user_input2(double dt) { - int eaten = 0, miss = 0, max_eat = 50; + int eaten = 0, miss = 0, max_eat = 50, do_flush = 1; int g, g_in; - double spin = 0.0, tm = 0.0; + double spin = 0.0, tm; double quick_spin_fac = 0.40; double grind_spin_time = 0.175; - dtime(&tm); + dtime0(&tm); g = g_in = got_pointer_input; if (!got_pointer_input) { return; @@ -22163,9 +24937,7 @@ static void check_user_input2(double dt) { } else { rfbCFD(1000); } - X_LOCK; - XFlush(dpy); - X_UNLOCK; + rfbCFD(0); spin += dtime(&tm); @@ -22174,8 +24946,24 @@ static void check_user_input2(double dt) { break; } if (got_pointer_input > g) { + int i, max_extra = max_eat / 2; g = got_pointer_input; - if (eaten++ < max_eat) { + eaten++; + for (i=0; i<max_extra; i++) { + rfbCFD(0); + if (got_pointer_input > g) { + g = got_pointer_input; + eaten++; + } else if (i > 1) { + break; + } + } + X_LOCK; + do_flush = 0; +if (0) fprintf(stderr, "check_user_input2-A: XFlush %.4f\n", tm); + XFlush(dpy); + X_UNLOCK; + if (eaten < max_eat) { continue; } } else { @@ -22185,142 +24973,11 @@ static void check_user_input2(double dt) { break; } } - - - /* - * Probably grinding with a lot of fb I/O if dt is this large. - * (need to do this more elegantly) - * - * Current idea is to spin our wheels here *not* processing any - * fb I/O, but still processing the user input. This user input - * goes to the X display and changes it, but we don't poll it - * while we "rest" here for a time on order of dt, the previous - * scan_for_updates() time. We also break out if we miss enough - * user input. - */ - if (dt > grind_spin_time) { - int i, ms, split = 30; - double shim; - - /* - * Break up our pause into 'split' steps. We get at - * most one input per step. - */ - shim = 0.75 * dt / split; - - ms = (int) (1000 * shim); - - /* cutoff how long the pause can be */ - if (split * ms > 300) { - ms = 300 / split; - } - - spin = 0.0; - tm = 0.0; - dtime(&tm); - - g = got_pointer_input; - miss = 0; - for (i=0; i<split; i++) { - usleep(ms * 1000); - if (show_multiple_cursors) { - rfbPE(1000); - } else { - rfbCFD(1000); - } - spin += dtime(&tm); - if (got_pointer_input > g) { - X_LOCK; - XFlush(dpy); - X_UNLOCK; - miss = 0; - } else { - miss++; - } - g = got_pointer_input; - if (miss > 2) { - break; - } - if (1000 * spin > ms * split) { - break; - } - } - } -} - -static void check_user_input3(double dt) { - - int eaten = 0, miss = 0, max_eat = 50; - int miss_max = 2; - int g, g_in; - double spin = 0.0, tm = 0.0; - double quick_spin_fac = 0.40, button_down_time = 0.050; - double grind_spin_time = 0.125; - - dtime(&tm); - g = g_in = got_pointer_input; - if (!got_pointer_input) { - return; - } - /* - * Try for some "quick" pointer input processing. - * - * About as fast as we can, we try to process user input calling - * rfbProcessEvents or rfbCheckFds. We do this for a time on - * order of the last scan_for_updates() time, dt, but if we stop - * getting user input we break out. We will also break out if - * we have processed max_eat inputs. - * - * Note that rfbCheckFds() does not send any framebuffer updates, - * so is more what we want here, although it is likely they have - * all be sent already. - */ - while (1) { - double spin_out; - int mcut = miss_max; - - if (show_multiple_cursors) { - rfbPE(1000); - } else { - rfbCFD(1000); - } + if (do_flush) { X_LOCK; +if (0) fprintf(stderr, "check_user_input2-B: XFlush %.4f\n", tm); XFlush(dpy); X_UNLOCK; - - - spin += dtime(&tm); - - spin_out = quick_spin_fac * dt; - if (button_mask && button_down_time > spin_out) { - spin_out = button_down_time; - } - - if (spin > spin_out) { - /* get out if spin time comparable to last scan time */ - break; - } - - if (got_pointer_input > g) { - g = got_pointer_input; - if (button_mask) { - miss = 0; - } - if (eaten++ < max_eat) { - continue; - } - } else { - miss++; - } - - if (button_mask) { - mcut = 4; - } - if (miss >= mcut) { - if (! button_mask || spin > button_down_time) { - break; - } - } } @@ -22353,8 +25010,7 @@ static void check_user_input3(double dt) { } spin = 0.0; - tm = 0.0; - dtime(&tm); + dtime0(&tm); g = got_pointer_input; miss = 0; @@ -22367,7 +25023,17 @@ static void check_user_input3(double dt) { } spin += dtime(&tm); if (got_pointer_input > g) { + int i, max_extra = max_eat / 2; + for (i=0; i<max_extra; i++) { + rfbCFD(0); + if (got_pointer_input > g) { + g = got_pointer_input; + } else if (i > 1) { + break; + } + } X_LOCK; +if (0) fprintf(stderr, "check_user_input2-C: XFlush %.4f\n", tm); XFlush(dpy); X_UNLOCK; miss = 0; @@ -22385,7 +25051,7 @@ static void check_user_input3(double dt) { } } -static void check_user_input4(double dt, double dtr, int tile_diffs) { +static void check_user_input3(double dt, double dtr, int tile_diffs) { int allowed_misses, miss_tweak, i, g, g_in; int last_was_miss, consecutive_misses; @@ -22422,8 +25088,7 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) { gcnt = 0; ginput = 0; - tm = 0.0; /* timer variable */ - dtime(&tm); + dtime0(&tm); to = tm; /* last time we did rfbPE() */ g = g_in = got_pointer_input; @@ -22529,7 +25194,7 @@ int fb_update_sent(int *count) { return rc; } -static void check_user_input5(double dt, double dtr, int tile_diffs) { +static void check_user_input4(double dt, double dtr, int tile_diffs) { int g, g_in, i, ginput, gcnt, tmp; int last_was_miss, consecutive_misses; @@ -22590,8 +25255,7 @@ static void check_user_input5(double dt, double dtr, int tile_diffs) { first = 0; } - tm = 0.0; /* timer variable */ - dtime(&tm); + dtime0(&tm); if (dt < dt_cut) { dt_use = dt_cut; @@ -22601,8 +25265,8 @@ static void check_user_input5(double dt, double dtr, int tile_diffs) { if (push_frame) { int cnt, iter = 0; - double tp = 0.0, push_spin = 0.0; - dtime(&tp); + double tp, push_spin = 0.0; + dtime0(&tp); while (push_spin < dt_use * 0.5) { fb_update_sent(&cnt); if (cnt != update_count) { @@ -22744,11 +25408,20 @@ static int check_user_input(double dt, double dtr, int tile_diffs, int *cnt) { if (use_xrecord) { int rc = check_xrecord(); - if (rc == 1) { - return 0; - } else if (rc == 2) { -if (0) fprintf(stderr, " CXR: check_user_input ret 1\n"); - return 1; + /* + * 0: nothing found, proceed to other user input schemes. + * 1: events found, want to do a screen update now. + * 2: events found, want to loop back for some more. + * 3: events found, want to loop back for some more, + * and not have rfbPE() called. + * + * For 0, we precede below, otherwise return rc-1. + */ +if (debug_scroll && rc > 1) fprintf(stderr, " CXR: check_user_input ret %d\n", rc - 1); + if (rc == 0) { + ; /* proceed below. */ + } else { + return rc - 1; } } @@ -22787,11 +25460,9 @@ if (0) fprintf(stderr, " CXR: check_user_input ret 1\n"); if (pointer_mode == 2) { check_user_input2(dt); } else if (pointer_mode == 3) { - check_user_input3(dt); + check_user_input3(dt, dtr, tile_diffs); } else if (pointer_mode == 4) { check_user_input4(dt, dtr, tile_diffs); - } else if (pointer_mode == 5) { - check_user_input5(dt, dtr, tile_diffs); } return 0; } @@ -22815,7 +25486,7 @@ double dtime(double *t_old) { gettimeofday(&now, NULL); t_now = now.tv_sec + ( (double) now.tv_usec/1000000. ); - if (*t_old == 0) { + if (*t_old == 0.0) { *t_old = t_now; return t_now; } @@ -22824,15 +25495,20 @@ double dtime(double *t_old) { return(dt); } +/* common dtime() activities: */ +double dtime0(double *t_old) { + *t_old = 0.0; + return dtime(t_old); +} + +double dnow(void) { + double t; + return dtime0(&t); +} + void measure_display_hook(rfbClientPtr cl) { ClientData *cd = (ClientData *) cl->clientData; -#if 0 -double tm = 0.0; -dtime(&tm); -fprintf(stderr, " MDH: %.4f\n", tm); -#endif - cd->timer = 0.0; - dtime(&cd->timer); + dtime0(&cd->timer); } int get_rate(int which) { @@ -22841,7 +25517,7 @@ int get_rate(int which) { int irate, irate_min = 1; /* 1 KB/sec */ int irate_max = 100000; /* 100 MB/sec */ double slowest = -1.0, rate; - static double save_rate = 10000.0; /* 10000.0 B/sec */ + static double save_rate = 1000 * NETRATE0; iter = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(iter)) ) { @@ -22882,7 +25558,7 @@ int get_latency(void) { int ilat, ilat_min = 1; /* 1 ms */ int ilat_max = 2000; /* 2 sec */ double slowest = -1.0, lat; - static double save_lat = 0.020; /* 20 ms */ + static double save_lat = ((double) LATENCY0)/1000.0; iter = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(iter)) ) { @@ -22965,8 +25641,8 @@ void initialize_speeds(void) { if (! speeds_read_rate) { int n = 0; - double dt, timer = 0.0; - dtime(&timer); + double dt, timer; + dtime0(&timer); if (fullscreen) { copy_image(fullscreen, 0, 0, 0, 0); n = fullscreen->bytes_per_line * fullscreen->height; @@ -22998,11 +25674,39 @@ int get_read_rate(void) { return 0; } +int link_rate(int *latency, int *netrate) { + *latency = get_net_latency(); + *netrate = get_net_rate(); + + if (speeds_str) { + if (!strcmp(speeds_str, "modem")) { + return LR_DIALUP; + } else if (!strcmp(speeds_str, "dsl")) { + return LR_BROADBAND; + } else if (!strcmp(speeds_str, "lan")) { + return LR_LAN; + } + } + + if (*latency == LATENCY0 && *netrate == NETRATE0) { + return LR_UNSET; + } else if (*latency > 150 || *netrate < 20) { + return LR_DIALUP; + } else if (*latency > 50 || *netrate < 150) { + return LR_BROADBAND; + } else if (*latency < 10 && *netrate > 300) { + return LR_LAN; + } else { + return LR_UNKNOWN; + } +} + int get_net_rate(void) { + int spm = speeds_net_rate_measured; if (speeds_net_rate) { return speeds_net_rate; } - if (! speeds_net_rate_measured) { + if (! spm || spm == NETRATE0) { speeds_net_rate_measured = get_cmp_rate(); } if (speeds_net_rate_measured) { @@ -23012,10 +25716,11 @@ int get_net_rate(void) { } int get_net_latency(void) { + int spm = speeds_net_latency_measured; if (speeds_net_latency) { return speeds_net_latency; } - if (! speeds_net_latency_measured) { + if (! spm || spm == LATENCY0) { speeds_net_latency_measured = get_latency(); } if (speeds_net_latency_measured) { @@ -23035,7 +25740,7 @@ void measure_send_rates(int init) { int min_cmp = 10000, nclients; rfbClientIteratorPtr iter; rfbClientPtr cl; - int db = 0; + int db = 0, msg = 0; if (! measure_speeds) { return; @@ -23051,8 +25756,7 @@ void measure_send_rates(int init) { if (start == 0.0) { dtime(&start); } - now = 0.0; - dtime(&now); + dtime0(&now); now = now - start; nclients = 0; @@ -23078,15 +25782,13 @@ void measure_send_rates(int init) { rbs = cl->rawBytesEquivalent; if (init) { - double tmr = 0.0; if (db) fprintf(stderr, "%d client num rects req: %d mod: %d cbs: %d " "rbs: %d dt1: %.3f t: %.3f\n", init, (int) sraRgnCountRects(cl->requestedRegion), (int) sraRgnCountRects(cl->modifiedRegion), cbs, rbs, dt1, now); - dtime(&tmr); - cd->timer = tmr; + cd->timer = dnow(); cd->cmp_bytes_sent = cbs; cd->raw_bytes_sent = rbs; continue; @@ -23124,8 +25826,7 @@ if (db) fprintf(stderr, "%d client num rects req: %d mod: %d cbs: %d " /* mark a small rectangle: */ mark_rect_as_modified(0, 0, 16, 16, 1); - tm = 0.0; - dtime(&tm); + dtime0(&tm); dt2 = 0.0; dt3 = 0.0; @@ -23186,8 +25887,7 @@ if (db) fprintf(stderr, "dt2 calc: num rects req: %d/%d mod: %d/%d " /* mark a 2nd small rectangle: */ mark_rect_as_modified(0, 0, 16, 16, 1); i = 0; - tm = 0.0; - dtime(&tm); + dtime0(&tm); dt3 = 0.0; /* @@ -23297,9 +25997,30 @@ if (db) fprintf(stderr, "dt3 calc: num rects req: %d/%d mod: %d/%d " rfbLog("client %d latency: %.1f ms\n", cd->uid, 1000.0*dt3); rfbLog("dt1: %.4f, dt2: %.4f dt3: %.4f bytes: %d\n", dt1, dt2, dt3, cbs); + msg = 1; } rfbReleaseClientIterator(iter); + if (msg) { + int link, latency, netrate; + char *str = "error"; + + link = link_rate(&latency, &netrate); + if (link == LR_UNSET) { + str = "LR_UNSET"; + } else if (link == LR_UNKNOWN) { + str = "LR_UNKNOWN"; + } else if (link == LR_DIALUP) { + str = "LR_DIALUP"; + } else if (link == LR_BROADBAND) { + str = "LR_BROADBAND"; + } else if (link == LR_LAN) { + str = "LR_LAN"; + } + rfbLog("link_rate: %s - %d ms, %d KB/s\n", str, latency, + netrate); + } + if (init) { if (nclients) { screen->displayHook = measure_display_hook; @@ -23313,10 +26034,15 @@ if (db) fprintf(stderr, "dt3 calc: num rects req: %d/%d mod: %d/%d " * utility wrapper to call rfbProcessEvents * checks that we are not in threaded mode. */ +#define USEC_MAX 999999 /* libvncsever assumes < 1 second */ void rfbPE(long usec) { if (! screen) { return; } + + if (usec > USEC_MAX) { + usec = USEC_MAX; + } if (! use_threads) { rfbProcessEvents(screen, usec); } @@ -23326,17 +26052,72 @@ void rfbCFD(long usec) { if (! screen) { return; } + if (usec > USEC_MAX) { + usec = USEC_MAX; + } if (! use_threads) { rfbCheckFds(screen, usec); } } +int choose_delay(void) { + static double t0 = 0.0, t1 = 0.0, t2 = 0.0; + static int x0, y0, x1, y1, x2, y2; + int dx0, dy0, dx1, dy1, dm, ms = waitms; + double cut1 = 0.2, cut2 = 0.1, cut3 = 0.25; + int fac = 1; + + if (waitms == 0) { + return waitms; + } + + t2 = dnow(); + x2 = cursor_x; + y2 = cursor_y; + + dx0 = nabs(x1 - x0); + dy0 = nabs(y1 - y0); + dx1 = nabs(x2 - x1); + dy1 = nabs(y2 - y1); + if (dx1 > dy1) { + dm = dx1; + } else { + dm = dy1; + } + + if ((dx0 || dy0) && (dx1 || dy1)) { + if (t2 < t0 + cut1 || t2 < t1 + cut2 || dm > 20) { + fac = 3; + } + } else if ((dx1 || dy1) && dm > 40) { + fac = 3; + } + + if (fac == 1 && t2 < last_keyboard_time + cut3) { + fac = 2; + } + ms = waitms / fac; + if (ms == 0) { + ms = 1; + } + + x0 = x1; + y0 = y1; + t0 = t1; + + x1 = x2; + y1 = y2; + t1 = t2; + + return ms; +} + /* * main x11vnc loop: polls, checks for events, iterate libvncserver, etc. */ static void watch_loop(void) { - int cnt = 0, tile_diffs = 0; - double dt = 0.0, dtr = 0.0; + int cnt = 0, tile_diffs = 0, skip_pe = 0; + double tm = 0.0, dt = 0.0, dtr = 0.0; time_t start = time(0); if (use_threads) { @@ -23351,27 +26132,35 @@ static void watch_loop(void) { urgent_update = 0; if (! use_threads) { - double tm = 0.0; dtime(&tm); - measure_send_rates(1); - rfbPE(-1); - measure_send_rates(0); + if (! skip_pe) { + measure_send_rates(1); + rfbPE(-1); + measure_send_rates(0); + fb_update_sent(NULL); + } dtr = dtime(&tm); - fb_update_sent(NULL); if (! cursor_shape_updates) { /* undo any cursor shape requests */ disable_cursor_shape_updates(screen); } - if (screen && screen->clientHead && - check_user_input(dt, dtr, tile_diffs, &cnt)) { + if (screen && screen->clientHead) { + int ret = check_user_input(dt, dtr, + tile_diffs, &cnt); /* true means loop back for more input */ -if (0) fprintf(stderr, "watch_loop: LOOP-BACK\n"); - continue; + if (ret == 2) { + skip_pe = 1; + } + if (ret) { +if (debug_scroll) fprintf(stderr, "watch_loop: LOOP-BACK: %d\n", ret); + continue; + } } } else if (use_threads && wireframe && button_mask) { check_wireframe(); } + skip_pe = 0; if (shut_down) { clean_up_exit(0); @@ -23385,9 +26174,11 @@ if (0) fprintf(stderr, "watch_loop: LOOP-BACK\n"); check_new_clients(); check_xevents(); + check_autorepeat(); check_connect_inputs(); check_padded_fb(); check_xdamage_state(); + check_add_keysyms(); if (started_as_root) { check_switched_user(); } @@ -23433,8 +26224,8 @@ if (0) fprintf(stderr, "watch_loop: LOOP-BACK\n"); X_UNLOCK; } else { /* for timing the scan to try to detect thrashing */ - double tm = 0.0; - dtime(&tm); + double tm; + dtime0(&tm); if (use_snapfb) { int t, tries = 5; @@ -23446,14 +26237,22 @@ if (0) fprintf(stderr, "watch_loop: LOOP-BACK\n"); tile_diffs = scan_for_updates(0); } dt = dtime(&tm); -if (0 && tile_diffs > 4) { -fprintf(stderr, "TILES: %d dt: %.4f t: %.4f\n", tile_diffs, dt, tm); + +if ((debug_tiles || debug_scroll > 1 || debug_wireframe > 1) + && (tile_diffs > 4 || debug_tiles > 1)) { + double rate = (tile_x * tile_y * bpp/8 * tile_diffs) / dt; + fprintf(stderr, "============================= TILES: %d dt: %.4f" + " t: %.4f %.2f MB/s\n", tile_diffs, dt, tm - x11vnc_start, + rate/1000000.0); } check_x11_pointer(); } /* sleep a bit to lessen load */ - usleep(waitms * 1000); + if (! urgent_update) { + int wait = choose_delay(); + usleep(wait * 1000); + } cnt++; } } @@ -23938,6 +26737,9 @@ static void print_help(int mode) { "-?, -opts Only list the x11vnc options.\n" "-V, -version Print program version and last modification date.\n" "\n" +"-dbg instead of exiting after cleaning up, run a simple\n" +" \"debug crash shell\" when fatal errors are trapped.\n" +"\n" "-q Be quiet by printing less informational output to\n" " stderr. Same as -quiet.\n" "-bg Go into the background after screen setup. Messages to\n" @@ -23961,9 +26763,13 @@ static void print_help(int mode) { " the X server instead of Mode_switch (AltGr).\n" #endif "-xkb When in modtweak mode, use the XKEYBOARD extension (if\n" -" the X display supports it) to do the modifier tweaking.\n" +"-noxkb the X display supports it) to do the modifier tweaking.\n" " This is powerful and should be tried if there are still\n" " keymapping problems when using -modtweak by itself.\n" +" The default is to check whether some common keysyms,\n" +" e.g. !, @, [, are only accessible via -xkb mode and if\n" +" so then automatically enable the mode. To disable this\n" +" automatic detection use -noxkb.\n" "-skip_keycodes string Ignore the comma separated list of decimal keycodes.\n" " Perhaps these are keycodes not on your keyboard but\n" " your X server thinks exist. Currently only applies\n" @@ -23972,10 +26778,16 @@ static void print_help(int mode) { " when ambiguities exist (more than one Keycode per\n" " Keysym). Run 'xmodmap -pk' to see your keymapping.\n" " Example: \"-skip_keycodes 94,114\"\n" +"-skip_dups Some VNC viewers send impossible repeated key events,\n" +"-noskip_dups e.g. key-down, key-down, key-up, key-up all for the\n" +" same key, or 20 downs in a row for the same key!\n" +" Setting -skip_dups means to skip these duplicates and\n" +" just process the first event. Default: %s\n" "-add_keysyms If a Keysym is received from a VNC viewer and\n" -" that Keysym does not exist in the X server, then\n" +"-noadd_keysyms that Keysym does not exist in the X server, then\n" " add the Keysym to the X server's keyboard mapping.\n" -" Added Keysyms will be removed when x11vnc exits.\n" +" Added Keysyms will be removed periodically and also\n" +" when x11vnc exits. Default: %s\n" #if 0 "-xkbcompat Ignore the XKEYBOARD extension. Use as a workaround for\n" " some keyboard mapping problems. E.g. if you are using\n" @@ -24000,14 +26812,53 @@ static void print_help(int mode) { " To map a key to a button click, use the fake Keysyms\n" " \"Button1\", ..., etc. E.g: \"-remap Super_R-Button2\"\n" " (useful for pasting on a laptop)\n" -"-norepeat Option -norepeat disables X server key auto repeat\n" -"-repeat when VNC clients are connected. This works around a\n" +"\n" +" Dead keys: \"dead\" (or silent, mute) keys are keys that\n" +" do not produce a character but must be followed by a 2nd\n" +" keystroke. This is often used for accenting characters,\n" +" e.g. to put \"'\" on top of \"a\" by pressing the dead\n" +" key and then \"a\". Note that this interpretation\n" +" is not part of core X11, it is up to the toolkit or\n" +" application to decide how to react to the sequence.\n" +" The X11 names for these keysyms are \"dead_grave\",\n" +" \"dead_acute\", etc. However some VNC viewers send the\n" +" keysyms \"grave\", \"acute\" instead thereby disabling\n" +" the accenting. To work around this -remap can be used.\n" +" For example \"-remap grave-dead_grave,acute-dead_acute\"\n" +" As a convenience, \"-remap DEAD\" applies these remaps:\n" +"\n" +" g grave-dead_grave\n" +" a acute-dead_acute\n" +" c asciicircum-dead_circumflex\n" +" t asciitilde-dead_tilde\n" +" m macron-dead_macron\n" +" b breve-dead_breve\n" +" D abovedot-dead_abovedot\n" +" d diaeresis-dead_diaeresis\n" +" o degree-dead_abovering\n" +" A doubleacute-dead_doubleacute\n" +" r caron-dead_caron\n" +" e cedilla-dead_cedilla\n" +"\n" +" If you just want a subset use the first letter\n" +" label, e.g. \"-remap DEAD=ga\" to get the first two.\n" +" Additional remaps may also be supplied via commas,\n" +" e.g. \"-remap DEAD=ga,Super_R-Button2\". Finally,\n" +" \"DEAD=missing\" means to apply all of the above as\n" +" long as the left hand member is not already in the\n" +" X11 keymap.\n" +"\n" +"-norepeat Option -norepeat disables X server key auto repeat when\n" +"-repeat VNC clients are connected and VNC keyboard input is\n" +" not idle for more than 5 minutes. This works around a\n" " repeating keystrokes bug (triggered by long processing\n" -" delays between key down and key up client events:\n" -" either from large screen changes or high latency).\n" +" delays between key down and key up client events: either\n" +" from large screen changes or high latency).\n" +" Default: %s\n" +"\n" " Note: your VNC viewer side will likely do autorepeating,\n" " so this is no loss unless someone is simultaneously at\n" -" the real X display. Default: %s\n" +" the real X display.\n" "\n" " Use \"-norepeat N\" to set how many times norepeat will\n" " be reset if something else (e.g. X session manager)\n" @@ -24237,6 +27088,10 @@ static void print_help(int mode) { " so CopyRect is skipped when scaling unless you specify\n" " -wirecopyrect on the command line or by remote-control.\n" "\n" +"-debug_wireframe Turn on debugging info printout for the wireframe\n" +" heuristics. \"-dwf\" is an alias. Specify multiple\n" +" times for more output.\n" +"\n" "-scrollcopyrect mode Like -wirecopyrect, but use heuristics to try to guess\n" "-noscrollcopyrect if a window has scrolled its contents (either vertically\n" " or horizontally). This requires the RECORD X extension\n" @@ -24253,12 +27108,13 @@ static void print_help(int mode) { " it does there is a nice speedup from using the VNC\n" " CopyRect encoding (see -wirecopyrect). The speedup\n" " is both in reduced network traffic and reduced X\n" -" framebuffer polling/copying. On the other hand,\n" -" it may induce undesired transients (e.g. a terminal\n" -" cursor being scrolled up when it should not be) or other\n" -" painting errors. These are automatically repaired in a\n" -" short period of time. If this is unacceptable disable\n" -" the feature with -noscrollcopyrect.\n" +" framebuffer polling/copying. On the other hand, it may\n" +" induce undesired transients (e.g. a terminal cursor\n" +" being scrolled up when it should not be) or other\n" +" painting errors (window tearing, bunching-up, etc).\n" +" These are automatically repaired in a short period\n" +" of time. If this is unacceptable disable the feature\n" +" with -noscrollcopyrect.\n" "\n" " \"mode\" can be \"never\" (same as -noscrollcopyrect)\n" " to never try the copyrect, \"keys\" means to try it\n" @@ -24280,6 +27136,102 @@ static void print_help(int mode) { " it controlled. We want to be sure to skip the small\n" " scrollbar and get the large panel. Default: %d\n" "\n" +"-scr_skip list Skip scroll detection for applications matching\n" +" the comma separated list of strings in \"list\".\n" +" Some applications implement their scrolling in\n" +" strange ways where the XCopyArea, etc, also applies\n" +" to invisible portions of the window: if we CopyRect\n" +" those areas it looks awful during the scroll and\n" +" there may be painting errors left after the scroll.\n" +" Soffice.bin is the worst known offender.\n" +"\n" +" Use \"##\" to denote the start of the application class\n" +" (e.g. \"##XTerm\") and \"++\" to denote the start\n" +" of the application instance name (e.g. \"++xterm\").\n" +" The string your list is matched against is of the form\n" +" \"^^WM_NAME##Class++Instance<same-for-any-subwindows>\"\n" +" The \"xlsclients -la\" command will provide this info.\n" +"\n" +" If a pattern is prefixed with \"KEY:\" it only applies\n" +" to Keystroke generated scrolls (e.g. Up arrow). If it\n" +" is prefixed with \"MOUSE:\" it only applies to Mouse\n" +" induced scrolls (e.g. dragging on a scrollbar).\n" +" Default: %s\n" +"\n" +"-scr_inc list Opposite of -scr_skip: this list is consulted first\n" +" and if there is a match the window will be monitored\n" +" via RECORD for scrolls irrespective of -scr_skip.\n" +" Use -scr_skip '*' to skip anything that does not match\n" +" your -scr_inc. Use -scr_inc '*' to include everything.\n" +"\n" +"-scr_keys list For keystroke scroll detection, only apply the RECORD\n" +" heuristics to the comma separated list of keysyms in\n" +" \"list\". You may find the RECORD overhead for every\n" +" one of your keystrokes disrupts typing too much, but you\n" +" don't want to turn it off completely with \"-scr mouse\"\n" +" and -scr_parms does not work or is too confusing.\n" +"\n" +" The listed keysyms can be numeric or the keysym\n" +" names in the <X11/keysymdef.h> header file or from the\n" +" xev(1) program. Example: \"-scr_keys Up,Down,Return\".\n" +" One probably wants to have application specific lists\n" +" (e.g. for terminals, etc) but that is too icky to think\n" +" about for now...\n" +"\n" +" If \"list\" begins with the \"-\" character the list\n" +" is taken as an exclude list: all keysyms except those\n" +" list will be considered. The special string \"builtin\"\n" +" expands to an internal list of keysyms that are likely\n" +" to cause scrolls. BTW, by default modifier keys,\n" +" Shift_L, Control_R, etc, are skipped since they almost\n" +" never induce scrolling by themselves.\n" +"\n" +"-scr_parms string Set various parameters for the scrollcopyrect mode.\n" +" The format is similar to that for -wireframe and packed\n" +" with lots of parameters:\n" +"\n" +" Format: T+B+L+R,t1+t2+t3,s1+s2+s3+s4+s5\n" +" Default: %s\n" +"\n" +" If you leave nothing between commas: \",,\" the default\n" +" value is used. If you don't specify enough commas,\n" +" the trailing parameters are set to their defaults.\n" +"\n" +" \"T+B+L+R\" indicates four integers for how close in\n" +" pixels the pointer has to be from the Top, Bottom, Left,\n" +" or Right edges of the window to consider scrollcopyrect.\n" +" If -wireframe overlaps it takes precedence. This is a\n" +" speedup to quickly exclude a window from being watched\n" +" for scrollcopyrect: set them all to zero to not try\n" +" the speedup (things like selecting text will likely\n" +" be slower).\n" +"\n" +" \"t1+t2+t3\" specify three floating point times in\n" +" seconds that apply to scrollcopyrect detection with\n" +" *Keystroke* input: t1 is how long to wait after a key\n" +" is pressed for the first scroll, t2 is how long to keep\n" +" looking after a Keystroke scroll for more scrolls.\n" +" t3 is how frequently to try to update surrounding\n" +" scrollbars outside of the scrolling area (0.0 to\n" +" disable)\n" +"\n" +" \"s1+s2+s3+s4+s5\" specify five floating point times\n" +" in seconds that apply to scrollcopyrect detection with\n" +" *Mouse* input: s1 is how long to wait after a mouse\n" +" button is pressed for the first scroll, s2 is how long\n" +" to keep waiting for additional scrolls after the first\n" +" Mouse scroll was detected. s3 is how frequently to\n" +" try to update surrounding scrollbars outside of the\n" +" scrolling area (0.0 to disable). s4 is how long to\n" +" buffer pointer motion (to try to get fewer, bigger\n" +" mouse scrolls). s5 is the maximum time to spend just\n" +" updating the scroll window without updating the rest\n" +" of the screen.\n" +"\n" +"-debug_scroll Turn on debugging info printout for the scroll\n" +" heuristics. \"-ds\" is an alias. Specify it multiple\n" +" times for more output.\n" +"\n" "-pointer_mode n Various pointer motion update schemes. \"-pm\" is\n" " an alias. The problem is pointer motion can cause\n" " rapid changes on the screen: consider the rapid changes\n" @@ -24304,22 +27256,18 @@ static void print_help(int mode) { " of input events it tries to detect if it should try to\n" " \"eat\" additional pointer events before continuing.\n" "\n" -" n=3 is basically the same as n=2 except with slightly\n" -" tweaked parameters. We made this a new one so one\n" -" could use -pm 2 for the old behavior. NOT FINISHED.\n" -"\n" -" n=4 is basically a dynamic -nodragging mode: it detects\n" +" n=3 is basically a dynamic -nodragging mode: it detects\n" " when the mouse motion has paused and then refreshes\n" " the display.\n" "\n" -" n=5 attempts to measures network rates and latency,\n" +" n=4 attempts to measures network rates and latency,\n" " the video card read rate, and how many tiles have been\n" " changed on the screen. From this, it aggressively tries\n" " to push screen \"frames\" when it decides it has enough\n" " resources to do so. NOT FINISHED.\n" "\n" -" The default n is %d. Note that modes 2, 3, 4, 5 will\n" -" skip -input_skip keyboard events (but it will not count\n" +" The default n is %d. Note that modes 2, 3, 4 will skip\n" +" -input_skip keyboard events (but it will not count\n" " pointer events). Also note that these modes are not\n" " available in -threads mode which has its own pointer\n" " event handling mechanism.\n" @@ -24337,7 +27285,7 @@ static void print_help(int mode) { "\n" "-speeds rd,bw,lat x11vnc tries to estimate some speed parameters that\n" " are used to optimize scheduling (e.g. -pointer_mode\n" -" 5) and other things. Use the -speeds option to set\n" +" 4) and other things. Use the -speeds option to set\n" " these manually. The triple \"rd,bw,lat\" corresponds\n" " to video h/w read rate in MB/sec, network bandwidth to\n" " clients in KB/sec, and network latency to clients in\n" @@ -24369,6 +27317,10 @@ static void print_help(int mode) { " (deferUpdateTime) Default: %d\n" "-wait time Time in ms to pause between screen polls. Used to cut\n" " down on load. Default: %d\n" +"-readtimeout n Set libvncserver rfbMaxClientWait to n seconds. On\n" +" slow links that take a long time to paint the first\n" +" screen libvncserver may hit the timeout and drop the\n" +" connection. Default: %d seconds.\n" "-nap Monitor activity and if it is low take longer naps\n" "-nonap between screen polls to really cut down load when idle.\n" " Default: %s\n" @@ -24377,7 +27329,8 @@ static void print_help(int mode) { " for about 1.5 secs). Use 0 to disable. Default: %d\n" "\n" "-noxdamage Do not use the X DAMAGE extension to detect framebuffer\n" -" changes even if it is available.\n" +" changes even if it is available. Use -xdamage if your\n" +" default is to have it off.\n" "\n" " x11vnc's use of the DAMAGE extension: 1) significantly\n" " reduces the load when the screen is not changing much,\n" @@ -24423,6 +27376,7 @@ static void print_help(int mode) { " by checking the tile near the boundary. Default: %d\n" "-fuzz n Tolerance in pixels to mark a tiles edges as changed.\n" " Default: %d\n" +"-debug_tiles Print debugging output for tiles, fb updates, etc.\n" "\n" "-snapfb Instead of polling the X display framebuffer (fb) for\n" " changes, periodically copy all of X display fb into main\n" @@ -24689,6 +27643,8 @@ static void print_help(int mode) { " xkb enable -xkb modtweak mode.\n" " noxkb disable -xkb modtweak mode.\n" " skip_keycodes:str enable -xkb -skip_keycodes \"str\".\n" +" skip_dups enable -skip_dups mode.\n" +" noskip_dups disable -skip_dups mode.\n" " add_keysyms enable -add_keysyms mode.\n" " noadd_keysyms stop adding keysyms. those added will\n" " still be removed at exit.\n" @@ -24742,6 +27698,10 @@ static void print_help(int mode) { " scrollcopyrect:str set -scrollcopyrect string. same \"scr\"\n" " noscrollcopyrect disable -scrollcopyrect mode. \"noscr\"\n" " scr_area:n set -scr_area to n\n" +" scr_skip:list set -scr_skip to \"list\"\n" +" scr_inc:list set -scr_inc to \"list\"\n" +" scr_keys:list set -scr_keys to \"list\"\n" +" scr_parms:str set -scr_parms parameters.\n" " pointer_mode:n set -pointer_mode to n. same as \"pm\"\n" " input_skip:n set -input_skip to n.\n" " speeds:str set -speeds to str.\n" @@ -24751,7 +27711,7 @@ static void print_help(int mode) { " nodebug_keyboard disable -debug_keyboard, same as \"nodk\"\n" " defer:n set -defer to n ms,same as deferupdate:n\n" " wait:n set -wait to n ms.\n" -" rfbwait:n set -rfbwait (rfbMaxClientWait) to n ms.\n" +" readtimeout:n set read timeout to n seconds.\n" " nap enable -nap mode.\n" " nonap disable -nap mode.\n" " sb:n set -sb to n s, same as screen_blank:n\n" @@ -24792,6 +27752,10 @@ static void print_help(int mode) { " nodebug_wireframe disable debugging wireframe mechanism.\n" " debug_scroll enable debugging scrollcopy mechanism.\n" " nodebug_scroll disable debugging scrollcopy mechanism.\n" +" debug_tiles enable -debug_tiles\n" +" nodebug_tiles disable -debug_tiles\n" +" dbg enable -dbg crash shell\n" +" nodbg disable -dbg crash shell\n" "\n" " noremote disable the -remote command processing,\n" " it cannot be turned back on.\n" @@ -24846,38 +27810,40 @@ static void print_help(int mode) { " solid nosolid blackout xinerama noxinerama xtrap\n" " noxtrap xrandr noxrandr xrandr_mode padgeom quiet q\n" " noquiet modtweak nomodtweak xkb noxkb skip_keycodes\n" -" add_keysyms noadd_keysyms clear_mods noclear_mods\n" -" clear_keys noclear_keys remap repeat norepeat\n" -" fb nofb bell nobell sel nosel primary noprimary\n" -" cursorshape nocursorshape cursorpos nocursorpos cursor\n" -" show_cursor noshow_cursor nocursor arrow xfixes noxfixes\n" -" xdamage noxdamage xd_area xd_mem alphacut alphafrac\n" -" alpharemove noalpharemove alphablend noalphablend\n" -" xwarp xwarppointer noxwarp noxwarppointer buttonmap\n" -" dragging nodragging wireframe_mode wireframe wf\n" -" nowireframe nowf wirecopyrect wcr nowirecopyrect nowcr\n" -" scr_area scrollcopyrect scr noscrollcopyrect noscr\n" -" pointer_mode pm input_skip input client_input speeds\n" -" debug_pointer dp nodebug_pointer nodp debug_keyboard dk\n" -" nodebug_keyboard nodk deferupdate defer wait rfbwait\n" -" nap nonap sb screen_blank fs gaps grow fuzz snapfb\n" -" nosnapfb rawfb progressive rfbport http nohttp httpport\n" +" skip_dups noskip_dups add_keysyms noadd_keysyms\n" +" clear_mods noclear_mods clear_keys noclear_keys\n" +" remap repeat norepeat fb nofb bell nobell sel nosel\n" +" primary noprimary cursorshape nocursorshape cursorpos\n" +" nocursorpos cursor show_cursor noshow_cursor nocursor\n" +" arrow xfixes noxfixes xdamage noxdamage xd_area xd_mem\n" +" alphacut alphafrac alpharemove noalpharemove alphablend\n" +" noalphablend xwarppointer xwarp noxwarppointer noxwarp\n" +" buttonmap dragging nodragging wireframe_mode wireframe\n" +" wf nowireframe nowf wirecopyrect wcr nowirecopyrect\n" +" nowcr scr_area scr_skip scr_inc scr_keys scr_parms\n" +" scrollcopyrect scr noscrollcopyrect noscr pointer_mode\n" +" pm input_skip input client_input speeds debug_pointer dp\n" +" nodebug_pointer nodp debug_keyboard dk nodebug_keyboard\n" +" nodk deferupdate defer wait readtimeout nap nonap\n" +" sb screen_blank fs gaps grow fuzz snapfb nosnapfb\n" +" rawfb progressive rfbport http nohttp httpport\n" " httpdir enablehttpproxy noenablehttpproxy alwaysshared\n" " noalwaysshared nevershared noalwaysshared dontdisconnect\n" -" nodontdisconnect desktop noremote\n" +" nodontdisconnect desktop debug_xevents nodebug_xevents\n" +" debug_xevents debug_xdamage nodebug_xdamage\n" +" debug_xdamage debug_wireframe nodebug_wireframe\n" +" debug_wireframe debug_scroll nodebug_scroll debug_scroll\n" +" debug_tiles dbt nodebug_tiles nodbt debug_tiles dbg\n" +" nodbg noremote\n" "\n" -" aro= debug_xevents nodebug_xevents debug_xevents\n" -" debug_xdamage nodebug_xdamage debug_xdamage\n" -" debug_wireframe nodebug_wireframe debug_wireframe\n" -" debug_scroll nodebug_scroll debug_scroll display\n" -" vncdisplay desktopname http_url auth users rootshift\n" -" clipshift scale_str scaled_x scaled_y scale_numer\n" -" scale_denom scale_fac scaling_blend scaling_nomult4\n" -" scaling_pad scaling_interpolate inetd privremote\n" -" unsafe safer nocmds passwdfile using_shm logfile\n" -" o flag rc norc h help V version lastmod bg sigpipe\n" -" threads readrate netrate netlatency pipeinput clients\n" -" client_count pid ext_xtest ext_xtrap ext_xrecord\n" +" aro= display vncdisplay desktopname http_url auth\n" +" users rootshift clipshift scale_str scaled_x scaled_y\n" +" scale_numer scale_denom scale_fac scaling_blend\n" +" scaling_nomult4 scaling_pad scaling_interpolate inetd\n" +" privremote unsafe safer nocmds passwdfile using_shm\n" +" logfile o flag rc norc h help V version lastmod bg\n" +" sigpipe threads readrate netrate netlatency pipeinput\n" +" clients client_count pid ext_xtest ext_xtrap ext_xrecord\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" @@ -24982,6 +27948,8 @@ static void print_help(int mode) { shared ? "on":"off", vnc_connect ? "-vncconnect":"-novncconnect", use_modifier_tweak ? "-modtweak":"-nomodtweak", + skip_duplicate_key_events ? "-skip_dups":"-noskip_dups", + add_keysyms ? "-add_keysyms":"-noadd_keysyms", no_autorepeat ? "-norepeat":"-repeat", alt_arrow_max, alt_arrow, alpha_threshold, @@ -24992,10 +27960,13 @@ static void print_help(int mode) { wireframe_copyrect_default, scroll_copyrect_default, scrollcopyrect_min_area, + scroll_skip_str0 ? scroll_skip_str0 : "(empty)", + SCROLL_COPYRECT_PARMS, pointer_mode_max, pointer_mode, ui_skip, defer_update, waitms, + rfbMaxClientWait/1000, take_naps ? "take naps":"no naps", screen_blank, xdamage_max_area, NSCAN, xdamage_memory, @@ -25121,7 +28092,7 @@ static char *choose_title(char *display) { } } strncat(title, display, MAXN - strlen(title)); - if (subwin && valid_window(subwin, NULL)) { + if (subwin && valid_window(subwin, NULL, 0)) { char *name; if (XFetchName(dpy, subwin, &name)) { strncat(title, " ", MAXN - strlen(title)); @@ -25367,7 +28338,7 @@ void immediate_switch_user(int argc, char* argv[]) { int main(int argc, char* argv[]) { - int i, len; + int i, len, tmpi; int ev, er, maj, min; char *arg; int remote_sync = 0; @@ -25383,6 +28354,8 @@ int main(int argc, char* argv[]) { /* used to pass args we do not know about to rfbGetScreen(): */ int argc_vnc = 1; char *argv_vnc[128]; + dtime0(&x11vnc_start); + if (!getuid() || !geteuid()) { started_as_root = 1; @@ -25392,6 +28365,7 @@ int main(int argc, char* argv[]) { argv_vnc[0] = strdup(argv[0]); program_name = strdup(argv[0]); + program_pid = (int) getpid(); solid_default = strdup(solid_default); /* for freeing with -R */ @@ -25614,6 +28588,8 @@ int main(int argc, char* argv[]) { } else if (!strcmp(arg, "-V") || !strcmp(arg, "-version")) { fprintf(stdout, "x11vnc: %s\n", lastmod); exit(0); + } else if (!strcmp(arg, "-dbg")) { + crash_debug = 1; } else if (!strcmp(arg, "-q") || !strcmp(arg, "-quiet")) { quiet = 1; } else if (!strcmp(arg, "-bg") || !strcmp(arg, "-background")) { @@ -25627,17 +28603,28 @@ int main(int argc, char* argv[]) { use_modifier_tweak = 1; } else if (!strcmp(arg, "-nomodtweak")) { use_modifier_tweak = 0; + got_nomodtweak = 1; } else if (!strcmp(arg, "-isolevel3")) { use_iso_level3 = 1; } else if (!strcmp(arg, "-xkb")) { + use_modifier_tweak = 1; use_xkb_modtweak = 1; + } else if (!strcmp(arg, "-noxkb")) { + use_xkb_modtweak = 0; + got_noxkb = 1; } else if (!strcmp(arg, "-xkbcompat")) { xkbcompat = 1; } else if (!strcmp(arg, "-skip_keycodes")) { CHECK_ARGC skip_keycodes = strdup(argv[++i]); + } else if (!strcmp(arg, "-skip_dups")) { + skip_duplicate_key_events = 1; + } else if (!strcmp(arg, "-noskip_dups")) { + skip_duplicate_key_events = 0; } else if (!strcmp(arg, "-add_keysyms")) { add_keysyms++; + } else if (!strcmp(arg, "-noadd_keysyms")) { + add_keysyms = 0; } else if (!strcmp(arg, "-clear_mods")) { clear_mods = 1; } else if (!strcmp(arg, "-clear_keys")) { @@ -25734,6 +28721,9 @@ int main(int argc, char* argv[]) { } else if (!strcmp(arg, "-nowirecopyrect") || !strcmp(arg, "-nowf")) { set_wirecopyrect_mode("never"); + } else if (!strcmp(arg, "-debug_wireframe") + || !strcmp(arg, "-dwf")) { + debug_wireframe++; } else if (!strcmp(arg, "-scrollcopyrect") || !strcmp(arg, "-scr")) { CHECK_ARGC @@ -25749,6 +28739,21 @@ int main(int argc, char* argv[]) { if (tn >= 0) { scrollcopyrect_min_area = tn; } + } else if (!strcmp(arg, "-scr_skip")) { + CHECK_ARGC + scroll_skip_str = strdup(argv[++i]); + } else if (!strcmp(arg, "-scr_inc")) { + CHECK_ARGC + scroll_good_str = strdup(argv[++i]); + } else if (!strcmp(arg, "-scr_keys")) { + CHECK_ARGC + scroll_key_list_str = strdup(argv[++i]); + } else if (!strcmp(arg, "-scr_parms")) { + CHECK_ARGC + scroll_copyrect_str = strdup(argv[++i]); + } else if (!strcmp(arg, "-debug_scroll") + || !strcmp(arg, "-ds")) { + debug_scroll++; } else if (!strcmp(arg, "-pointer_mode") || !strcmp(arg, "-pm")) { char *p, *s; @@ -25786,6 +28791,9 @@ int main(int argc, char* argv[]) { } else if (!strcmp(arg, "-wait")) { CHECK_ARGC waitms = atoi(argv[++i]); + } else if (!strcmp(arg, "-readtimeout")) { + CHECK_ARGC + rfbMaxClientWait = atoi(argv[++i]) * 1000; } else if (!strcmp(arg, "-nap")) { take_naps = 1; } else if (!strcmp(arg, "-nonap")) { @@ -25838,6 +28846,9 @@ int main(int argc, char* argv[]) { } else if (!strcmp(arg, "-fuzz")) { CHECK_ARGC tile_fuzz = atoi(argv[++i]); + } else if (!strcmp(arg, "-debug_tiles") + || !strcmp(arg, "-dbt")) { + debug_tiles++; } else if (!strcmp(arg, "-snapfb")) { use_snapfb = 1; } else if (!strcmp(arg, "-rawfb")) { @@ -26143,17 +29154,11 @@ int main(int argc, char* argv[]) { if (! scroll_copyrect) { set_scrollcopyrect_mode(NULL); } + initialize_scroll_matches(); /* increase rfbwait if threaded */ if (use_threads && ! got_rfbwait) { - if (0) { - /* -rfbwait = rfbScreen->maxClientWait is not used */ - argv_vnc[argc_vnc++] = strdup("-rfbwait"); - argv_vnc[argc_vnc++] = strdup("604800000"); - } else { - /* set the global in sockets.c instead: */ - rfbMaxClientWait = 604800000; - } + rfbMaxClientWait = 604800000; } /* no framebuffer (Win2VNC) mode */ @@ -26191,6 +29196,9 @@ int main(int argc, char* argv[]) { } } + /* initialize added_keysyms[] array to zeros */ + add_keysym(NoSymbol); + /* tie together cases of -localhost vs. -listen localhost */ if (! listen_str) { if (allow_list && !strcmp(allow_list, "127.0.0.1")) { @@ -26206,6 +29214,8 @@ int main(int argc, char* argv[]) { /* set OS struct UT */ uname(&UT); + initialize_crash_handler(); + if (! quiet) { fprintf(stderr, "\n"); fprintf(stderr, "Settings:\n"); @@ -26230,6 +29240,7 @@ int main(int argc, char* argv[]) { fprintf(stderr, " conn_once: %d\n", connect_once); fprintf(stderr, " timeout: %d\n", first_conn_timeout); fprintf(stderr, " inetd: %d\n", inetd); + fprintf(stderr, " http: %d\n", try_http); fprintf(stderr, " connect: %s\n", client_connect ? client_connect : "null"); fprintf(stderr, " connectfile %s\n", client_connect_file @@ -26269,12 +29280,14 @@ int main(int argc, char* argv[]) { fprintf(stderr, " rc_file: \"%s\"\n", rc_rcfile ? rc_rcfile : "null"); fprintf(stderr, " norc: %d\n", rc_norc); + fprintf(stderr, " dbg: %d\n", crash_debug); fprintf(stderr, " bg: %d\n", bg); fprintf(stderr, " mod_tweak: %d\n", use_modifier_tweak); fprintf(stderr, " isolevel3: %d\n", use_iso_level3); fprintf(stderr, " xkb: %d\n", use_xkb_modtweak); fprintf(stderr, " skipkeys: %s\n", skip_keycodes ? skip_keycodes : "null"); + fprintf(stderr, " skip_dups: %d\n", skip_duplicate_key_events); fprintf(stderr, " addkeysyms: %d\n", add_keysyms); fprintf(stderr, " xkbcompat: %d\n", xkbcompat); fprintf(stderr, " clearmods: %d\n", clear_mods); @@ -26290,6 +29303,7 @@ int main(int argc, char* argv[]) { fprintf(stderr, " multicurs: %d\n", show_multiple_cursors); fprintf(stderr, " curs_mode: %s\n", multiple_cursors_mode ? multiple_cursors_mode : "null"); + fprintf(stderr, " arrow: %d\n", alt_arrow); fprintf(stderr, " xfixes: %d\n", use_xfixes); fprintf(stderr, " alphacut: %d\n", alpha_threshold); fprintf(stderr, " alphafrac: %.2f\n", alpha_frac); @@ -26303,6 +29317,19 @@ int main(int argc, char* argv[]) { fprintf(stderr, " dragging: %d\n", show_dragging); fprintf(stderr, " wireframe: %s\n", wireframe_str ? wireframe_str : WIREFRAME_PARMS); + fprintf(stderr, " wirecopy: %s\n", wireframe_copyrect ? + wireframe_copyrect : wireframe_copyrect_default); + fprintf(stderr, " scrollcopy: %s\n", scroll_copyrect ? + scroll_copyrect : scroll_copyrect_default); + fprintf(stderr, " scr_area: %d\n", scrollcopyrect_min_area); + fprintf(stderr, " scr_skip: %s\n", scroll_skip_str ? + scroll_skip_str : scroll_skip_str0); + fprintf(stderr, " scr_inc: %s\n", scroll_good_str ? + scroll_good_str : scroll_good_str0); + fprintf(stderr, " scr_keys: %s\n", scroll_key_list_str ? + scroll_key_list_str : "null"); + fprintf(stderr, " scr_parms: %s\n", scroll_copyrect_str ? + scroll_copyrect_str : SCROLL_COPYRECT_PARMS); fprintf(stderr, " ptr_mode: %d\n", pointer_mode); fprintf(stderr, " inputskip: %d\n", ui_skip); fprintf(stderr, " speeds: %s\n", speeds_str @@ -26311,6 +29338,7 @@ int main(int argc, char* argv[]) { fprintf(stderr, " debug_key: %d\n", debug_keyboard); fprintf(stderr, " defer: %d\n", defer_update); fprintf(stderr, " waitms: %d\n", waitms); + fprintf(stderr, " readtimeout: %d\n", rfbMaxClientWait/1000); fprintf(stderr, " take_naps: %d\n", take_naps); fprintf(stderr, " sb: %d\n", screen_blank); fprintf(stderr, " xdamage: %d\n", use_xdamage); @@ -26332,7 +29360,7 @@ int main(int argc, char* argv[]) { fprintf(stderr, " gui_mode: %s\n", gui_str ? gui_str : "null"); fprintf(stderr, " noremote: %d\n", !accept_remote_cmds); - fprintf(stderr, " safemode: %d\n", safe_remote_only); + fprintf(stderr, " unsafe: %d\n", !safe_remote_only); fprintf(stderr, " privremote: %d\n", priv_remote); fprintf(stderr, " safer: %d\n", more_safe); fprintf(stderr, " nocmds: %d\n", no_external_cmds); @@ -26423,6 +29451,11 @@ int main(int argc, char* argv[]) { scr = DefaultScreen(dpy); rootwin = RootWindow(dpy, scr); + if (! quiet) { + rfbLog("\n"); + rfbLog("------------------ USEFUL INFORMATION ------------------\n"); + } + if (remote_cmd || query_cmd) { int rc = do_remote_query(remote_cmd, query_cmd, remote_sync); XFlush(dpy); @@ -26474,11 +29507,40 @@ int main(int argc, char* argv[]) { } if (! quiet && xdamage_present && use_xdamage) { rfbLog("X DAMAGE available on display, using it for" - " polling hints\n"); - rfbLog(" to disable this behavior use: " + " polling hints.\n"); + rfbLog(" To disable this behavior use: " "'-noxdamage'\n"); } + if (! quiet && wireframe) { + rfbLog("Wireframing: -wireframe mode is in effect for window moves.\n"); + rfbLog(" If this yields undesired behavior (poor response, painting\n"); + rfbLog(" errors, etc) it may be disabled:\n"); + rfbLog(" - use '-nowf' to disable wireframing completely.\n"); + rfbLog(" - use '-nowcr' to disable the Copy Rectangle after the\n"); + rfbLog(" moved window is released in the new position.\n"); + rfbLog(" Also see the -help entry for tuning parameters.\n"); + } + + tmpi = 1; + if (scroll_copyrect) { + if (strstr(scroll_copyrect, "never")) { + tmpi = 0; + } + } else if (scroll_copyrect_default) { + if (strstr(scroll_copyrect_default, "never")) { + tmpi = 0; + } + } + if (! quiet && tmpi) { + rfbLog("Scroll Detection: -scrollcopyrect mode is in effect to\n"); + rfbLog(" use RECORD extension to try to detect scrolling windows\n"); + rfbLog(" (induced by either user keystroke or mouse input).\n"); + rfbLog(" If this yields undesired behavior (poor response, painting\n"); + rfbLog(" errors, etc) it may be disabled via: '-noscr'\n"); + rfbLog(" Also see the -help entry for tuning parameters.\n"); + } + overlay_present = 0; #ifdef SOLARIS_OVERLAY if (! XQueryExtension(dpy, "SUN_OVL", &maj, &ev, &er)) { @@ -26666,6 +29728,12 @@ int main(int argc, char* argv[]) { } #endif + if (xkb_present && !use_xkb_modtweak && !got_noxkb) { + if (use_modifier_tweak) { + switch_to_xkb_if_better(); + } + } + #if LIBVNCSERVER_HAVE_LIBXRANDR if (! XRRQueryExtension(dpy, &xrandr_base_event_type, &er)) { if (xrandr && ! quiet) { @@ -26680,6 +29748,11 @@ int main(int argc, char* argv[]) { } #endif + if (! quiet) { + rfbLog("--------------------------------------------------------\n"); + rfbLog("\n"); + } + raw_fb_pass_go_and_collect_200_dollars: if (! dt) { |